Quantcast
Channel: A Day In The Boy's Life
Viewing all 287 articles
Browse latest View live

クラスファイルのオートローダーを実装する

$
0
0

システムの規模が大きくなってくるといろんなクラスファイルが出来上がり、必要なものをincludeやrequireするのがかなり面倒になったりプログラムの可読性が悪くなったりします。

こういうときにオートローダーがあれば便利なわけですが、PHP5からは標準でクラスのオートロードの機能が実装されています。



PHPでクラスのオートローダーを作る


PHP5からは、__autoload() という関数があるのですが、現在推奨されておらず変わりにspl_autoload_register() 関数を用いることが推奨されています。


今回は、オートローダー機能がついた基底クラスを作り、サブクラスでは特に意識せずに所定のライブラリを利用できるような構成のプログラムを書いてみます。


<?phpclass baseClass {    function __construct() {        define ('LIB_DIRPATH', '/path/to/lib/');        spl_autoload_register(array($this, 'classAutoLoad'));    }    /**     * オートローダー     * @param string $className : ロードするクラス名     */    public function classAutoLoad($className) {        $path = LIB_DIRPATH . $className . '.php';        if (is_readable($path)) {            require_once $path;        }    }}

spl_autoload_register()関数によって、不明なクラスオブジェクトを生成しようとした際に呼び出す関数を定義しておきます(ここではclassAutoLoad()関数)。

コンストラクタとして定義しているので、サブクラス側では意識せずに自動的に呼び出されます。


classAutoLoad()関数は、呼び出したクラス名が自動的に引数として渡されるので、それをライブラリが置かれているパスの中からrequireするようなロジックを組み立てておきます。

クラス名とそれを定義しているプログラム名は同名になるようにしておきます。


利用する方は下記のような書き方になります。


<?phprequire_once 'baseClass.class.php';class subClass extends baseClass {    function main() {        $class = new fooClass();        $class->checkString("xyz"));    }}$obj = new subClass();$obj->main();


subClass側のmain関数で呼び出しているfooClassは特にincludeやrequireはしていませんが、基底クラスのほうで定義しているclassAutoLoad()によって自動的にrequireされます。
これで、多くのクラスファイルを利用するプログラムでも先頭に長大なrequireを書く必要はありません。


spl_autoload_register()は、クラスをオートロードする役目というよりは認識できないクラスオブジェクトが生成されたときの処理を定義するという感じで、結局はその時にクラスファイルを読み込むためのロジックをclassAutoLoad()で書いておくということをしています。


名前空間に対応させたい場合、classAutoLoad()に渡される引数に名前空間付のクラス名が渡されるので一工夫が必要です。

また、ライブラリのパスがphp.iniに定義されているinclude_path内に必ずあるというならstream_resolve_include_path() を使ったほうが便利かもしれません。


    /**     * オートローダー     * @param string $className : ロードするクラス名     */    public function classAutoLoad($className) {        // 名前空間付の場合は分解してクラス名だけを取り出す        $className = (array_pop(explode('\\', $className)));        // php.iniのinclude_pathからファイルを探してパスを返してくれる        $path = stream_resolve_include_path($className . ".php");        if ($path !== FALSE && is_readable($path)) {            require_once $path;        }    }


呼び出し側は、下記のように名前空間付で呼び出しても勝手にロードしてくれます。


    function main() {        $logic = new \hoge\FooBar();        var_dump($logic->checkString("abc"));    }


名前空間の扱いは自動で分解するなど、もっとうまいことやってくれたらいいのになぁとか思ったりはしますが。






[Composer] PHPのパッケージ管理にComposerを使う

$
0
0

今までPHPの外部ライブラリを使う場合はPEARを使ってたんですが、最近は廃れてきて変わってComposerというパッケージ管理ツールが主流となっているようなのでその使い方のメモです。


PEARの場合、root権限が必要だったりするので導入の障壁が高かったんですが、Composerの場合は一般ユーザーでパッケージを導入できるので、ユーザー環境ごとに異なるパッケージやバージョンなどを使うといったメリットがあったりします。



Composerをインストールする


とりあえずインストール方法は、下記のコマンドを叩きます。


$ curl -sS https://getcomposer.org/installer | php

社内環境などプロキシを介してインターネット接続する必要がある場合は、環境変数のHTTP(S)_PROXYをセットしておきましょう。


export HTTP_PROXY=http://proxy.example.com:8080export HTTPS_PROXY=http://proxy.example.com:8080

下記のようにPHPのエラーが出た場合はメッセージのとおり、detect_unicodeオプションをOFFにセットしなおします。


#!/usr/bin/env phpSome settings on your machine make Composer unable to work properly.Make sure that you fix the issues listed below and run this script again:The detect_unicode setting must be disabled.Add the following to the end of your `php.ini`:    detect_unicode = OffThe php.ini used by your command-line PHP is: /usr/local/lib/php.iniIf you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.


まぁ、php.ini自体rootじゃないと触れないじゃんって環境の人は、下記のように実行することでもComposerのインストールが可能です。


$ curl -sS https://getcomposer.org/installer | php -d detect_unicode=Off#!/usr/bin/env phpAll settings correct for using ComposerDownloading...Composer successfully installed to: /home/foo/composer.pharUse it: php composer.phar


が、このやり方をとってしまうとcomposer実行時に毎回detect_unicodeオプションを指定しないといけないので、素直にphp.iniを変更したほうがよさそうです。

オプションなしで実行すると下記のようにエラーが出て実行できません。


$ composer????

先ほどのダウンロードのメッセージが表示されたらインストールは完了で、実行ディレクトリにcomposer.pharというファイルが保存されています。


あとは、PATHが通っている適当なディレクトリへ(名前が少し長いのでお好みで変更して)保存しておきます。


# mv composer.phar /usr/local/bin/composer

Composerが動くかどうかは一度実行してみましょう。


$ composer    ______  / ____/___  ____ ___  ____  ____  ________  _____ / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___// /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/                    /_/Composer version 1.0-dev (1d8f05f1dd0e390f253f79ea86cd505178360019) 2015-02-11 11:31:57Usage: [options] command [arguments]Options: --help (-h)           Display this help message. --quiet (-q)          Do not output any message. --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug. --version (-V)        Display this application version. --ansi                Force ANSI output. --no-ansi             Disable ANSI output. --no-interaction (-n) Do not ask any interactive question. --profile             Display timing and memory usage information --working-dir (-d)    If specified, use the given directory as working directory.Available commands: about            Short information about Composer archive          Create an archive of this composer package browse           Opens the package's repository URL or homepage in your browser. clear-cache      Clears composer's internal package cache. clearcache       Clears composer's internal package cache. config           Set config options create-project   Create new project from a package into given directory. depends          Shows which packages depend on the given package diagnose         Diagnoses the system to identify common errors. dump-autoload    Dumps the autoloader dumpautoload     Dumps the autoloader global           Allows running commands in the global composer dir ($COMPOSER_HOME). help             Displays help for a command home             Opens the package's repository URL or homepage in your browser. info             Show information about packages init             Creates a basic composer.json file in current directory. install          Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json. licenses         Show information about licenses of dependencies list             Lists commands remove           Removes a package from the require or require-dev require          Adds required packages to your composer.json and installs them run-script       Run the scripts defined in composer.json. search           Search for packages self-update      Updates composer.phar to the latest version. selfupdate       Updates composer.phar to the latest version. show             Show information about packages status           Show a list of locally modified packages update           Updates your dependencies to the latest version according to composer.json, and updates the composer.lock file. validate         Validates a composer.json


Composerの使い方


PEARの場合はパッケージ名を直接指定してインストールしますが、Composerの場合はJSONファイルに必要なパッケージのリストを定義して管理します。
インストールしたいパッケージは、下記のサイトから見つけることができますが、全てではないので使いたいパッケージがComposerに対応しているかは個別に確認していくほうがよさそうです。


https://packagist.org/


今回はPEARの時にも使っていたPHP_CodeSniffer をインストールしてみたいと思います。

余談ですがPHP_CodeSnifferの使い方は以前に書いた「PHP_CodeSnifferでコーディング規約に準拠しているかチェックをする」を参照してください。


PHP_CodeSnifferをインストールするためにcomposer.jsonというファイルを作り、下記のように書いておきます。


composer.json{  "require": {    "squizlabs/php_codesniffer": "dev-master"  }}

これでインストールを実行してみます。


$ composer installLoading composer repositories with package informationInstalling dependencies (including require-dev)  - Installing squizlabs/php_codesniffer (dev-master ecd984a)    Cloning ecd984a526a6ba78967fdaaf2b313cc0dea4b60f    Failed to download squizlabs/php_codesniffer from source: The process "git clone --no-checkout 'https://github.com/squizlabs/PHP_CodeSniffer.git' '/home/foo/workspace/vendor/squizlabs/php_codesniffer' && cd '/home/foo/workspace/vendor/squizlabs/php_codesniffer' && git remote add composer 'https://github.com/squizlabs/PHP_CodeSniffer.git' && git fetch composer" exceeded the timeout of 300 seconds.    Now trying to download from dist  - Installing squizlabs/php_codesniffer (dev-master ecd984a)    Downloading: 100%         Writing lock fileGenerating autoload files

インストールが完了するとカレントディレクトリ内にvendorディレクトリができ、その中にダウンロードしたファイルが格納されています。
PHP_CodeSnifferの場合、インストールされるのはコマンドとなりますが、vendor/binにphpcsコマンドが格納されています。


$ ./vendor/bin/phpcs test.phpFILE: /home/foo/workspace/test.php----------------------------------------------------------------------FOUND 1 ERROR AFFECTING 1 LINE---------------------------------------------------------------------- 2 | ERROR | Missing file doc comment----------------------------------------------------------------------Time: 58ms; Memory: 6Mb


必要に応じてvendor/binにPATHをを通しておけば便利でしょう。

PHP_CodeSnifferの場合、コマンドにインストールとなるためプログラムの組み込みとか不要ですが、ライブラリ系で自前のプログラムに組み込んで使いたいといった場合、venter/autoload.phpをrequireするだけで利用が可能となります。


例としてpear/log を使って見たいと思いますが、その前にインストールを行います。

先ほどのcomposer.jsonにpear/logの情報を追記します。


{  "require": {    "squizlabs/php_codesniffer": "dev-master",    "pear/log": "dev-master"  }}


これでinstallオプションで実行といいたいところですが実際に実行してもインストールされません。


$ composer installLoading composer repositories with package informationInstalling dependencies (including require-dev) from lock fileWarning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.Nothing to install or updateGenerating autoload files


composerの設定ファイルにはcomposer.json以外にcomposer.lockというファイルがあり、こちらが優先されます。
メッセージのとおりcomposer.lockが更新されていないので特にやること無いよって言われて処理がストップしています。
そこで、更新処理を実行します。


$ composer update  Loading composer repositories with package informationUpdating dependencies (including require-dev)Your requirements could not be resolved to an installable set of packages.  Problem 1    - Installation request for pear/log dev-master -> satisfiable by pear/log[dev-master].    - pear/log dev-master requires pear/pear_exception 1.0-beta1 -> no matching package found.Potential causes: - A typo in the package name - The package is not available in a stable-enough version according to your minimum-stability setting   see  for more details.Read  for further common problems.


ただ、上記のようにエラーが出てインストールされません。

メッセージの内容から依存関係があるパッケージが存在しないとあり、Packagistにも依存関係のあるパッケージの情報が記載されています。

※ 厳密に言うとこのメッセージは依存関係のエラーというよりはスタビリティに対応するパッケージ名が見つからないというものです。


pear/logの依存関係


ってことで、その情報も追加しておきます(複数のパッケージを書く場合は、JSONの配列表記に注意)。



{  "require": {    "squizlabs/php_codesniffer": "dev-master",    "pear/pear_exception": "1.0.*@dev",    "pear/log": "dev-master"  }}


これでもう一度updateを実行してみます。


$ composer updateLoading composer repositories with package informationUpdating dependencies (including require-dev)  - Installing pear/pear_exception (1.0-beta1)    Downloading: 100%           - Installing pear/log (dev-master df7aa17)    Cloning df7aa179cb596a217d6d3eb8c5e900f3a63b9dbepear/log suggests installing pear/db (Install optionally via your project's composer.json)Writing lock fileGenerating autoload files


だいぶ話がそれましたがこれでpear/logが新規にインストールされたので早速使ってみます。
通常なら、vendor/pear/log/Log.phpをrequireするところですが、Composerのautoload.phpをrequireすればインストールしているパッケージが利用可能になります。


<?phprequire_once './vendor/autoload.php';$console = Log::factory('console', '', 'TEST');$console->log('Logging to the console.');

最後にパッケージの削除方法ですが、composer.jsonから不要なパッケージを削除してupdateすれば削除されます。

先ほど追加したpear/pear_exceptionおよびpear/logの行を削除してupdateをしてみます。


$ composer updateLoading composer repositories with package informationUpdating dependencies (including require-dev)  - Removing pear/log (dev-master)  - Removing pear/pear_exception (1.0-beta1)Writing lock fileGenerating autoload files


定義ファイルを書き換えていくやり方は少し戸惑いますが、慣れてしまえばそんな難しくもありません。

説明が長くなったので、その他の使い方などはまた別途書きたいと思います。





PR: 花粉情報をチェックして 早めの予防と対策を-政府広報

$
0
0
春になると多くの人がつらい症状に悩まされる花粉症!万全の花粉症対策を行うには?

[Composer] composer.jsonを読み解く

$
0
0

[Composer] PHPのパッケージ管理にComposerを使う の続き。

composerの管理に使うcomposer.jsonファイルの中身についての解説です。


composer.jsonの基本フォーマット


前回のエントリ内で書いたcomposer.jsonは下記の内容でした。


{  "require": {    "squizlabs/php_codesniffer": "dev-master",    "pear/pear_exception": "1.0.*@dev",    "pear/log": "dev-master"  }}

この中で、requireキーにはComposerで管理する対象パッケージとバージョンを「:」で区切り(というかJSONの配列フォーマット)で記述します。

Packagistを見ながらインストールする場合、requireキーの書き方は記載があるので参照しながら書いていくのがよいと思います。


特定のバージョンをインストールしたい場合は、バージョン欄に番号やブランチ名などを指定します。

バージョンの番号はワイルドカードやレンジで指定することもできます。

「1.0.*」とした場合、1.0系の全てのバージョン(1.0.1や1.0.2など)が対象となります。

「1.*」とした場合、1系の全てのバージョン(1.1や1.2、1.1.1や1.1.10など)が対象となります。


下記のようにレンジを指定した場合、その範囲のバージョンが対象となります。


{  "require": {    "squizlabs/php_codesniffer": "1.0 - 2.0",    "pear/pear_exception": ">=1.0,<1.1"  }}

squizlabs/php_codesnifferのレンジの指定の仕方では、バージョン1.0から2.0まで(1.0や2.0を含む)が対象となります(つまり、1.1や1.1.10、1.9など)。

pear/pear_exceptionの場合は、バージョン1.0から1.1まで(1.1を含まない)が対象となります(つまり、1.0.1や1.0.10など)。

結構細かい制御ができるみたいで、「~1.2」と書くと1.2以上、2.0未満を対象にしたりといろんな書き方が用意されているみたいなので詳細はマニュアル も参照してください。


もう1つ最初のcomposer.jsonの例の中で


"pear/pear_exception":1.0.*@dev,

という書き方がありますが、最後の@devはスタビリティフラグと呼ばれるものらしいです。

これはそのパッケージの安定性を示すフラグで、デフォルトではstable(安定版)となっています。

ちょっとパッケージの例を変えますが、nette/utils というパッケージの最新版をインストールするには


"nette/utils":2.3.*@dev

という書き方をしますが、スタビリティフラグ(@dev)を削除して


"nette/utils": "2.3.*"

のような書き方をすると「2.3.*@stable」という扱いになるのでパッケージが見つからないというエラーが出てしまいます。


$ composer updateLoading composer repositories with package informationUpdating dependencies (including require-dev)Your requirements could not be resolved to an installable set of packages.  Problem 1    - The requested package nette/utils could not be found in any version, there may be a typo in the package name.Potential causes: - A typo in the package name - The package is not available in a stable-enough version according to your minimum-stability setting   see  for more details.Read  for further common problems.

最初の設定例のpear/exceptionでも同様なのですが、こちらには@dev以外にも安定板のバージョン1.0.0が公開されているため、エラー無くインストールできたりします。

この場合、@devの1.0.x-devのバージョンではなくstableのバージョン1.0.0がインストールされてるわけですが、この2つはエイリアスになっているので結局は一緒のものがインストールされてます。


デフォルトのスタビリティフラグを変えたい場合、minimum-stabilityという属性をセットすることで変更することができます。


{  "minimum-stability": "dev",  "require": {    "nette/utils": "2.3.*"  }}

上記の例であればうまくインストールができますが、パッケージ全体のスタビリティフラグが変わるため、安定しないバージョンのパッケージがインストールされる可能性があり、通常は変更せずにパッケージごとに必要に応じてスタビリティフラグを明記したほうがよいと思います。

この辺の話は、下記の記事が詳しく書かれています。


Composerがパッケージのstabilityを解決するしくみ @ オープンソースこねこね



その他の便利なオプションとか


依存関係を無視してインストールする


composer.jsonに設定できるその他の便利なオプションとして、依存関係を無視するprovideオプションがあります。

例えば、pear/logパッケージはpear/pear_exceptionパッケージを必要としますが、下記の様に書くことでpear/pear_exceptionをインストールしなくてもpear/logパッケージを導入できます(それで動くか動かないかは別にして)。


{  "provide": {    "pear/pear_exception": "*"  },  "require": {    "pear/log": "dev-master"  }}

ソースだけダウンロードしたい場合や依存関係にあるもののその機能をまったく使わないといった場合は便利なオプションかもしれません。


リポジトリを指定する


デフォルトでcomposerがパッケージを検索、取得するリポジトリはpackagistからになりますが、別のリポジトリを指定することもできます。

例えば、PEARパッケージはpackagistでも提供されていますが、PEARのリポジトリからもインストール可能です。


{  "repositories": [    {      "type": "pear",      "url": "http://pear.php.net/"    }  ],  "require": {    "pear-pear.php.net/PEAR" : "*"  }}

上記のようにrepositoriesの属性を追加します。


$ composer installLoading composer repositories with package informationInitializing PEAR repository http://pear.php.netInstalling dependencies (including require-dev)  - Installing pear-pear.php.net/xml_util (1.2.3)    Downloading: 100%           - Installing pear-pear.php.net/console_getopt (1.3.1)    Downloading: 100%           - Installing pear-pear.php.net/structures_graph (1.0.4)    Downloading: 100%           - Installing pear-pear.php.net/archive_tar (1.3.13)    Downloading: 100%           - Installing pear-pear.php.net/pear (1.9.5)    Downloading: 100%         Writing lock fileGenerating autoload files

上記のようにPEARサイトからインストールされていることがわかります。 packagistからインストールした場合の表示はこんな感じ。


$ composer installLoading composer repositories with package informationInstalling dependencies (including require-dev)  - Installing pear/pear_exception (v1.0.0)    Loading from cache  - Installing pear/xml_util (1.2.3)    Downloading: 100%           - Installing pear/console_getopt (v1.3.1)    Downloading: 100%           - Installing pear/structures_graph (dev-trunk 6331691)    Cloning 633169128f282e749b1efdbda77b2760a8b110d0  - Installing pear/archive_tar (1.3.11)    Downloading: 100%           - Installing pear/pear (dev-master d346efa)    Cloning d346efa9809bdf0e53d711fbffd385d070207f96pear/pear suggests installing pear/pear_frontend_gtk (For GTK support)pear/pear suggests installing pear/pear_frontend_web (For Web support)Writing lock fileGenerating autoload files

ちなみに、repositoriesの属性はその他のリポジトリからもソースをダウンロードできたりして、例えばcomposerを通してjQueryをインストールする(インストールというかソースのダウンロード)というようなこともできたりします。


{  "repositories": [    {      "type": "pear",      "url": "http://pear.php.net/"    },    {      "type": "package",      "package": {        "name": "jquery/jquery",        "version": "2.1.3",        "type": "jquery",        "dist": {          "url": "http://code.jquery.com/jquery-2.1.3.js",          "type": "file"        }      }    }  ],  "require": {    "pear-pear.php.net/PEAR" : "*",    "jquery/jquery": "*"  }}

上記のようにpackageのタイプを追加し、ダウンロード先のURLなどの情報を書いておき、requireでjqueryを指定すればインストールできます。


$ composer updateLoading composer repositories with package informationInitializing PEAR repository http://pear.php.netUpdating dependencies (including require-dev)  - Installing jquery/jquery (2.1.3)    Downloading: 100%         Writing lock fileGenerating autoload files

まぁ、使い道の良し悪しはあるもののパッケージ管理という観点では楽になるかもしれません。

詳細はマニュアル も参照してください。


インストール前後でスクリプトを実行する


インストールやアップデートをトリガーとしてとあるバッチを動かしたいといったことも可能です。

例えば、パッケージが更新されたらメール通知を行うといったことができます。


{  "autoload": {    "psr-0": {        "MyVendor\\MyClass": ""    }  },  "scripts": {      "post-package-install": [          "MyVendor\\MyClass::postPackageInstall"      ],      "post-package-update": [          "MyVendor\\MyClass::postPackageInstall"      ]  },  "require": {    "nette/utils": "2.3.*@dev"  }}

scriptsの属性が設定箇所で、post-package-installやpost-package-updateがトリガーとなるイベント名です。

それぞれ、パッケージがインストール/更新が発生した際にMyClass.phpのpostPackageInstall()を呼び出すという処理になります。

設定できるトリガーはマニュアル 参照。


MyClass.phpは下記のようなものです。


<?phpnamespace MyVendor;use Composer\Script\Event;class MyClass{    public static function postPackageInstall(Event $event)    {        $installedPackage = $event->getOperation()->getPackage()->getName();        var_dump($installedPackage);    }}


このファイルは、Composerのルートディレクトリ内(composer.jsonが配置されているのと同じディレクトリ)のMyVendor/MyClass.phpとして保存しています。

パスの通し方は、Composerがオートロードできる箇所ならどこでもよいのですが、composerのautoloadの設定にてパスを指定していればそのプログラムが呼び出されます。


余談ですが、インストール/更新されたパッケージ名を取り出すことをしているのですが、$event->getOperation()->getPackage()はパッケージがインストールされた場合しか呼び出せません。

ですので、post-update-cmdやpost-install-cmdのイベントで呼び出そうとすると下記のようにPHPエラーが発生します。


Fatal error: Call to undefined method Composer\Script\CommandEvent::getOperation() 

実際にcomposerのスクリプト内を見ればわかりますが、post-update-cmdではCommandEventクラスが呼び出され、post-package-updateはPackageEventクラスが呼び出されているからです。

後はスクリプト次第なので、運用・保守で必要なロジックを書いていけばよいかと思います。





クラウド化によって捨て去られる技術

$
0
0

最近、クラウド化の勢いによって新規システム構築やリプレイスの案件においてオンプレミスでやろうということが少なくなってきました。

クラウドを利用することのメリットは、構築のコスト削減というだけでなくリリースまでのスピード感や保守・運用の手間を軽減することなどがありますので、システム部門ではない人にとってはこの部分のメリットがどうしても魅力的に感じるようです(実際はどうなのかはおいといて)。


しかし、クラウドを利用することにより、システム部門の存在意義が薄くなるだけでなく、今までその分野を支えてきたエンジニアの質の変化というのも起きていたりして、企業全体にとっての歪みが生まれているようにも感じたりします。



捨てる技術と注ぐ技術


クラウドによるサービス構築をするということは、そのシステムを作らず業務に注力できるということにはなりますが、今まで自部門で構築していたものがなくなったとなれば、当然その分野における技術力というのは低下していきます。


少なくとも、運用フェーズにおいても何らかの改善のための要件決めや他システムとのインターフェース部分の作りこみや業務の組み立て方などで話ができるレベルの人材というのは必要になってきますが、あくまで話ができるレベルであって、その構築や内部設計レベルで対応・理解できる人材というのは存在意義がなくなるため希薄になります


この辺のことは、企業内のキャリアパスにおいてエンジニアとしてある一定の経験を積めば上流工程の仕事を任せていくという流れがあったりもして、経験を積めば積むほどエンジニアの職人技を磨いていかせるというよりは、より単価の高い仕事を取らせるための人材育成に持ち込もうという方針もあったりでその流れを加速させる結果になっている気もします。

まぁ、クラウド化したのが単なるウェブシステムというのであれば、単に労力を削減したぐらいで済む話ではありますけど、その技術がどこでどう関連しているのかということを理解せずに捨て去ってしまうと、後々のトラブルが発生した際に対応できるエンジニアが全然いなくってベンダー任せになったり、自社の運用は手放したといってもその分野の技術を利用したシステム構築案件を受注したりしたときにわかる人がいないといった事態にもなるために注意が必要です。


自社のコアコンピタンスとなる分野の技術を捨てるということはないでしょうけど、技術は関連するために少なからずエンジニアの能力も失われている部分が出てきます。

こういったことは別にIT業界だけの話ではなくって、時代の変化によってニッチとなってしまった分野の職人さんが減っているというのも同じことで、失ったことでどうなるのかという未来を考えておかなくてはなりません。

それがレガシーな技術であって新しいものに取って代わられているのであれば単に時代錯誤な代物として見切ってもよいかもしれませんが、その技術が完全に死なない以上、何らかのかかわりを持たざるを得ないことはよくあることです。


例えば、メールは死んだなんてことは数年前から言われていますけど、現在でもビジネスでの情報交換の主流はメールになってますし、じゃあそれを見切るかといってもシステムからメールを飛ばしたいなんて要件は当たり前についてきます。

プログラム的には単にメール送信のメソッドを呼び出すだけでよいのかもしれませんけど、そのメールの配送経路やプロトコルまで理解しておかないと組み立てられないシステムもあったりして、専門のエンジニアがいない場合に現場で困ることにもなったり、トラブルが起きたときに調査・対応ができる人がいないとなると致命的な状況に陥る可能性があります



クラウドを操る技術の汎用性


クラウドサービスを利用する際に、単に表向きのシステムを使うということだけでなく、自社のシステムと連携させて使うといったケースがあったりします。

この際にクラウド側で用意しているAPIを通して連携させたりもするのですが、そういったクラウドを利用するAPIの知識というのはそのサービス特有のものであったりして、他でも同様の事ができるわけでもありません

確かに、プロトコル的にHTTP(S)をつかってRESTなAPIを叩くというようなコアになる部分は同じかもしれませんが、一度作ればそういったところもラッピングされてしまうため、単にパラメータの変更だけで保守が成り立ってしまうことにもなります(それが一つのメリットであることは確かなのですが)。


こういった状況が続けば、当然高度な知識を持っていないエンジニアでも対応できるようになるわけですから組織としてのスキルレベルが低下することにもなるでしょうし、エンジニア自身もそのサービスでのやり方しか理解できなくなって応用が利かない状況にもなります。

これは現実問題としてよくある、レガシーなシステムを数十年触っていてそのシステムの仕事しかできない人材が出来上がったという状況と変わらないわけで、単にそれがクラウドという名の今風な環境に変わったというだけです。


こうやって溜め込まれていくナレッジというのは、現場のエンジニアに対してシステム構築や保守・運用の中核を担うためのスキルというよりは、サービスやツールを扱うためのマニュアルのようなものであって、現にそういったマニュアルを細かくアップデートすることを何度もやらせているのもよく見る光景です。

そして、そのマニュアルが多ければ多いほど現場のスキルが充実していっていると勘違いしていることもよく起きていることだったりします。


クラウドという名の雲の中に複雑な技術を隠してしまっているわけで、その中の技術というものに誰も興味を示さなくなるのでその分野のスキルを磨こうという人が出てきません。

今までの煩雑な保守・運用から開放された!という喜びの中で、使わなくなったスキルを何時までも維持したいと思う人は少ないでしょう。

しかし、そこで得た新たなスキルというのは利用しているクラウドの中で閉じた世界であって、汎用性があるものでなかったりします。


別にクラウド自体を否定しているわけではなくて、要は使いどころをきちんと考えようとか、それを使ったときに使わなくなる技術(つまりはその分野のエンジニアがいなくなるリスク)って何だっけ、ってちゃんと考えた方がいいよと思うわけです。

オンプレ万歳という時代がくるということは無いと思いますが、クライド化への傾倒によって自社の技術レベルが失われ競争力も低下するリスクが出てこないのかということをちゃんと考えとかないとシステム部門としても何かあった際にベンダーに丸投げするだけではますます存在価値がなくなってくることになってしまうと思います。





プログラマにとって大事なことを考えるのが大事な理由

$
0
0

その人が優秀なプログラマかどうかって話は、結構そのチームで求められる役割に大きく左右されたりして、コミュニケーションをとるのがうまいとか、コーディングのスピードが速いとか、フルスタック的にインフラからアプリケーションレイヤまで幅広く話が通じるとか、様々あったりするのですけど純粋にプログラマとしての役割だけを見た場合、何か一歩足りないというか突き抜けていない感が出ている場合があったりします。


それは、実際にコードレビューとかをしてみると、仕事は早いんだけど単に動くだけのコードであったり、コーディング規約がばらばらで見づらいものであったり、プログラムへのこだわりが感じられなかったりと、要はシステムとして見ればなんら影響は無い(少なくとも表面上は)のだけど、蓋を開けてみれば色々と問題があるなと感じたりする時です。



プログラマとしてののこだわり


例えば、(記事としては古いのですが)最近話題になってた記事で


新人プログラマーに読ませて欲しいネーミングの大切さ

というようなものがありますけど、これも要は動くからどうでもいいと思ってしまうのか、俺がちゃんと書かないと後々面倒になると思うか(というよりそれが自然にできる)が分かれ道になってくると思うのです。

それは、過去に苦い経験をしたことからくる心理からかもしれませんし、世の中に出ているハウツーを見たときに得た共感によるものかもしれませんが、こういうことってプログラミングの世界で何が大事なのか、つまりはプログラマとして何が大事なのかってことが頭の中にあるかどうかによってくるんじゃないかと思ったりします。


それは細かいことかもしれませんけど、そういったこだわりを持つことでコードの質もだいぶ違ってくるでしょうし、プログラマにとって何が大事かを考えるってことは、それに反した状況に陥ったときに「じゃあどうすればいいのか」という探求の反復にもつながります。

技術レベルで言うと特定の技術であっても、技術同士が連動するため深く掘り下げていくことはかなり困難なわけですけど、そういったこだわりを持っている人はその辺を気にしなかったりします。

プログラムのレイヤーを離れてミドルウェアやカーネルのレベルまでソースを確認したり、そうするために必要な知識を身につけることを苦に思っていなかったり。


こだわりというのはもちろん自分の好き嫌いというものではなく、プログラムとして何が正しいのかということを知っているということです。

何が正しいのかを知るためには、正しい方法を知る必要があります。

つまりは正しい方法を知るための模索が必要で、それにはかなりの努力を要します(そして本人的にはあまり努力しているという感じを見せませんし実際にそうは思ってないんだと思います)。

この探究心がある限り、その人はプログラマとしての成長が望めますし、コードの善悪がわかることでシステム全体の質を上げることに貢献してくれます。


単に動くコードを書くというのではなく、その場に適した正しいコードがかけるというのはプログラマの能力として大きな違いがあると思うわけです。



何故正しいコードを書く必要があるのか


それは、開発の生産性があがるとか、保守性が向上するとか、教育コストが抑えられるとか様々なことが言われたりしていますが、そういったことを改めていいたいのではなく、肝心なのはコードはコピーされるという事実です。


新人プログラマが学習するために書くコードは、ネットで調べたものや参考書の中のコードを書き写したものが多いと思います。

実際に現場でコーディングをすることになってもサンプルとして参考にするプログラムを教えられたりしますので、正しくないコードは正しくないコードとして学習されてしまいます。


正しくないコードでも正しく動くのは事実だったりしますが、パフォーマンスに問題があるかもしれませんし、セキュリティ的なリスクが内在しているかもしれませんし、レガシーの枠を超えることができずにプログラマの知識向上が行えないかもしれませんし、そんなコードを何時までも保守することでモチベーションが下がるかもしれません。

ですので、例えそれが動くコードであったとしても「このコードはテストをパスするかもしれないけど、そんなコードはクソコードだ!」ってはっきり言える人が必要なんだと思います。

(もちろんそんなにはっきり言うプログラマは扱いに難しい側面もあるわけですが)


そうしないと正しくないコードがどんどん生産され、それがコピーして量産されていきます。

その結果は先ほど書いたとおりのことが起きるかもしれないわけで、優秀なプログラマを育てたいというのと正反対の環境が生まれます。

何がプログラマとして正しいのかということを知っている人がいれば、コードが是正されていくわけでそこで正しいコードというものが作られていくきっかけとなります。


正しいコードこそが、プログラマとして何が正しいのかという知識を深め、優秀なプログラマを育てるための環境を作ってくれると思うわけです。





新人の頃に抱く疑問はメモっとけって話

$
0
0

入社したての頃はそれまでと比べて社会人としてのルールやスタイルなどの違いや、仕事のやり方などに様々な疑問や不明点というものが出てきます。

ただ、そういったことは社会人の経験を重ねるとともに忘れていったり、そもそも疑問にすら感じなくなってきます。

理由は、要領を掴むことで応用が利くようになったり、慣れによって感覚が麻痺してしまうからでしょう。


それはある意味成長なのかもしれませんが、同時に視野が固まったり考え方が偏っていることでもあります。

要は一般的な社会の一員に染まってしまうということです。



疑問を残す


自分の部署では新入社員が入ると、仕事において疑問に感じたことをメモしてもらうことをしています。

まぁ、一般的なことではあるんですけど自分のノートにまとめるんじゃなくてデータで残すというようなことをしてます。


それは、例えばわからない技術的な専門用語であったり、仕事でよく使うような略語であったり、仕事のルールであったりと様々なのですが、そうしている理由は入社したての右も左もわからない状態からあとで見直してみるとそんなこともわからなかったのかという成長の軌跡を実感してもらうためであったりするのですが、それは表向きのものであって実際の効果というのはそんなに高いようには感じていません。

何故なら途中で実務が多くなるにつれてメモをする余裕がなくなってきたり、詰め込みの教育の現場からすぐに答えが出されて自分で調べるという時間が与えられなかったり、過去の記録を見返すという行為自体をしなかったりするからです。


本当の狙いとしては、わからない人の視点というのはどういったものかを実感して欲しかったり、過去の自分も含めて視野レベルが違う人がいるという認識を持ってもらうことにあったりします。

それは、仕事において人と話すときに相手の知りたいポイントを探ったり相手のその分野においてのレベル感を量るときに役立ったりします。

また、実際にわからない人の視点というのは結構貴重だったりもするわけです。

わかる人になってしまったときに、わからない人の何が困っているのかがわからなくなります

そして、多くの製品やサービスというものは困っている人やわからない人に届ける物が多かったり、最初に触れるものへの抵抗感を限りなく低くすることというのは重要だったりもするのですが、過去のわからない頃の自分の中にそのヒントが埋もれているかもしれません。


だからこそ、わかる人になってしまう前にわからない頃の自分の考えや思いの断片を残しておくというのは大切なんじゃないかと思うわけです(まぁ、あとから見直したら結構恥ずかしかったりもするんですが)。



ギャップを知る


過去の自分を見つめなおす機会は今の自分とのギャップの確認でもあります。

それは、過去の時点で想像していた未来の自分と現時点の自分とのギャップでもあります。

こういったギャップを確認するというのは仕事においてもよくあることで、特に失敗しそうな(した)時にAsIsとToBeとのギャップを分析することは大事だったりもします。


ギャップを知るにははっきりとした基準が必要で、今の自分というのはいつでもはっきりしていますが、過去の自分というのは記憶から薄れていくとどんどんとぼんやりしていったりします

特に社会に出てしまうと、次々に頭に詰め込まれたり、タスクを割り振られたりで過去の自分がどんどん流されてしまったりして、夢ややりたい事というのも薄れていったりします。

ですから、過去の自分とのギャップを知りたくてもぼんやりとした記録の中で確認していかなくてはなりませんから何らかの記録の断片があったほうが分析しやすくなるわけです。

頭の記録に頼ると「あー俺、この時はこんなこと考えてたっけ」ぐらいな記憶しかなかったりもするのですが、それがあるだけましという場合もあって、実際のところ考えが大きく変わっていることにさえ気が付かないことはよくあったりします。


TwitterやFacebookなどへの投稿でさえ日々の自分の新しい発言に過去の自分の発言は流され薄められていきます。

こんなにデジタル化が進みなんでもかんでも記録し記録される時代であっても過去の自分というものを見直したり発見するのはとても難しいことです。

皮肉にもビッグデータとか言う自動的に記録されていく動向分析の中の自分(のデータ)のほうが、自分自身よりはっきりとした情報を持っているかもしれません。


まぁ、過去の自分とかそんなことを気にせずに前に進めということかもしれませんけど、闇雲に進めばいいというものでもないでしょう。

きちんと何処にいて何処に進めばいいのかというものさしが必要です。

なので、何かにその時の自分をメモしておくというのは大事だって思ったりするわけです。

メモする方法は色々あるわけですが、個人的にはみんなブログ書こうぜとか思いつつ、このエントリをまとめたいと思います。





[Composer] プログラムのオートロードはComposerにお任せ

$
0
0

composerには、composerでインストールしたパッケージを呼び出すためのオートローダー機能を備えています。

これは何もcomposerでだけ使えるというわけではなく、自分たちで作成したライブラリなんかもこのオートローダーを使って自動で呼び出せたりす るので便利です。


もちろん、クラスファイルのオートローダーを実装する で書いたように、自前のオートロー ダーを作成することもできますが、ライブラリのパスが固定になったりPSRなどのコーディング規約に沿ったファイルパスを使いたいといった場合 に作りこむのが手間だったりもします(そういうサンプルは公開されてたりもしますけど)。



PSR-0に準拠したクラスファイルをオートロードする


ってことで、composerのオートローダーを使って自前のライブラリを呼び出すための手順です。

マニュアル に書いているように、オートロードする には幾つかの方法が存在します。

まずは、PSR-0に準拠したライブラリパスで呼び出す方法です(ComposerはPSR-4にも対応しています)。

composer.jsonに下記のように名前空間とクラスファイルが置かれたパスを指定します。


{  "autoload": {    "psr-0": {        "Foo\\Bar\\": "/path/to/lib/"    }  }}

PSR-0の規則に沿うと上記の場合は「/path/to/lib/Foo/Bar/」内にあるディレクトリがオートロードされます。

パスは相対パスでも記述できますが、その場合はcomposerのルートディレクトリ(composer.jsonやvendorディレクトリが存在するディレクトリ) から見たパスとなります。

また、パスは間違っていたとしてもcomposer updateで再構成した際にエラーなどは出ないので注意が必要です。


{  "autoload": {    "psr-0": {        "Foo\\Bar\\": "../../lib/"    }  }}

composer updateを実行するとオートロード用のファイルが更新されます。

具体的には、vendor/composer/autoload_namespaces.phpに先ほどの名前空間とそれに対応するパスが配列に追加されます。


<?php// autoload_namespaces.php @generated by Composer$vendorDir = dirname(dirname(__FILE__));$baseDir = dirname($vendorDir);return array(    'phpDocumentor' => array($vendorDir . '/phpdocumentor/reflection-docblock/src'),    'Foo\\Bar\\' => array('/path/to/lib/'),    'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),    'Log' => array($vendorDir . '/pear/log'),);

自前のオートローダーをどうしても作りたい場合は、上記のファイルからごにょごにょしてもよいかもしれません。

余談ですけど、オートロードのファイルだけを更新したい場合は


$ composer dump-autoload

とするだけでもいけます。



独自のクラスファイルをオートロードする


続いて、PSRに準拠していないクラスファイルを読み込みたいという場合ですが、Classmapという仕組みを使うことができます。


{  "autoload": {    "psr-0": {        "Foo\\Bar\\": "/path/to/lib/"    },    "classmap": [      "/path/to/another/lib/",      "Foo.class.php"    ]  }}

Classmapは単純に読み込みたいディレクトリを指定するだけです。

Foo.class.phpのようにダイレクトにファイル名を指定することも可能です。

Classmapの場合は、composer updateまたはdump-autoloadを実行するとvendor/composer/autoload_classmap.phpに情報が出力されます。


一応、特定のファイルを読み込みたいという場合はfilesという項目も存在します。


{  "autoload": {    "psr-0": {        "Foo\\Bar\\": "/path/to/lib/"    },    "classmap": [      "/path/to/another/lib/",      "Foo.class.php"    ],    "files": [      "Bar.class.php",      "Hoge.php"    ]  }}

こちらは特定のファイルを読み込むように書くだけですが、classmapでも対応できるので正直なんであるのかよくわかりません。

オートロードが完了したら、composerのautoloader.phpを読み込んであげれば自由に使えるようになります。


<?phprequire_once 'vendor/autoload.php';$obj = new Hoge();$obj->bar();

自前で用意するよりも柔軟に管理ができたりもしますので、オートロードは全部composerにお任せというのもありかもしれません。






仕事が中断するというコミュニケーションの弊害

$
0
0

仕事をする上では、少なくとも数名でチームを組んで取り組むことが多いですし、そもそも会社という組織に入れば上下関係などからコミュニケーションは避けて通れない状況に陥ります。

ただ、そこでのコミュニケーションによってたびたび自分の仕事が中断に追い込まれ、酷く生産性の悪い状況に陥ったりするのですが、こういった議論ってチーム内であんまりされませんしオフィスでも意図的ではないにしろ邪魔してくださいといわんばかりの環境が多かったりします。



中断から再開へのエネルギー


仕事が中断に追い込まれる理由は多々あります。

部下が不明点を質問にきたり、同僚が雑談してきたり、上司が資料を作るように急かして優先度を変更してきたり、打ち合わせが入ったり、トラブルが起きたり。

仕事のことではなければ「今は忙しいので後にして」とあしらうこともできますが、仕事のこととなれば相手も急いでたりしてどうしてもすぐの答えが求められたりします。


こうやって中断する仕事を再開するエネルギーって結構なもので、自分の事で言えばコードを書いてる集中力が途切れたり、ログを追っている状況なら何処まで見たかわからなくなったり、設計資料を書いているものなら実装しようとしている仕様が頭から飛んだりもします。

そんな状況ですぐにまた同じ状況に復帰できるかというと難しく、かなりの時間をかけて徐々に回復させていかなくてはなりません。

集中力とかは再開できるってレベルのものでもなく、「もう今日は無理」って状況になったりもしますし、エンジニアの仕事に限らず黙々とやるタスクってゾーンに入ることがあったりして、ものすごいスピードで処理できる気分を味わえたりもするんですけど、そんな中で仕事が中断させられると再びゾーンに入れることはありません(そもそも長続きする状況じゃないのにさらに横槍入れられたらモチベーションも下がりますし)。


仕事が中断されるというのは生産性が悪くなるという以外に、本人にとってとてもフラストレーションが溜まります

気分が乗っていたのに止めさせられたり、思い出す作業で時間をとられてしまったり、やりたいことができないという自分のペースを乱されることへのイライラが募ります。

相手は自分の目的を達成するので特に気にしませんが、中断された側としては自分の目的が達成できない状況に追い込まれることにかなりの負担を強いられたりもするわけです。


これは誰しも経験があることだと思いますが、仕事を中断させてしまって悪いなと思いつつ、「今ちょっといいですか?」という問いに対して「大丈夫」と答えられたらついつい相手の仕事を遮ってしまいがちです。

多くの場合は肯定的な回答を貰うことで相手の仕事を中断させる免罪符を得てしまいますし、それ以降は気にも留めずに話し込んでしまったりもします。

オフィス環境も、多くは長机に数人が座るような構成が多いでしょうから話がしやすいですし、回りで話し込まれたりウロウロされたりしたら気が散ってしまうような環境も悪循環に拍車をかける形になっている気がします。



集中する環境を作る


で、どうやって仕事に集中できる時間をなるべく多く確保できるようにするかって事に関して色々言われてたりもしますけど、権限をフラット化してある程度個々の判断に委任することで細かい承認作業を省略したり、私語や会議を禁止する時間帯を設けている企業もあるとか聞きますし、オフィスにしても各自に個室を分け与えるのは無理でも別室で仕事ができるようにする事で雑音を遮断できる環境を与えてあげることができるかもしれません。


まぁ、多くのことは一個人として状況を変えていくのは難しいので、うまいこと喋りかけるなオーラを出す方法を身につけるぐらいしかないかなと思うんですが、こういうのって個人の性格にもよったりするので会社の中でのキャラクターというのも大事な要素になってくるな、とか思ったりします。
後は、一日の予定の中で予め時間が細切れになることが予想できるのであれば、長く時間がかかるようなタスクというのは諦めてしまうとか、時間を確保するために集中的に雑務をこなしてしまうというのもありかもしれません。

が、大体の場合は今日の雑務が片付いても明日の雑務が発生するので、やりたいことややらないといけないことは他の雑務を切り捨てることを割り切って、最初から優先度を上げてやっていってしまった方が効率的だったりもするんですけどね。


これが管理職の立場となると、部下にその集中する時間を確保してあげなくてはなりませんので、ある程度自分が犠牲にならざるを得ないことも多々あります。

「お前まだ仕事終わってないのか」とか自らが集中する時間を遮ってしまうのは害悪でしかないですし、そもそも当人に依頼しているタスクは、その当人がやることが現状を考慮してベストと判断しているのであれば、その人が最も集中して仕事をする必要があるわけです。

会議なんかでダラダラと説教とか始めると、その全ての人の時間を奪うことにもなっているわけで、「その話を聞いたところで仕事が進むわけではないんだよ」とかはよく思うところです。


コミュニケーションをとることでしか進まない仕事も当然ありますので一概に言えないことではあるんですけど、馴れ合いの組織環境にいたりするとそれがかなり悪く働いたりしますし、一方で疎遠な環境だと仕事は集中できるものの、それぞれがあらぬ方向に進んでしまうミスも発生したりし、息苦しさを感じる人も多くなるかもしれません。


ある程度、話すことでしか解決できないコミュニケーションと、話さなくてもよいコミュニケーションというものをルール付けして仕事をした方が効率的になるのではないかと思います。

前者は厳密に言うとそんなものは存在しないんでしょうけど、早急に決めないといけなかったり、トラブルなどの対処が必要なものだったり、話す内容が複雑でテキストで伝えることが困難な場合といったものがどうしてもあったりします。

後者の話は要はメールとか、チケット管理システムとかのツールを使うといったケースで、大体の場合はこちらで相手の時間をさえぎることなくコミュニケーションができたりもします。

問題は、すぐに答えを欲しがったり、人と話すことが好きだったり、権威を示したいと思うような人がいるってところで、結局はメンバーの意識改革をするというのが一番難しいところだなと思う今日この頃です。





[Composer]開発と本番環境を分離する

$
0
0

composerを使って管理するパッケージの中で、開発環境では必要だが本番では必要ないといったパッケージもあったりします。

例えば、テストツールのPHPUnitは開発環境では必要だけど本番ではいらないといった具合です。

こういった場合でも、composer.jsonをうまく書いてあげれば環境を分離して不要なパッケージを本番環境に導入せずに済んだりします。



開発環境で必要なパッケージを定義する


前回の[Composer] composer.jsonを読み解く にて、composerでインストールしたいパッケージはrequireキーの中にJSONの配列形式で記述すると書きましたが、require-devという項目も存在します。

文字通り、開発環境で必要なパッケージを定義するもので、デフォルトではこのrequire-devに指定したパッケージもインストール、更新の対象となります。


{  "require": {    "pear/pear_exception": "1.0.*@dev",    "pear/log": "dev-master"  },  "require-dev": {    "squizlabs/php_codesniffer": "dev-master",    "phpunit/phpunit": "4.5.*"  }}

そのままupdateを実行するとrequireおよびrequire-devの両方がインストールされます。


$ composer show -idoctrine/instantiator             1.0.4              A small, lightweight utility to instantiate objects in PHP without invoking their const...pear/log                          dev-master df7aa17 PEAR Logging Frameworkpear/pear_exception               1.0-beta1          The PEAR Exception base class.phpdocumentor/reflection-docblock 2.0.4              phpspec/prophecy                  1.4.0              Highly opinionated mocking framework for PHP 5.3+phpunit/php-code-coverage         2.0.15             Library that provides collection, processing, and rendering functionality for PHP code ...phpunit/php-file-iterator         1.3.4              FilterIterator implementation that filters files based on a list of suffixes.phpunit/php-text-template         1.2.0              Simple template engine.phpunit/php-timer                 1.0.5              Utility class for timingphpunit/php-token-stream          1.4.0              Wrapper around PHP's tokenizer extension.phpunit/phpunit                   4.5.1              The PHP Unit Testing framework.phpunit/phpunit-mock-objects      2.3.1              Mock Object library for PHPUnitsebastian/comparator              1.1.1              Provides the functionality to compare PHP values for equalitysebastian/diff                    1.3.0              Diff implementationsebastian/environment             1.2.2              Provides functionality to handle HHVM/PHP environmentssebastian/exporter                1.2.0              Provides the functionality to export PHP variables for visualizationsebastian/global-state            1.0.0              Snapshotting of global statesebastian/recursion-context       1.0.0              Provides functionality to recursively process PHP variablessebastian/version                 1.0.5              Library that helps with managing the version number of Git-hosted PHP projectssquizlabs/php_codesniffer         dev-master f307928 PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a def...symfony/yaml                      v2.6.6             Symfony Yaml Component

本番環境(require-devで指定したパッケージは不要)の場合は、下記のように実行します。


$ composer update --no-dev$ composer show -ipear/log            dev-master df7aa17 PEAR Logging Frameworkpear/pear_exception 1.0-beta1          The PEAR Exception base class.

require-devのパッケージをインストールしてしまった場合でも、--no-devオプションをつけたら不要なパッケージは削除してくれます。

オプションの順番を変えて


$ composer --no-dev update

でも動いたりしますので、本番環境では省略したcomposerコマンドのAliasを作ってしまってもよいかもしれません。


ちなみに、--no-devオプションはオートロードでも有効になります。

本番環境と開発環境で読み込むライブラリが違うといった場合は、下記のように環境を分けることもできたりします。


{  "autoload": {    "psr-0": {      "Foo\\Bar\\": "prod/lib/"    },    "classmap": [      "prod/another_lib/Foo.class.php"    ]  },  "autoload-dev": {     "psr-0": {     "Foo\\Bar\\": "dev/lib/"    },    "classmap": [      "dev/another_lib/Foo.class.php"    ]  }}

classmapで指定しているように本番(require)と開発(require-dev)で同一のクラスを読み込んでいる場合、デフォルトだとautoload-devのものがオートロードされます。

本番環境用にしたければ同じように--no-devオプションを付けます。

dump-autoloadでも使えます。


$ composer dump-autoload --no-dev


composer.jsonを環境ごとにうまく管理していけば同様のこともできたりもしますが、手間となったりミスがあった場合に余計なパッケージが導入されてしまうというリスクもあるため、うまく活用すれば管理が楽になるかもしれません。





新人エンジニアの教育を悩ませる7つの環境と課題

$
0
0

4月も少し過ぎ、新しく入社したエンジニアたちは会社の事業内容や規則、業務フローの説明などもほどほどに具体的に現場で仕事をするための教育が始まっているかもしれません。

ここでは、プログラミングやHTMLの知識、ネットワークやサーバー周りの知識など所定のカリキュラムに沿って教育が実施されと思いますが、何れも十分な時間をとることができないため、広く浅くといった教育になりがちです。


また、一方でそのような教育を受けたとしても具体的に何を何処で使うのかがわからなかったりするため、次のステップとしてOJTなどでもう少し具体的な知識を身につけさせたり、実践に近いような形式で課題を与えてスキルアップさせたりする次の教育が始まったりもします。


ここで課題となるのがより実践に近いものを作らせるというものなのですが、そもそも具体的に何を作らせればいいのかと今度は教育をする側が悩むことにもなったりします。

理由は、インフラやサービスが整いすぎて新人レベルで作らせられるものが見つかりにくいというものです。



新人エンジニアの教育を取り巻く課題


何か新しい技術を教育したとしてもあくまでそれは机上のことであって、実際にそれを使ってみないことにはなかなかスキルというものは身につきにくかったりしますし、保守・運用の課題は実際にそれを保守・運用してみないと気が付かないことは多々あります。

要は、一定のスキルアップを図るためには実際にそれを作らせ、運用させ自分自身で体験し、自分自身で気が付かせるということが大事だったりします。


ただ、問題はその適当に作らせるものが見当たらないということです。

適当というのは、架空のサービスを作らせたとしても実際にそれを運用させるというのは難しかったりもしますし(作ったら終わりとなるので)、利用者が多いサービスの改修などをやらせてしまっては影響が大きかったりもして、その粒度が適当なものが見つかりにくいというものです。

例を挙げると以下のような点が理由にあります。


1. クラウドなどで安価な外部サービスをすぐに使うことができるのでわざわざ作る必要が無い

2. 知識としては必要だが、その技術を自社で保有していない

3. 失敗しても許される環境が無い

4. 業務フローやマニュアル外のことをやらせたくない

5. 技術がカプセル化しすぎていて具体的に中身を見せるのが難しい

6. システムのライフサイクルが長くなっている

7. そもそも時間が無い


もう少し具体的に書いていくと、1.に関しては車輪の再発明を避けるために今あって正しく動いているものをわざわざ新たに作る必要は無いという考え方です。

もちろん、教育の一環で作らせることは幾らでも可能なのですが、この場合はそれが本番としてサービスインできるようなものを新人自らに作らせるか、という点においてです。

一昔前なら、「これは自分が新人のときに作ったサービスだ」ってものが動いていたのかもしれませんが、今やよりよいサービスが低コスト・短納期で始められるのに悠長に作らせている時間は無いと思っている人も多いのかもしれません。


2.に関してはクラウドの流れと関連がある話ですが、以前に書いた「クラウド化によって捨て去られる技術 」のように捨てた技術というものもあったりして、ただエンジニアとして最低限その知識は持っておきたいものの具体的に触れることができる環境が手元に無いというものです。

ですので、参考書で読みました、ネットで調べて知識を得ましたというレベルまでしか教育ができなかったりします。


3.に関してはシステムの数が増え、また互いに密接に関わりあうような構成になっている関係で、一つのトラブルが他にも大きく波及してしまうので失敗が許されないというものです。

当たり前の話だったりもするのですが、それに加えてITサービスに対してユーザーの目が肥えていることもあり、何かトラブルが起きることのリスクを保守・運用側も恐れ、絶対に失敗させないような教育カリキュラムにすることで、踏み込んだ経験をさせることができなかったりもします。

実際には失敗でわかることやその経験が次に活かせることは大きかったりするので失敗することは悪いことではないんですけどね。


4.は3.の失敗できない環境に関連して、そのリスク回避として膨大なマニュアルが作られたり厳格な業務フローが定義されていたりすることです。

マニュアルどおりにすることを優先させ、その技術の本質に触れる機会が奪われたり、本番環境ならまだしも開発環境やソースにコミットをかけることすら申請が必要だといった厳格なフローが決められていたりしたらそのマニュアルやフローから外れるような知識は一切身につけることができなかったりもします。


5.は豊富なライブラリや仮想化技術の発展により、技術のコアに触れる機会がなくなっていたりしています。

開発効率を上げるために作り上げた独自のメソッドやAPIを通してデータアクセスや加工ができるとなると、その中で行われている処理の詳細がわからなくてもシステムを組み上げることができたり、物理的なサーバーがなくなることでハードウェアに触れる機会が無かったり、アプリケーションの仮想化によりミドルウェアが抽象化され何が動いているのかわからなかったり、簡単に環境がコピーできたりもします

こういった知識はトラブルが起きたときに影響が大きかったりもして、この環境がないと対応できませんとか、GUIじゃないとやったことがありませんとか、API叩いているだけなんでデータ構造は知りませんとかなったりして何もできない状況に陥ったりもします。


6.は仮想化などの技術の発展、ベンダーによる保守の延長、クラウドを利用することによるオンプレミス型の運用からの開放など、システムのライフサイクルがずっと長くなることでリプレイス案件などが少なくなってきたりしています。

つまり、先輩が作ったものを後輩が作り変えるといった案件がなくなることで、経験の場が失われていったりもします。


7.は言うまでもなく新人教育に割く時間がそもそも無いというものです。

初期の教育期間が終われば即、現場に出されるというのも珍しくありませんし、そこで経験を積めばよいという考え方の管理職も多数いたりします。

しかし実際には、これまで書いてきたような理由により現場でもなかなか思った以上の経験を積むことができず、要するにその環境でしか仕事ができなかったり、マニュアルどおりの対応しかできないエンジニアが育ったりして何時までも教育に関する悩みは尽きない状況となります。



まとめ


何れにせよ、思い切った経験をさせるということは本人にとってかなりプラスになることです。

自分としてはかなり恵まれていた(?)のか、多くの失敗が経験できる環境で教育を受けました。

実際のところ教育というよりは放任され、自分たちで作れ何とかしろって感じだったので、自ら作ったもので失敗を重ね、改良を加えていくということを繰り返してわかったことが多かったように思えます。


今ではそのような堂々と失敗できる環境や全てをコントロールできるような環境というのはなかなか見つからず、ある意味新人エンジニアにとってはつまらないものとなっているかもしれません。
そのようなことを促進できるためにも、管理する立場がもう少し自由度を与え、何かあったら自分たちで責任とって復旧するぐらいの考えを持たないといけないのかもしれません。





WindowsのXAMPP環境下でPHPからLDAPを使う

$
0
0

以前に「PHPからActive Directoryに認証・パスワード変更する方法 」について書きましたが、このソースコードを開発環境であるローカルのWindows7(+ XAMPP)環境に持ってくるとうまく動作しなかったのでその回避方法についてのメモです。

確認した環境でのXAMPPのバージョンは1.8.2、含まれるPHPのバージョンは5.4.31です。



LDAPが有効になっているかを確認する


まずはXAMPPのPHPがLDAP関数を使えるようになっているかを確認します。

手っ取り早い方法はphpinfo()で見てみることです。


ldap.gif


上記のようにLDAP Supportがenabledになっているかを確認します。

なっていない場合は、PHPの設定でLDAPを有効にしていない可能性があるので、php.iniファイルを編集します。


extension=php_ldap.dll

php_ldap.dllは、php/extディレクトリ内にあるはずです。

設定変更後は、Apacheを再起動し再度phpinfoでLDAPが有効になったかを確認します。



LDAPSを使えるようにする


LDAPS(LDAP over SSL/TLS)を使いたい場合は、OpenSSLの設定も必要になります。

LDAPSを使いたいというのは、


$conn = ldap_connect('ldaps://ldap.example.com');

というような接続をしたいというケースです。

まずは、OpenSSL関係のextensionを有効にしてdllを読み込ませます。


extension=php_openssl.dll

続いて、証明書の検証を無効にするように設定しておきます。

接続先のLDAPサーバーが自己証明書を使っている場合、この設定を入れておかないとチェックに引っかかって接続できません。


c:\openldap\sysconf\ldap.conf

上記ファイルを編集します。

存在しない場合は、新しく作成します。

その後、下記の一行を追加します(中身はこの一行のみで大丈夫です)。


TLS_REQCERT never

これにより証明書のチェックが行われなくなります。

この設定無しに、LDAPS経由でサーバーに接続すると「Can't connect to the LDAP server」が返ってきてうまく接続できませんでした(LDAPだとうまく接続できたり)。

Linuxサーバー上だと、意図的にopensslをインストールしているので設定ファイルとか把握していましたが、XAMPP環境だとオールインワンのせいでどこに何がインストールされているのかよくわからないため、こんな設定ができるとも知らず結構はまってしまいました。





エンジニアの評価と組織の立ち位置との矛盾

$
0
0

もっと技術力を高めたいとか、知らない分野の知識を貪欲に吸収していって成長していきたいと願うエンジニアって結構いたりしますが、会社の組織の中においてそういう人たちに適切な評価を与えることによってその成長機会を奪うケースって結構あるような気がします。


どういうことかというと、組織において一定の評価を得たときにそれ相応の役職が与えられたりして、それによって本来のエンジニアとしての仕事以外のこともせざるを得なくなるというものです。

また、与えられた権威が邪魔して競争力や本来の貪欲さを失ったりすることもあったりします。



組織のトップとしての仕事


会社の中のスキルパスとして、管理職や専門職といった明確な区分けがあったとしても一定の職位まで達した際にのしかかる仕事はあまり相違が無いものになったりもします。

例えば、任される組織内の人員管理であったり、その組織においてミッションを達成するための計画化であったり、その予実管理であったり報告であったり。


こうなってくると専門職としてのスキルパスを描いていたとしても、本来やりたいこととは異なる業務が与えられた職位によってやらざるを得なくなり、技術を磨く機会は減ってきたりもします。

よくテレビとかでその分野で実績を出している科学者とかが記者会見で実績の報告をしたりする姿を見たりもしますが、本来そういうことってやりたくないんじゃないかって個人的には心配になったりもします。

もちろん、その実績を誰よりわかっているのは当人であり、その実績を世間に的確に説明する義務があったり、一部ではその組織の広告塔としての役割も一員としてやらざるを得ないのは理解できるのですが、その時間があったら研究を進めたいとか思っている人も多いんじゃないかと思ったりもするわけです。


職人気質なエンジニアはこの辺のことにあまり興味が無かったりして、自分の実績や持っている技術力の価値をうまく伝えられずに適切な評価を受けていないケースも多々あったりするので、こういうある程度の職位に付く人ってコミュニケーション能力やプレゼン能力も長けててそんなことを苦労していると思っていないのかもしれません。

ただ、何れにせよ当人が本当にやりたいことに割く時間が奪われていることは確かなので、組織としての役割とその人が望む成長機会とは相反する部分っていうのは出てしまうのだと思います。



No.2であることの意義


自身のスキルを磨きたいと望んでいるエンジニアにとって、組織の中の立ち位置としてNo.1を目指すことにあまり興味はなかったりします。

技術の世界においてNo.1という称号はそもそも判断が難しいことですし、一般社会においてはスポーツのように何か勝ち負けで決まるわけではありません。

その技術分野の第一人者というのはあったりしますけど、それは新しい分野を切り開いた人であり、多くの企業にとってそんな技術的に新しい分野を切り開くという事業をしているのはよっぽど大手のIT企業や業界団体ぐらいしかないのではないかと思うんですけど、つまりは一般企業が抱える優秀なエンジニアにおいてNo.1の称号を与えるのは大小問わず組織の階級と一致させるしかなく、多くの企業はそういった評価基準を採用しているのだと思います。


ただ、それによって今日から君がこの分野のNo.1だと言われたところで当人の心境とのギャップもあったりします。

技術を磨いていくというのは自分より優れている人の存在がいることが大きかったりもします。

あの人が書くコードが凄いから自分も負けないようにしたいとか、悩んでいることを聞くと幅広いレイヤーでアドバイスをくれるその知識量にあこがれるとかあったりするわけで、技術を貪欲に磨いていくにはNo.2であることの意義って結構大きかったりもするわけです。


もちろん、そんなこと気にせずにスキルアップを目指すエンジニアも多いとは思いますが、それは組織外でもうまく理想の人を見つけられる人だったりして、ある組織の中でしか視野をもてなくなってしまうと途端にそのNo.1の称号が邪魔をして本来の意欲を失わせてしまうことにもなったりします。

これは、その組織に本人をとどめておく意味を失わせることにもなりますし、留めておいたとしても成長機会を与えたと思っている組織と成長機会を奪われたと思う当人との間に溝ができることにもなったりします。


だからNo.2にしておけというわけではなく、スキルアップに貪欲なエンジニアへの成長機会の与え方と評価の仕方というのは組織構造とは離れて考えるべきでしょうし、逆に優秀なのに職位が無いから評価が低いというは考え方として正していかないといけないのではないかと思うわけです。





[PHP] フレームワークをどうやって選んでいくべきか

$
0
0

とある案件で新しいPHPのフレームワークを使おうという話になり、結論としては最近流行のLaravel を使おうということになったのですが、どういったステップで選定作業をしていったのかのまとめを書いてみます。

どちらかというと選定ステップに重点を置いて書いているのであまり技術的なことは書いてません。



まずはどんなフレームワークがあるのか


ググって見たら色んな情報が出てくるわけですが、下記のQiitaのまとめがPHP以外にも様々な言語のフレームワークの情報が掲載されていて役立ちました。


2014年 Webアプリケーションフレームワークトレンド(PHP / Java / Ruby / Python / Perl) / Qiita


2014年って言っても年末に書かれているので比較的新しいソースになってます。

さて、この中から何を選ぶかということになりますが、PHPのフレームワークだけでも17も載っていてこれらを一つずつ調べていくほど時間はありません。


ある程度自分が見聞きしたことがあるってもので対象を絞ってもよいですが、フレームワークを使うに当たって情報量の多さって命綱になるので、その辺で候補を絞るのが無難かもしれません。

こうなってくると、このまとめが書かれた時点でのGoogleのインデックス数から


・ FuelPHP

・ Zend Framework

・ CakePHP

・ CodeIgniter

・ Symfony

・ Laravel

・ Yii


といったフレームワークが候補として出てきます。

個人的にはCakePHPが好みだったりもしたり、Symfony1系を使ったことがあったりでその後継(2系)でいいじゃんみたいな意見もあったりしたのですが、もう少し客観的に見て選んだ方が後々後悔しなくて済みそうです。

ここからさらに絞り込むに当たって、どんな基準で選定していかないといけないかをまとめて見ます。



情報量の多さで選ぶ


先ほど書いたように新しく習得するフレームワークの情報量の多さってかなり大きなウェイトを占めたりします。

もちろん、ネット上の情報だけで判断する必要も無いので、きちんとまとめられた優良な書籍があれば事足りたりもしますが。


後は、情報量が多いといってもなるべく日本語の情報であったほうがありがたかったりもします。

エンジニアたるもの英語圏の情報ぐらい読めってのは全くその通りだったりもするのですが、その案件に多くのエンジニアが携わることになった場合、やはり日本語の情報が多い方が学習コストやトラブル時の対処において調査に大きな時間をとられずに済むので助かります。


で、日本語の情報量ということになった場合、先ほどのQiitaのまとめではStackOverflow のタグ数も参考値に入れていますが、StackOverflowは主に英語圏でのエンジニアQAサイトのため、これが活発というのは主に海外での流行を表していたりもします。

日本語用のサイト もあったりしますが、本家よりもだいぶ情報量が少ないですし、大体困ったときにググってヒットするのって英語圏の人たちのやり取りだったりもします(それはそれで役立ったりもしているのですけど)。


一方で、Qiitaのタグ数を見るとこれは日本人エンジニア向けのQAサイトではあるため、(あくまでタグ数なので一概には言えませんけど)比較的日本においてのエンジニア数や情報の多さを表しているため、こちらを参考にした方がよさそうです。

こうなってくると、CodeIgniterやYiiはどちらかというと海外で主流になっていて、日本ではまだそれほど馴染みが無いように見受けられます。



安定性で選ぶ


もちろん採用する技術が安定しているに越したことはありません。

しかし、フレームワークの世界で言うと安定したレガシーなものはアプリケーション開発手法自体も古臭く安定の見返りに開発コストが増加したり、エンジニアの教育の観点で考えると採用することが望ましくないケースもあったりします。


安定しないといっても、不具合の修正などに伴うバージョンアップが繰り返し行われていればそれをカバーしてくれたりもします。

バグ修正や機能の追加・改修の対応が行われるというのはそのコミュニティが活発に活動している証拠でもあったりしますので、どれくらいのスパンでマイナーバージョンアップが行われているのかや、メジャーバージョンがどれくらいまでサポートされるのかの情報を調べてみるのは大事だったりもします。

(あんまりはっきりと何時までサポートするといったライフサイクルを提示しているフレームワークって無かったりもするんですけど)


このバージョンって結構厄介で、選定の過渡期でメジャーバージョンが発表されたりすると現行の安定バージョンと新バージョンのどちらを採用すればよいのか悩ましい問題に陥ります。

Laravelは現在4系の最新であるバージョン4.2に加えて、最近発表されたバージョン5の両方が並行して開発されていますし、CakePHPも2.6系に加えて3.0系もリリースされていたりしてこれらはアーキテクチャや開発手法が違っていたり、パフォーマンスも(場合によっては最新バージョンの方が悪かったりと)違ったりして、候補としてはメジャーバージョンが違えば別物として候補に上げて選定していった方がよいかと思います。



既存環境との親和性で選ぶ


新しいフレームワークを採用するにしろ、一部は既存の環境を流用しなければならないというケースはあったりします。

例えば、DBは今使っているOracleを使おうといったもので、これは全く新しく環境を作るにしてもシステム要件からどうしてもこのOS環境やソフトウェアを採用しなくてはならないといったことがあったりもするので、その環境に新しいフレームワークが馴染むのかどうかを調査しておくのは重要だったりもします。

(FuelPHPはこの辺で採用が見送られたりもしました)


DBの件で言えば、フレームワークに付属しているORMとかは全てのDBに対応できてなかったり、そのために本家とは別に有識者による別のモジュールを導入せざるを得なかったりもします。

DBの仕様からシステム構成が強要されるケースもあったりして、例えばMySQLのAUTO_INCREMENTはOracleにはない機能なので、別でシーケンスを作る必要が出たりもします。

これは別にフレームワークの問題ではないんですけど、シーケンスが増えるとシステム移行の際に保守・運用で手間が増えたりするので、既存環境を優先すべきなのか全く新しい環境を作ったほうがよいのかはフレームワークを導入するに当たってよく検討しておいた方がいい事項ではあります。


また、既存の社内標準ライブラリを使う必要があるならその組み込みができるかとか、Laravelの話で言えばパッケージ管理にComposerを使う必要があったりするので、いやいやPEAR使い続けようぜみたいなイタタ・・・な反対が出たりすると採用が難しくなったりもするのでその辺はどう環境が変わっていくのかも調べて話し合っておいた方がよいかと思います。



まとめ


あんまり具体的なことをかけては無いのですが、こんなことを基準にさらに候補のフレームワークを絞っていったわけですが、最終的にはサンプルアプリを作ったりして実際に開発するメンバーの感触を掴んでもらってどれがいいか決めていくのが一番よい方法だとは思います。

どのフレームワークにもマニュアルにサンプルアプリの構築手順が掲載されていたりもしますので。


どのフレームワークにも良し悪しがありますし、選定基準から機械的点数化して候補を絞ってもよいでしょうけど、実際に導入するに当たっての障壁がそれぞれの環境やチーム体制、システム要件や顧客の要望などから限定されたりもしますからそれに見合ったフレームワークを選んでいくということも大事だとは思います。




デザイナーが描く理想のデザインとプログラマが危惧する現実

$
0
0

自分の場合、主に社内システム構築に携わることが多いんですけど、社内用サイトだからといってあんまり適当なデザインもどうかと思うのでデザイナーさんも含めて画面設計をしたりすることがあったりします。

その際に「こういう今風なデザインにしましょう!」というデザイナー側の意見に対して「いや、それだと対象のユーザー層がついてこれなさそうだし、このシステム要件に合わないのでもう少し現実的なデザインにしましょう」みたいな意見の対立が起きたりします。


そんな激しい意見の言い合いをするわけではないのですが、見た目を格好よく見せたり流行に乗せたデザインにしたいという意見と、実際にユーザーとの窓口をする保守・運用の観点から見た目はどうであれなるべく問合せがないわかりやすいデザインにしておきたいという意見があったりするわけです。



本当は作りたい理想の形


別にエンジニアが現実的な意見ばかりを口にするわけではありません。

本音を言えば、モダンで美しく無駄の無い洗練されていてキュッキュ動いて半透明な感じのデザインとかでシステムを作り上げたいと思ってたりはするんです。

ですが、どうしてもデザインありきになってくると実際にそれをずっと運用したときのことを考えてしまったりします。


理由はいくつかありますが、一番大きいのは現場で利用者と近い存在のエンジニアにとってはあれこれ言ってくるユーザーやそのトラブルの対処に時間をとられることを恐れてなるべく複雑なデザインを取り入れたくない思っています。

単に見てくれだけを気にするデザインはすべきではないのでしょうけど、実際には絵コンテしか書けませんと言ったデザイナーさんも多く、どうしても本人の理想のデザインを語ってきたりもします。


私はデザイナーではないのでその真意についてはよくわからない部分も多いのですが、エンジニア側の意見に話を戻せば、例えばそのサービスのユーザー層を考えたときに、幅広い年齢層の人が使っていたりしてモダンなインターフェースを使いこなすことが難しいだろうというのがあったりします。

社内システムの場合、面倒なことに年齢層と社内の権威というものが比例するケースがあったりして、どうしても上のレイヤーを無視できないケースもあったりします。


また、社内のIT環境の基準が決まっていたりしてモダンなデザインやUIをきちんとサポートできる最新のブラウザが利用できないといったケースもあります。

これも結局は社員のIT水準が一定ではないことの弊害だったりもするんですけど、ヘルプデスク業務の効率化のためにブラウザはIEのみとかになってたり、業務都合でブラウザやバージョンを固定されていたりするケースもあったりして、理想のデザインを実現することが技術的な観点で難しいケースもあったりします。


そのほかにも、以前に「プログラマから見たデザイン 」で書いたようにそのシステムの業務側の担当者がいて、その人の感覚でデザインが変えられてしまって本来の意図やそこに込められたメッセージが全て無駄となるようなこともあったりと、なんだかんだでそういう過去の経験から、折り合いを付けたいと現実的なデザイン案を言ってしまったりします。



現場が考えるデザイン


一方で、じゃあ現場で考えているデザインというものが果たしてベストなのかといったら全然そんなことも無くって、それはもうデザインと呼べるものではなく単にテキストでしかないといったものも多かったりします。

要するに問い合わせに跳ねるようなことがないように説明文をやたら入れたり、マニュアルをいたるところに添付したり、間違いを無くすためにやたらと長いナビゲーションページを用意したり、無駄に文字がでかかったり、目を引くようにとほとんどの文字が赤字だったりします。


元をただせばそういうものを解消するために必要なのがデザインなのに、現場ではその知識が無いためにできることといったら先ほど書いたようなテキストの羅列で回避するぐらいしかできないための苦肉の策が最悪な方向に向いたりするわけです。

ですが、結局ユーザーはそんな注釈を読んだりしません。

どんなに書いたところで無視されます。
現場も実はユーザーはそんなもんだと思っていたりして、注釈をやたら入れているのはクレームが入ったときに「その件はきちんと画面に書いてますよ?」って言うための予防線を引いているだけだったりします。


現場は、そもそもデザインがどうあるべきかって話はされないので理想像さえも描けません。

まぁ、デザインする手段を持ち合わせていないのでできることといったら書いたとおりテキストの羅列しかないので致し方ないのかもしれませんけど。

デザイナーが一方的に描く理想像をそのまま受け入れることも描いたように現場としては難しいことも多いですし、一方で現場が改善しようとしているものはデザインとは呼べないもので結果としてより酷いものになっていくことになります。

ですので、一度エンジニア側とデザイナー側とでそれぞれの理想像を語ることは必要だと思います。

そこから現実的な落しどころを探していけば言いだけですので、こういう意見の対立はあって叱るべきだとは思います。






Laravelのインストールと利用までの各種設定

$
0
0

Laravel 始めました。

ってことでまずはインストール編です。

Laravel5がリリースされていますが、今回はバージョン4.2のものをインストールしています。



Laravelをインストールする


LaravelをインストールするにはLaravelのインストーラを利用する方法と、composerを利用する方法の種類があるのですが、インスト ーラーを利用する方法はバージョンが指定できずに最新版がインストールされてしまうため、今回はcomposer経由でインストールする方法を書いています(一応インストーラーバージョンも後述してます)。

composerのインストールに関しては以前に書いた「[Composer] PHPのパッケージ管理にComposerを使う 」を参照してください。


Laravelをインストールしたい適当なディレクトリに移動し、下記のコマンドを実行するだけでインストールができます。


$ composer create-project laravel/laravel=4.2.* yourdir --prefer-dist

※ yourdirはLaravelのプロジェクトディレクトリ名で自由に決めれます。

※ 4.2はバージョンで指定しなかったら最新版がインストールされます。


Laravelはエラーメッセージなどを各言語にローカライズする仕組みがあり、ソース内のコメントなども含めて有志があらかじめ日本 語化しているパッケージも存在します。

これを利用したい場合は、下記のようにlaravel-jaのリポジトリを指定してインストールします。


$ composer create-project laravel-ja/laravel=4.2.* laravel --prefer-dist

どっちを利用しても、一応Laravel4.2の最新バージョンがインストールされます。

中身の違いはローカライズされたファイルやソース内のコメントが日本語化されているだけの違いになります。(確認した範囲では一部でごくごく小さな差分はあるようですが)


インストールしたLaravelのバージョンを確認したい場合はlaravel用ディレクトリ内で下記のようにコマンドを実行します。


$ cd laravel$ php artisan --versionLaravel Framework version 4.2.17

Laravelの最新バージョンを利用したい場合、Laravelインストーラを利用することもできます。

この場合、まずはcomposer経由でLaravelインストーラをインストールします。


$ composer global require "laravel/installer=~1.1"

あとは、インストールしたlaravelコマンドを利用して同様にlaravelのプロジェクトディレクトリを作成します。


$ /path/to/.composer/vendor/bin/laravel new yourdir

余談ですが、Laravelインストール時に下記のようにZipArchiveがないって怒られた場合は、PHPのconfigureオプションに--enable-zipを付けて再コンパイルしましょう(PHPのパッケージ版の場合はzlib-develあたりをインストールすればいいっぽい)。


Fatal error: Class 'ZipArchive' not found in /path/to/.composer/vendor/laravel/installer/src/NewCommand.php on line 110

マニュアル を見ると--with-libzipオプションが使えるよ!見たいな書き方をし ているんですけど、このオプションを付けてもZipArchiveは使えないみたいです。



Laravelなどの各種設定


基本的に、Laravel内のpublicディレクトリをWebサーバーのドキュメントルートに指定すればアクセスできるようになります。

Apacheの場合は下記。


<virtualhost />  DocumentRoot "/path/to/laravel/public"  ServerName www.example.com  ServerAdmin mail@example.com</virtualhost /><Directory "/path/to/laravel/public">    AllowOverride all</Directory>

後半の設定は、laravelディレクトリ内のpublicディレクトリにある,htaccessの設定を上書きできるようにするためのもので、この辺は元の設定がどうなっているかで判断してください。

後はApacheを再起動すれば利用できるんですけど、日本語環境で使う場合は幾つか設定を変えておいたほうが便利です。

この設定は、laravel/app/config/app.phpファイルにあるので下記のように2箇所編集しておきましょう。


'timezone' => 'Asia/Tokyo','locale' => 'ja',

localeを指定することで、laravel/app/lang/ja/の下にある各種メッセージファイルが読み込まれます。

日本語化されているLaravelパッケージを利用した場合は、あらかじめこの辺のローカライズされたファイルが配置されています。


もし、既にWebサーバーに別のフレームワークやアプリケーションが動いていて、Webサーバーのドキュメントルートを変更することが 難しいといった場合は、下記のようにAliasを用意することで回避することができます。

Apacheの場合はhttpd.confに下記の設定を追加します。


Alias /foo "/path/to/laravel/public"

次に、laravel/public/.htaccessを以下のように編集します。


<ifmodule />    <ifmodule />        Options -MultiViews    </ifmodule />    RewriteEngine On    RewriteBase /foo    # 最後がスラッシュのURLでアクセスされた場合のリダイレクト    RewriteRule ^(.*)/$ /foo/$1 [L,R=301]    # フロントコントローラーへの処理    RewriteCond %{REQUEST_FILENAME} !-d    RewriteCond %{REQUEST_FILENAME} !-f    RewriteRule ^ index.php [L]</ifmodule />

追加したのはRewriteBaseの設定で、そのすぐ下のRewriteRuleの設定は、スラッシュつきのURLにアクセスされた場合にリダイレクトする処理を合わせて/foo付きのURLにするように編集しています。

この設定により、www.example.com/foo/ のURLでLaravelにアクセスできるようになります。


これからもう少し学びながら記事をまとめていきたいと思います。




便利屋がいた方がプロジェクトは捗る

$
0
0

便利屋といっても要はオールラウンダー(今風で言うとフルスタック)な存在であったりするんですけど、プロジェクトはレイヤーが違う幾つものタスクの積み重ねで成り立っていますので、色んな場所で活躍できる人材がいてくれた方が何かと助かります。

ただ、実際問題そのような人材というのはなかなかいませんし、いたとしてもうまく機能しないケースが多かったりします。



タスクの隙間を誰が埋めるのか


プロジェクトは無数のタスクに分割され、それぞれの担当が決められ対応していきます。

一見、役割分担をうまくしているように見えても実際にはタスクの隙間というのが存在してしまいます。

箱(プロジェクト)の中にボール(タスク)を詰めていっても隙間ができるのと同じで、綺麗に詰め込めたつもりでも隙間だらけというのはよくある話です。


この隙間に結構落とし穴があって、その細かなタスクを担当する人が決められておらず宙に浮いてたり、「それそっちの担当と思ってたよ」とかで押し付け合いになったり、いつか誰かやるだろうと後回しにした結果、タスクをつなぎ合わせるところでうまくいかないといった大きな課題が直前で露出したりします。

こういうのを埋めるための幅広い知識を持っている便利屋となる人の存在ってかなり重要なわけで、細かいところに気が付いてカバーしてくれたり、担当者間のごたごたに仲介してうまく取り持ってくれたり、作業を効率化するためのツールの提供や調査などを行ってくれたりします。


システム開発の現場で言えば、モジュール間の仕様のずれに気が付いて修正パッチを作ったり、原因がよくわからないけど取りあえず回避でほっとかれたエラーの調査を行ったりとか、もはや誰も何も考えずに実行しているコマンドを理解して自動化するとか、誰も書こうとしないテストコードを書くとか、運用チームへの依頼や調整とか、煩い担当者との要求レベルとスケジュールの折り合いをつける交渉とか、枚挙にいとまがなかったりするんですけど、「そのタスクはこのチームが担当ね」と仕切ったところでプロジェクト推進上でこうした課題や新しいタスクというのはどんどん出てきます。


プロジェクトの中に隙間ができるは単にタスクの洗い出しに問題があったというだけでなく、どうしても担当者間で認識のずれが出ていたり、どっちがやるのか曖昧な領域があったり、担当チームの負担を増やしたくないために押し付け合いになった結果であったりと理由は様々あるんですけど、何れにせよそういう隙間というのは必ず発生してしまいますし、誰かが手を上げて対応しないと延々と放置されたりしてプロジェクトの後半になる頃には手が付けられないような状況になったりします。


要はそういう隙間を埋めることができる人材はどんなプロジェクトでも必要が出てきますし、出番が必ず回ってくるものだったりします。



誰が便利屋を買って出るのか


便利屋といえば都合がいい感じがして、実際プロジェクトマネージャーとかからもそういう目で見られている人も多くいたりはするんですけど、こういうポジションに選ばれるのはある程度の経験があって特定の専門分野に長けた人に押し付けられるのが常だったりもします。


何故こういった隙間を埋めるポジションや専門チームを作らないかは、(もちろん要員不足というものもありますが)プロジェクトリーダーなどが積み崩したタスクに網羅性と妥当性があると判断しているからで、担当者をきちんと決めて進捗を出せばうまく運営できると思っている部分はあるんですけど、どんなに細かくタスクを分割していったとしても先に書いたように隙間を100%埋めるようなことは無理なので便利屋に代打を必ずコールしなければならないの状況が生まれます。


また、最初から経験があって専門知識が豊富なキーとなるエンジニアを特定のポジションに付かせずに隙間を埋めるための便利屋として後方支援に回すようなことはプロジェクト運営的には誰もやらせたくないでしょうから、通常はシステム開発のリーダーなり現場の最前線に立たせる事となります

こうなってくると、自分の持ち場があるために他のレイヤーにまで気を回すことは難しくなりますし、手を出してしまうと自分の首が回らなくなってプロジェクトに悪影響を及ぼすことになったりします。

実際、こういった便利屋となるキーパーソンが色んなタスクに首を突っ込まれたり、担当外のその他の炎上案件の火消し役として参画させられることも多かったりして、負担がかなり大きくなることで別の課題が出てくることも多々あります。


ということで、プロジェクト運営的には様々なタスクで浮き彫りとなる課題を解消するための選任チームを置くこともなく、問題が浮き彫りになってからの付け焼刃的に今ある誰かをそれにあてがうようなことが繰り返されています。

個人的には、こういう便利屋の存在って選任がいないならプロジェクトリーダーやマネージャがやればいいのではと思ってたりはしますが。

進捗管理している関係でタスクの全体感が見えていますし、そもそも自分が洗い出したタスクの中に問題があったりもしているわけですし、各担当チームへ横断的に指示をできる権限をもってたりもしますから。

そのためにも当然、各専門チームに対応できるスキルや知識は当然必要となってくるわけですけどね。





PHPコードキャッシュにOPcacheを使ってみる

$
0
0

コードキャッシュといえばAPC だったんですけど、どうも最近開発がされていないっぽいので代用を探していたんですが、PHP5.5.0以上であればOPcache がデフォルトでバンドルされているのでこれを使ってみたいと思います。



OPcacheをインストールする


今回の環境では、PHP5.6.8/CentOS6.6にてOPcacheを使ってみます。

PHP5.4以前のバージョンではPECL経由でインストールできるみたいですが、詳細はマニュアル を参照してみてください。

インストールはPHPをコンパイルする際に--enable-opcacheオプションを付けるだけで利用できるようになります。

$ ./configure --enable-opache-- その他のオプション --

make、make installまで完了するとopcache.soファイルが作成されるのでパスを確認しておきます。

例えば、下記のようなパスに作成されます。


/path/to/php/extensions/no-debug-non-zts-20131226/opcache.so

あとは、php.iniを編集してOPcacheをロード、有効化すれば使えます。


[opcache]zend_extension=/path/to/php/extensions/no-debug-non-zts-20131226/opcache.soopcache.enable=1opcache.memory_consumption=128opcache.interned_strings_buffer=8opcache.max_accelerated_files=4000opcache.revalidate_freq=60opcache.fast_shutdown=1opcache.enable_cli=1

ちなみに、マニュアル上ではXdebugと同時に使っている場合は、(理由はよくわかりませんけど)OPcacheのほうを先にロードしろとあるので、この設定はXdebugの設定より設定ファイル上先に書いておいたほうが良さそうです。


この設定は、マニュアル上で推奨している値そのままです。

その他にも幾つも設定項目があるので、詳細は実行時設定のマニュアル を参照。

opcache.save_commentsとか有効にしたほうが良さそうなのにコメントに依存するアプリケーションを破壊するかもしれないとか怖いこと書いていて、実際にそんなことしているソースあるんだとか思ったり(独り言)。


まぁ、設定変更するとしてもOPcacheが使うメモリ量(opcache.memory_consumption)やファイルのタイムスタンプを確認する頻度(opcache.revalidate_freq)を変えるぐらいでしょうか。

opcache.revalidate_freqに関しては、プログラムをリリースしても反映までにこの秒数またされるっぽいので、OPcacheが原因でプログラムが即時に反映されないといった記事をよくみます。

また、キャッシュをさせたくない場合は、opcache.enable=0にすればすぐに無効化されます(要Apache再起動)。


CLI上でもOPcacheを有効にしている関係で、下記のように利用できるかどうか簡単に確認できます。


# php - vPHP 5.6.8 (cli) (built: Jun 10 2015 09:50:05) Copyright (c) 1997-2015 The PHP GroupZend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2015, by Zend Technologies    with Xdebug v2.3.2, Copyright (c) 2002-2015, by Derick Rethans

もちろんphpinfo()からでも。


ZendOPcache-1



OPcacheのキャッシュ状況を確認する


APCの場合、キャッシュ状況やメモリ使用量などをグラフ化してくれるプログラムが付属していますがOPcacheではありません。

ただ、GitHub上にそれっぽいことをしてくれるソースが公開されています。


https://gist.github.com/ck-on/4959032


このプログラムを適当な場所に設置してアクセスしてみるとOPcacheのキャッシュやメモリ使用状況が確認できます。


ZendOPcache-2


で、実際のところの効果はということでLaravelのプログラムに対してApacheBenchでアクセスしてテストしてみます。

まずは、OPcacheを無効にした場合


$ ab -n 100 -c 100 http://localhost/fooThis is ApacheBench, Version 2.3 <$Revision: 655654 $>Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/Licensed to The Apache Software Foundation, http://www.apache.org/Benchmarking localhost (be patient).....doneServer Software:        Apache/2.2.15Server Hostname:        localhostServer Port:            80Document Path:          /fooDocument Length:        2617 bytesConcurrency Level:      100Time taken for tests:   11.095 secondsComplete requests:      100Failed requests:        0Write errors:           0Total transferred:      288300 bytesHTML transferred:       261700 bytesRequests per second:    9.01 [#/sec] (mean)Time per request:       11095.334 [ms] (mean)Time per request:       110.953 [ms] (mean, across all concurrent requests)Transfer rate:          25.37 [Kbytes/sec] receivedConnection Times (ms)              min  mean[+/-sd] median   maxConnect:        4   24   5.5     24      49Processing:   904 7264 3788.2   8550   11062Waiting:      896 7252 3783.8   8472   11061Total:        924 7288 3789.9   8574   11091

次に、OPcacheを有効にした場合


$ ab -n 100 -c 100 http://localhost/fooThis is ApacheBench, Version 2.3 <$Revision: 655654 $>Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/Licensed to The Apache Software Foundation, http://www.apache.org/Benchmarking localhost (be patient).....doneServer Software:        Apache/2.2.15Server Hostname:        localhostServer Port:            80Document Path:          /fooDocument Length:        2617 bytesConcurrency Level:      100Time taken for tests:   4.670 secondsComplete requests:      100Failed requests:        2   (Connect: 0, Receive: 0, Length: 2, Exceptions: 0)Write errors:           0Total transferred:      282534 bytesHTML transferred:       256466 bytesRequests per second:    21.41 [#/sec] (mean)Time per request:       4670.117 [ms] (mean)Time per request:       46.701 [ms] (mean, across all concurrent requests)Transfer rate:          59.08 [Kbytes/sec] receivedConnection Times (ms)              min  mean[+/-sd] median   maxConnect:        3   24   3.4     25      25Processing:   879 2815 1274.5   2877    4643Waiting:        0 2791 1306.5   2871    4643Total:        902 2838 1275.4   2902    4667

結果を見るとRequests per secondが9.01に対して有効にした場合は21.41に、Time per request(mean, across all concurrent requests)が、110.953に対して有効にした場合は46.701となっているので、大体倍ぐらいの性能は出るようになったっぽいです。


フレームワークとか多数のライブラリが読み込まれる場合は結構有効に働いてくれる気がします。

もちろん、より多くのメモリを食ったりキャッシュが利いてプログラムが更新されないといった弊害もあったりしますのでチューニングは必要になってくるかと思います。

でも、デフォルトでバンドルされているものなので使わない手は無いと思いますが。





開発チームが劇団ふたりだった頃の話

$
0
0

昔々は同僚を中心に数人のシステム開発・保守部隊が構成されていたんですが、次々と辞めていったり他のチームに異動したりで最終的には2人になってしまったことがありました。

しかも、もう一人というのが中途入社で経験が浅かったこともあって実質そのシステムの細部までわかるのが自分一人となり、設計やらの上流工程や調達・決裁などの事務処理まで細々とした仕事までをほぼ請け負わないといけない状況となってました。


こういう状況でも何とか仕事を回していたのですが、特に上の人から見ればどうにかなっているように見えてしまうんですけど、実際のところどうにもならない状況だったりします。



現状維持さえできない


こういう状況になってくると、新しいシステム開発プロジェクトを立ち上げるなんて以ての外で、取りあえず今あるシステムの細かい改修要望を対応したり、バグを潰していったりといった取りあえず現状を維持する日々となってきます。

ただ、実際には現状維持なんてこともできていないわけですよ。

システムは耐用年数があったりしますので次のリプレイスまでのカウントダウンが刻々と迫ってきますし、技術は進化するので今のものがどんどんレガシー化しますし、それに伴って保守・運用コストが増加していって日々の業務時間を圧迫してくることになりますし、次々と新しい脅威やセキュリティホールが見つかるといったリスクも増大していったりします。


結局システムも生き物みたいなもので、日々メンテナンスをしないとどんどん弱っていきますし、病気が見つかれば処置が必要です。

2人じゃただ単にその対応に追われるだけで、そのサービスのあるべき姿なんて描かれても進捗することもできません。

それでも、周りからすれば現状のシステムの課題やあるべき論を展開して日々改善要求を突きつけてくるわけで、妥協案を探ってできるレベルまで落としこんだ上で何とかつないでいく中で、周りにはそれを現状を維持していると思い込んでしまうわけです。


現状維持という観点では、やっている本人もそういう思考に偏ってきて、そもそも手一杯の状況では新たに仕事をとってくることも生み出すことも自分たちの首を絞めることになるわけですから、そういう行動に移りたがらなかったりします。

例え新しいことをやりたいと思っていても、無理だ諦めろという考えに染まっていくわけで、そんな状況ですから運営側にも活気が全くなくなるわけです。


実際に開発・保守要員が1人でも2人でも現行のサービスを継続させることはできるかもしれません。

が、実際には問題を先延ばしにしているだけで内部的なリスクというのはどんどん増大していったりしてます。



エンジニアを育てることなんてできない


もう一つは、そういった状況に陥るとエンジニアのスキルアップもできなくなるので、成長させるといったことも難しくなります。

今の業務をこなすことはうまくなるかもしれませんが、そのものすごく狭い世界と固定化されたスキルでは他の仕事をすることも難しいので企業内におけるエンジニアの価値も低下していくことになります。

一番怖いのは、毎日の仕事はあるわけでそれを単調にこなしていく日々に慣れてしまって思考停止に陥ることです。

小規模なチームで固定の保守・運用業務を長年やらせているとこういった人がどんどん増えてきてしまって、他のチームへの人員配置も難しいようなことなるのを結構見たりしてます。


開発メンバーが複数人いると、それぞれの得意分野持っているスキルやノウハウも異なるため、それを補完しあうことで互いを高められるということができたりもします。

高スキルの人の影に埋もれてしまったり、チーム内の権威的な階層によって積極的に情報共有やスキルアップを図ろうとしない人もいたりするので、チームの人数が多ければいいという話ではないのですが、それでも切磋琢磨し合える仲間がいるというのはモチベーションにつながったりもします


高度なスキルを持った優秀なエンジニア二人ということであれば、それぞれを高めあう努力を継続することはできるかもしれませんが、そういった人は稀ですし実際もう一人のほうはあまりその辺が積極的ではなかったこともあって自分もエンジニアとしてのモチベーションはかなり低下してたりしました。


最終的には、なんとか今の状況と面倒を見ているサービスの重要性を説いて体制を強化してもらい、その後も徐々に大きくすることができました。

組織の体制として余分な人員配置や遊びの要員を抱えているというのはよろしくないこととは思いますけど、少なくとも現状維持できるだけの体制というのは実際のところ退化しているに等しいので、成長させていきたいという展望があるのであれば、それに見合った体制作りというのは当然必要になってくるのではないかと思います。





Laravelで大容量のファイルをダウンロードさせる方法

$
0
0

ファイルダウンロード処理とかよくあるものですが、Laravel標準での実装をすると一部問題が出たりもしたので、その辺の対策などのまとめです。

環境はLaravel4.2です。



Laravel標準実装のお作法


Laravelでの標準的なファイルダウンロードの実装方法は、Response::download()を利用する方法です。


public function anyIndex(){    $path = "/path/to/download/file.doc";    $name = "ダウンロード.doc";    $mimeType = "application/msword";    $headers = array(        'Content-Type' => $mimeType,        'Content-Length' => filesize($path)    );    return Response::download($path, $name, $headers);}

ただ、マニュアルの注記 にあるように、ファイル名はASCIIにする必要があると書いてたりします。

実際に上記のようにダウンロードファイル名にマルチバイトを渡すと、バージョンによってはInternet Explorerでは文字化けが発生します(見た限り FireFoxやChromeだと問題ない)。


ファイル名をASCIIにする必要があるって注記があるのにASCII以外を渡しても問題ないケースがあるのが変ではあるんですけど根本的な原因は、ダウンロード時のレスポンスヘッダにある下記のヘッダがブラウザによってうまく解釈できないことにあるようです。

Content-Dispositionattachment; filename="ダウンロード.doc"; filename*=utf-8''%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89.doc

一応、Response::download()の処理の流れもざっと追ってみると


public static function download($file, $name = null, array $headers = array(), $disposition = 'attachment'){    $response = new BinaryFileResponse($file, 200, $headers, true, $disposition);    if ( ! is_null($name))    {        return $response->setContentDisposition($disposition, $name, str_replace('%', '', Str::ascii($name)));    }    return $response;}

にあるように、ファイル名を指定した場合はBinaryFileResponse/setContentDisposition()が呼び出されます。

その際に、


laravel/vendor/laravel/framework/src/Illuminate/Support/Str.php

のascii()を通っていて(実態としてはlaravel/vendor/patchwork/utf8/class/Patchwork/Utf8.phpのtoAscii()メソッド)、この際に ファイル名をUTF-8からASCIIに変換かけてそれ以外の文字は切り捨てる処理をしてたりします。

setContentDispositionの実態を見てみると


public function setContentDisposition($disposition, $filename = '', $filenameFallback = ''){    if ($filename === '') {        $filename = $this->file->getFilename();    }    $dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback);    $this->headers->set('Content-Disposition', $dispositionHeader);    return $this;}

となっていて、第3引数で渡したファイル名は実際にはfallback filenameとあるので代替のファイル名のようです。

さらに実際にDispositionヘッダを生成しているmakeDisposition()を追うと


if ($filename !== $filenameFallback) {    $output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));}

のようにあるので、ここで実際のマルチバイトのファイル名と、ASCII変換によって切り捨てられた代替ファイル名が一致しないため、最初に書いたようなヘッダが作成されるようです。

何れにせよこのヘッダはRFC6266でちゃんと定義されているそうなので、古いブラウザ側がクソ仕様で動いているせいっぽいです。


PHPでダウンロードさせるファイル名がIEで文字化けする件 @Qiita


一応、その他の方法でもLaravelでファイルダウンロードを実装する方法があります。

それは、Response::make()を利用する方法です。


public function anyIndex(){    // ダウンロード対象ファイル    $path = "/path/to/download/file.doc";    $name = "ダウンロード.doc";    $mimeType = "application/msword";    // レスポンスヘッダ    $header = array(        'Content-Type' => $mimeType,        'Content-Length' => filesize($path),        'Content-Disposition' => attachment; filename=\"{$name}\""    );    $handle = fopen($path, 'r');    $contents = "";    while (!feof($handle)){        $contents .= fread($handle, 4096);    }    fclose($handle);    return Response::make($contents, 200, $header);}

この方法は、Dispositionヘッダを自分で生成する分、ダウンロードファイル名が文字化けするといったことは回避できるのですが、これはこれで問題があるようで、Response::make()の場合はファイルの中身を引数に渡しているんですが、呼び出した際に通るsetContent()の中で


public function setContent($content){    if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) {        throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content)));    }    $this->content = (string) $content;    return $this;}

データ自体をキャストしてコピーしているため、倍のメモリを食ったりします。

ですので、かなり大容量のファイルをダウンロードさせようとした場合に、そもそもその中身を取り出すのにメモリを消費し、さらに コピーされることでメモリを食ってといったことでメモリ枯渇のエラーとかを起こす可能性があります(実際に起きました)。


大容量のファイルをダウンロードさせる


ってことで話がかなり長くなりましたが、結論としては(元も子もないですけど)Laravelの作法を無視してPHP標準のやり方で実装しました。


// ダウンロード対象ファイル$path = "/path/to/download/file.doc";$name = "ダウンロード.doc";$mimeType = "application/msword";header("Content-Length: " . filesize($path));header("Content-Disposition: attachment; filename=\"{$fileName}\"");header("Accept-Ranges: bytes");header("Content-type: {$mimeType}");// 実ファイル呼び出し$handle = fopen($path, 'rb');while (!feof($handle)){    echo fread($handle, 4096);    ob_flush();    flush();}fclose($handle);

4096Byteごとにflush()するので、メモリを大量に消費するということもありません。

まぁ、フレームワークを使う以上はその作法に則るのがベストだとは思いますが、ここで書いた諸々の事情により、一部のブラウザに配慮するのであれば今のところはこの ようにするのが良さそうな気がします。





Viewing all 287 articles
Browse latest View live