The Rank of F

渋谷で働く無能のブログ

雑記 : PHP_CodeSnifferとPHPCompatibility

自分の書いているphpのコードが特定のphpバージョンで正常に動作するか確認したい。

ということでPHP_CodeSniffer(squizlabs/PHP_CodeSniffer)とPHPCompatibility(wimg/PHPCompatibility)を使って解析する方法をQiitaに投げた。

php 7.0へのマイグレーション確認用には他にもsstale/php7ccとかもあるようだけど,どうなんだろうか。

雑記 : Laravelの嫌いなところ

Laravelは好きだけど

Laravelは今最もトレンドなphp言語向けWebアプリケーションフレームワークで,5.1 LTSのリリース時から使い続けています。

以前使っていたZend Framework 2よりはるかに直感的に扱え,まさに「作りたい物を作りたいところだけ」作れる,そんな理想の存在がLaravelでした。

が,使い続けていくうちにだんだんと嫌なところも見えてきます。今回はそんなLaravelの嫌なところをまとめてみました。

1. Facadeが嫌い

Laravelにはまるで静的メソッドのようにインスタンスの動的なメソッドを呼び出すことの出来るFacadeという便利な仕組みがあります。

これはFacadeと呼ばれる静的クラスが,php言語のマジックメソッドを用いてDIコンテナにアクセスし,取得したインスタンスの動的メソッドを実行するというものです。

確かにこの構造は便利で,場所を気にせず好きなところでロジックを呼び出せます。

しかしこの構造はとてもいびつであり,DIコンテナとコンストラクタ・メソッドインジェクションによって下げられた疎結合性を一気に密結合なものへと変えてしまいます。

また,Facadeを用いた処理の実行は毎回マジックメソッドを経由して行われる為,実行コストも高いものとなります。そもそも強力なコンストラクタ・メソッドインジェクションがあるLaravelにおいては,Facadeの使用は必要最小限,もしくは完全に撤廃してしまっても問題ないように思います。

幸いにも,Laravel 5.5 LTSの時点ではFacadeの使用は任意であり,作成したプロジェクトのいくつかのファイルを修正することでFacadeを用いずにアプリケーションを作ることも可能です。また,そういった手法を用いることを好んでいる方も居ます(私もその一人です)

2. ヘルパ関数が嫌い

1.のFacadeが嫌いとほぼ同じ理由です。が,これに関しては回避手段がありません。LaravelはDIコンテナに対しstringで値を登録していることが多く,そのような値を取得するには最悪でもServiceLocator アンチパターンを用いるしかなくなってしまいます。

例えば,プロジェクトのストレージパスを取得するstorage_path()ヘルパ関数の実装がどのようになっているのか見てみましょう。

<?php

// 省略
if (! function_exists('storage_path')) {
    /**
     * Get the path to the storage folder.
     *
     * @param  string  $path
     * @return string
     */
    function storage_path($path = '')
    {
        return app('path.storage').($path ? DIRECTORY_SEPARATOR.$path : $path);
    }
}
// 省略

まずはapp()ヘルパ関数に対し,path.storageという文字列を与えているのがわかります。

ではapp()ヘルパ関数の実装を見てみます。

<?php
// 省略
if (! function_exists('app')) {
    /**
     * Get the available container instance.
     *
     * @param  string  $abstract
     * @param  array   $parameters
     * @return mixed|\Illuminate\Foundation\Application
     */
    function app($abstract = null, array $parameters = [])
    {
        if (is_null($abstract)) {
            return Container::getInstance();
        }

        return Container::getInstance()->make($abstract, $parameters);
    }
}
// 省略

はい,そうなのです。path.storageという文字列を抽象として,パスという文字列を具象として登録しています。

文字列は紛れもない具象です。文字列を表す抽象は存在し得ず,またオブジェクトではない為,コンストラクタ・メソッドインジェクションのどちらも使うことが出来ません。

Laravelは多くの箇所でこのような文字列に対する具象登録を行っており,ServiceLocatorパターンを用いない限り登録された具象を取得することが出来ません。これは回避不可能です。

3. Contractsが嫌い

Laravelは具象と抽象を区別するためにContractsという名前空間を用いてインターフェイスを隔離しています。しかし全ての具象に対してContractsが存在するわけではなく,またContracts以下の名前空間の問題からasを用いた別名定義を用いなければ使えない状況が多発しています。

例えば,Controllerのメソッドインジェクションを用いてViewCacheインスタンスを取得しようとすると,以下のようになります。

<?php

namespace App\Http\Controllers;

use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\Contracts\Cache\Factory as CacheFactory;

class TestController extends Controller
{
    public function index(ViewFactory $view, CacheFactory $cache)
    {
        $view->make('welcome');
    }
}

4. Eloquentが嫌い

嫌いというか,非常に学習コストが高いです。ActiveRecordのようにも見えますがActiveRecordにもなりきれておらず,悪く言えば中途半端,良く言えばどうとでも使えるOR Mapperになっています。

というのも,Eloquentは前述のFacadeと親しい部分があり,それがデータベースを表すのか行を表すのかが非常に判別しにくくなっています。

具体的に言うとこんな感じになります。あまりにわかりにくいので変数名にinstanceと命名することでそれが何であるかを明確にせざるを得ませんでした。

それでもLaravelは何よりも現実的

確かに設計の面でいびつな部分も数多く存在するLaravelですが,現在存在するphp言語向けWebアプリケーションフレームワークの中では最も開発効率に優れたものであるということも間違いないであろうと思っています。

Facadeヘルパ関数はコードの品質を下げかねない諸刃の剣ですが,使い方を間違えさえしなければよほどのことにはなりません。仮に密結合なコードを書いてしまったとしても,LaravelのDIコンテナには後からそれを救う機能が備わっています。

結論としては,Laravelはやっぱり良いフレームワークだな,というところでした。

家が燃えても気づける! @zeriyoshi_svr の裏側

みんなあるよね自宅サーバ

この界隈の人達にとって,自宅にサーバーがあるのは珍しいことではないかと思います。

特にファイルサーバーなんかは既製品を買ってくるよりも自作したほうが遥かに安く強力な物を作れるので便利です。

実際私も家にサーバーがあるのですが,WD Red 8TBx2をRAID1で構成し,ファイルサーバー兼Webサーバーとして稼働してもらっています。

この自宅サーバーなのですが,15分毎に室温+各種ハードウェアの温度をTwitterに投稿しています。

twitter.com

Twitterのフォロワーさんにこれを真似してくれている人が居たので,個人的なメモ代わりにもこのbotがどのようにして動いているかを書いておきたいと思います。

サーバー構成

  • CPU : Intel(R) Core(TM) i5-4570 CPU @ 3.20GHz (4th Gen, Haswell)
  • RAM : 20GB (8GBx2, 2GBx2)
  • OS : Arch Linux (x86_64)

ハードウェアはそれなりにパワーがあるのですが,自宅の回線が細い(100Mbps VDSL)なのであまり有効に活用できない...

最近はもっぱらファイルサーバーとして活躍しています。

死活監視が出来ない

自宅の回線は特にISPの固定IPオプションなどを契約していない為,グローバルIPv4アドレスが定期的に変わる環境にあります。

その為にドメインレジストラのDynamic DNS機能を用いて逐次現在のIPアドレスを登録しているのですが,何かしらの問題が発生して登録が正常に行われない可能性があります。

こうなるとサーバーに物理的にアクセスできない限り現状の把握が出来ません。もしかしたら停電が起こっていたり,最悪サーバーが燃えている可能性も考えられます。

ならば"サーバー側から"現在の状況を報告もらえばいいのです。手持ちのUSB温度計があったので同時に室温も報告してもらうことにしました。

例えば室温が80℃とかになっていればそれはもう確実に家が燃えているので,安心してかなしい気持ちになることができます。(そう,燃えていることに気づけるだけです。)

なぜTwitterなのか

自分が一番良く見ているのがTwitterだから

(関係ないですが,私に連絡する時はメールとかLINEよりもTwitterでreplyかDMした方が反応がたぶん早いです...)

@zeriyoshi_svrを作ろう

では実際に作っていきたいと思います。

Twitter側作業

アカウントの作成

15分毎に自分のメインアカウントでつぶやかれるとフォロワーさんに迷惑です。ちゃんとbot用アカウントを作りましょう。メインアカウントで垂れ流そうものなら速攻でミュートに突っ込みます。

アプリケーション登録

Twitterにつぶやく為にはアプリケーションの登録が必要です。

先程作成したアカウントでログインした状態でTwitter Applicaton Managementにアクセスし,Create New Appボタンを押して登録を進めます。

Callback URLは空欄のままで大丈夫です。

アプリケーション権限変更

アプリケーションの登録は完了しましたが,初期状態ではTwitterに書き込む権限を有していません。

Twitter Applicaton Managementから先程作成したアプリケーションを開き,Keys and Access Tokensタブからmodify app permissionsを開いてRead and Writeに変更してください。

自身のアクセストークンを取得

権限の変更が完了したら,Keys and Access Tokensタブの下にあるYour Access TokenToken ActionからCreate my access tokenボタンをクリックして自身のアカウント用のアクセストークンを生成します。

Access Tokenが生成されたら,しっかりとAccess LevelRead and Writeになっていることを確認しましょう。

最後にアカウントのスクリーンネームと以下の4つのToken, Secretをメモしておきましょう。

サーバー側作業

ShellからTweet出来る環境構築

情報取得の前にまずShellからTweet出来る環境を整える必要があります。

ShellからTwitterにつぶやく為の方法はいくつかありますが,今回は非常にポータブルで使いやすい火を吹くTwitter怪人“小鳥男”を使っていきます。

使い方...は記事の方で詳細に説明されているので省略します。

で,問題はスクリプトをどこに置くかなのですが,当時の自分は何も考えずsudo su -した後/etc/zeriyoshiなるディレクトリを作ってそこに全てを突っ込むという凶悪な行為を行っていました。

本来であれば適切なパーミッションを設定した上で/optディレクトリ以下に置いたほうが良いでしょう。

温度情報の取得とTweetを行うスクリプトの作成

まずlm_sensorshddtempコマンドを用いるのでこれらをインストールします。

Linux ディストロによっては最初から入ってたりパッケージ名が違ったりするかもなので適宜確認してください。

$ sudo pacman -S lm_sensors hddtemp

で,ざっと次のようなスクリプトを書きます。

tweet_temp.sh

#!/bin/bash

cpu_temp=`sensors | grep Package | cut -c 17-24`
hdd_temps=""

for device in /dev/sd[abcd];do
    hdd_temps="${hdd_temps}${device}:`hddtemp -n $device` °C
";
done

echo -e "CPU:${cpu_temp}\nHDDs:\n${hdd_temps}\n`date +\"%Y/%m/%d %H:%M:%S\"`" | /etc/zeriyoshi/kotoriotoko/BIN/tweet.sh

はい。本当に適当です。各種実行ファイルのパスやデバイスノード名は環境に合わせて書き換えてください。

systemd-timerに登録しスクリプトを定期実行する

ちょっと前まではcronを使って定期実行を行うのが通例でしたが,最近のLinuxディストロは殆どがsystemdinitデーモンとして用いており,systemd自体にcron代替機能が備わっているのでそれを利用します。

まずはサービスを作成します。

$ sudo vim /etc/systemd/system/tweet_temp.service

[Unit]
Description=Tweet CPU and HDDs temperature.

[Service]
Type=oneshot
ExecStart=/etc/zeriyoshi/tweet_temp.sh

次にタイマーを作成します。

$ sudo vim /etc/systemd/system/tweet_temp.timer

[Unit]
Description=Tweet CPU and HDDs temperature.

[Timer]
OnCalendar=*:0/15

[Install]
WantedBy=timers.target

終わったらsystemdのデーモンを再読込し,タイマーを有効化して開始します。

$ sudo systemctl enable tweet_temp.timer
$ sudo systemctl start tweet_temp.timer

タイマーが正常に動作しているか確認します。

$ sudo systemctl | grep tweet_temp
  tweet_temp.timer                                                                                      loaded active waiting   Tweet CPU and HDDs temperature.                                   

これで一通り完了です。

おまけ : 室温をつぶやこう

@zeriyoshi_svrでは自宅の室温もつぶやいています。ではこれはどうやっているのかというと

f:id:zeriyoshi:20180108000443j:plain

こうしています。

こういうのと

www.itmedia.co.jp

これを組み合わせて

github.com

室内温度を測定しています。このUSB温度計は販売終了品となってしまっていますが,中国のR Ding社のOEM品なので今でも普通に入手することができます。

コンパイルにはlibusbが必要になるのでインストール後にmakeしてください。

GitHubの説明通り適切に設定を行えば管理者ユーザーでなくとも実行できるようになりますが,systemd-timerで実行している為特に設定はしていません。

デフォルトでは現在時刻が出力されてしまう&センサーの個体差で取得できる温度にブレがあるので通常の室温計を元にキャリブレーションを行い再度コンパイルしました。

一応ソースも貼っておきます。

pcsensor.cの修正patch(不要な変数宣言を消しているだけです)
--- pcsensor.c   2018-01-08 00:36:23.685319615 +0900
+++ pcsensor.fix.c    2018-01-08 00:34:36.358652434 +0900
@@ -47,7 +47,6 @@ const static char uCmd3[] = { 0x52,    0
 const static char uCmd4[] = { 0x54,    0,    0,    0,    0,    0,    0,    0 };
 
 const static int reqIntLen=8;
-const static int reqBulkLen=8;
 const static int timeout=5000; /* timeout in ms */
  
 static int debug=0;
temper.cを温度の出力のみ変更したpatch
--- temper.c 2018-01-08 00:40:19.838654085 +0900
+++ temper.zeri.c 2018-01-08 00:33:46.555318858 +0900
@@ -9,8 +9,8 @@
 
 /* Calibration adjustments */
 /* See http://www.pitt-pladdy.com/blog/_20110824-191017_0100_TEMPer_under_Linux_perl_with_Cacti/ */
-static float scale = 1.0287;
-static float offset = -0.85;
+static float scale = 1.00000;
+static float offset = -2.00;
 
 int main(){
    int passes = 0;
@@ -38,15 +38,7 @@ int main(){
        /* Apply calibrations */
        tempc = (tempc * scale) + offset;
 
-      struct tm *utc;
-      time_t t;
-      t = time(NULL);
-      utc = gmtime(&t);
-      
-      char dt[80];
-      strftime(dt, 80, "%d-%b-%Y %H:%M", utc);
-
-      printf("%s,%f\n", dt, tempc);
+       printf("%.3f °C\n", tempc);
        fflush(stdout);
 
        return 0;

あとは先程のtweet_temp.shを以下のようにするだけです。

#!/bin/bash

cpu_temp=`sensors | grep Package | cut -c 17-24`
hdd_temps=""

for device in /dev/sd[abcd];do
    hdd_temps="${hdd_temps}${device}:`hddtemp -n $device` °C
";
done

echo -e "Room:`/etc/zeriyoshi/temper`\nCPU:${cpu_temp}\nHDDs:\n${hdd_temps}\n`date +\"%Y/%m/%d %H:%M:%S\"`" | /etc/zeriyoshi/kotoriotoko/BIN/tweet.sh

XperiaのOSSアーカイブ読み 2017/01/20

いつも通り適当にRSSTwitterに流れてくるサイトを巡回してたら,気になる記事を見つけた。

androplus.org

未発表のスマートフォンの画像がリークするのはいつも通りすぎて特に言うことはないのだけれど,気になったのはこの部分

また、Sonyのデータベースから、現在開発中のプラットフォーム名も判明しました。 一つは「BlancBright」、もう一つは「Yoshino」です。

Sony Mobileのスマートフォンには基本的に開発コードネームとプラットフォーム名がつけられている(例えば,Xperia Zなら開発コードネームYugaでプラットフォームがLagan(Qualcomm APQ8064 + Qualcomm MDM9215M)のだけれど,新しいプラットフォーム名が判明しているそう。

どうせいつも通りQualcommのSoCだろうし,最新のXperiaオープンソースアーカイブ見てみたら何か書いてるかも,と久々にXperia X Performance / XZの39.2.A.0.386をダウンロードして探してみた。

すると,ごく僅かに,断片的にだけれどもMakefile中にYoshinoに関する記載があった。

# vendor/semc/external/fips/kscl/Android.mk Line 16 ~ 26

ifneq ($(filter N 7.0, $(PLATFORM_VERSION)),)
  ifeq ($(strip $(SOMC_PLATFORM)),tone)
    TARGET_KERNEL_SOURCE := kernel/msm-3.18
  else ifeq ($(strip $(SOMC_PLATFORM)),yoshino)
    TARGET_KERNEL_SOURCE := kernel/msm-4.4
  else
    TARGET_KERNEL_SOURCE := kernel
  endif
else
  TARGET_KERNEL_SOURCE := kernel
endif

どうやら変数SOMC_PLATFORMyoshinoならカーネルソースとしてkernel/msm-4.4を使用するそう。

ここで出て来る特殊な用語(?)としては

  • SOMC : SOny Mobile Communications,昔はSEMC(Sony Ericsson Mobile Communications)だった。
  • msm : MSM(Mobile Station Modem,要はQualcomm Snapdragonのこと。
  • msm-3.18, msm-4.4 : msmにおけるターゲットLinuxカーネルバージョン。ここにあります。

ただし,39.2.A.0.386にはkernel/msm-4.4が含まれない為,わかったのはこれだけだった。

わかったこととしては

  • YoshinoプラットフォームはQualcommチップセットである。
  • Yoshinoプラットフォームはmsm-4.4ベースである。
  • YoshinoプラットフォームはLinux Kernel 4.4ベースになる。

ということくらい。

BlancBrightの名前は見つからなかった。たぶんMediaTekチップセットなんじゃないかと思う。そういやMediaTek Helio P20採用の機種の情報が漏れてましたね。

あと,Qualcommチップセットに元素名のコードネームをつけてるみたい。ちょっと適当にソースを眺めてみた感じだとこんな感じかな...間違ってるかもしれない。

  • msmcopper => MSM8974 (Snapdragon 800)
  • msmgold => MSM8917 (Snapdragon 425)
  • msmtitanium => MSM8953 (Snapdragon 625)
  • msmferrum => MSM8909 (Snapdragon 210)
  • msmplutonium => MSM8994 (Snapdragon 810)
  • msmtellurium => MSM8952 (Snapdragon 617)
  • msmthorium => MSM8937 (Snapdragon 430)
  • msmzinc => APQ8084 (Snapdragon 805)
  • msmcobalt => MSM8998 (Snapdragon 835)

夜フクロウがMacのdGPUを叩き起こす問題とユーザー側での対応方法

電池がモリモリ減るよ

前述の通り、初めてdGPU(ディスクリートGPU)つきのMacを買った。

今まではiGPUしかなかったから何も考える必要はなかったのだけれど、dGPU搭載ということで 必要のない時にはiGPUを使用することで電力消費を抑えることを考える必要が出てきた。

特に何も考えず数時間使っていたのだけれども、明らかに電池の消費が早すぎる。

で、現在使用しているグラフィックスの種類を表示するアプリであるgfxCardStatusを入れてみた。

GitHub - steveschow/gfxCardStatus: This is a fork of gfxCardStatus, this fork will handle integrated-only mode a little better

(正確にはフォーク版だけど)

すると、やっぱりというか、案の定というか、バッテリ駆動時だろうとなんだろうと常にdGPUが使われてることがわかった。

原因

gfxCardStatusには何が原因でiGPUに切り替えられないのかを表示してくれる便利な機能がある。 というわけで何が原因か調べてみると、どうやらNight Owlというアプリが原因でdGPUが叩き起こされているらしい。

Night Owl => 夜フクロウ

Night Owlはその名の通り夜フクロウのことで、非常に使いやすいTwitterクライアント。ほんとに使いやすい。Windowsにもこういうのほしいなと切実に思う。

sites.google.com

が、これが原因で常にdGPUが叩き起こされている。どうやらTwitterをするためにはクアッドコアCPUだけではなくディスクリートグラフィックスも必要らしい。TwitterのためにXeonのPCを組む人の気持ちが... わかるわけがない。

原因を探ってみると、どうやら夜フクロウ側でGPUの自動切り替えに対応している旨の宣言をしていないのが原因のようだった。

無理やり改造して対応させる

対応してないならさせればいい。

Mac App Storeから落とした場合はそれ関係の情報が付加されるので、うまくいじれない可能性やアップデートで崩壊する可能性を考慮して公式サイトで配布されているバージョンを落としてきて改造した。

夜フクロウオープンソースじゃない。けれどもWindowsと同じようにマニフェストファイルは自由に書き換えられる。

しかもMacのアプリケーションは.appというディレクトリ名を持つ単純なディレクトリなので、以下のようにして簡単にマニフェストファイルにアクセスできる。

$ cd Night\ Owl.app/
$ open Contents/Info.plist

で、GPU自動切り替えに対応していると嘘をつくために、マニフェストファイル(Info.plist)を以下のように書き換えた。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>BuildMachineOSBuild</key>
    <string>15G31</string>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>NSSupportsAutomaticGraphicsSwitching</key>
    <true/>
〜以下省略〜

あとは起動するだけ。成功した。

その他

本当に宣言するだけでなんの問題もなく使えてしまっているので、公式に対応していただければ...