追記 2014-12-19
Ray.Di, Ray.Aopを参考に、より簡単なinterfaceのDI/AOPライブラリを実装しました。お使ひください。
cf. PHPで簡単に華麗にDIとAOPをキメる http://c4se.hatenablog.com/entry/2014/12/11/013136
PHP Advent Calendar 2013 19日目です。みなさまに於かれましてはネコと和解されましたでせうか。昨日はPHP 文字列リテラルにおける変数展開ノ全テ - do_akiの徒然想記でした。うげ、複雑な構文2って然ふだったのか。securityとしては洞うなの……。
PHPの開発にGrunt使へ、と云ふ話しを予定してゐたのですが、ADVENTARの方のPHP Advent Calendar 2013でPHP 開発でも Grunt を使う - ngの日記に書かれてしまひました。因みにわたしのGruntfile.jsは、
'use strict'; module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), exec: { fixer: { command: 'php php-cs-fixer.phar fix lib/ --level=all', stdout: true, stderr: true }, lint: { command: 'find *.php -exec php -l {} \\; && ' + 'find templates/ -name \\*.php -exec php -l {} \\; && ' + 'find lib/ -name \\*.php -exec php -l {} \\;', stdout: true, stderr: true } }, jshint: { options: {jshintrc: '.jshintrc'}, all: [ 'Gruntfile.js' ] } }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-exec'); grunt.registerTask('test', ['jshint', 'exec:lint', 'exec:fixer']); };
です。lintとfixerをgrunt-execで回してゐます。Windowsではfind commandが動きません。
と云ふかどうせbowerとか使ふのだから、node.jsは必須です。Gruntを使ふと便利です。わたしは平気でGruntfile.jsからrakeを呼んだりしますが。
PHPの闇
いきあたりバタリ感のすごいPHPの仕様 (特に標準関数) ですが、最近はだいぶんPHPの闇も晴れてきました。しかしPHP教育の闇は相変わらずです。
PSR (Proposing a Standards Recommendation)は闇を晴らす光です。PSRの無いPHPを教えるな。Composer http://getcomposer.org https://packagist.org で依存関係の管理や適用が随分楽に成りました。PSR-0 (Autoloading Standard) と PSR-4 (Autoloader) に従いrequireをComposerに任せれば (ComposerはPSR-0 autoloaderも内蔵してゐ、localで使へます) 、よいclass構造を保つ助けに成ります。PSR-1 (Basic Coding Standard) と PSR-2 (Coding Style Guide) はcoding styleですから異論は有るでせうが、理由が無ければ採用すべきです (採用しない場合は理由を文書で記述するべきです)。PSR-3 (Logger Interface) が有るので便利なMonolog https://github.com/Seldaek/monolog を心配せずに使へます。Web siteにloggingは必須ですね?
此等はdefacto standardですが、技術に於いて標準はdefacto standard以外に有り得ません。標準に違反する場合、明示的に違反するべきです。殴り返して貰う為に殴るべきです。
Ray.DiとRay.Aopを触ってみる
さて、前述は前置きで、Ray.DiとRay.Aopを触ってみます。
cf. BEAR.Resource ≪ BEAR Blog http://www.bear-project.net/blog/2012/04/bear-resource/
cf. Object Framework - Ray.Di ≪ BEAR Blog http://www.bear-project.net/blog/2012/04/di/
cf. Object Framework - Ray.Aop ≪ BEAR Blog http://www.bear-project.net/blog/2012/04/object-framework-ray-aop/
cf. Ray.Di / Ray.Aop コトハジメ // Speaker Deck https://speakerdeck.com/madapaja/ray-dot-aop-kotohazime
cf. Ray.Di-Ray.AopからみるPHP5.4+フレームワークBEAR.Sunday // Speaker Deck https://speakerdeck.com/madapaja/ray-dot-di-ray-dot-aopkaramiruphp5-dot-4-plus-huremuwakubear-dot-sunday
BEAR.Sunday http://bearsunday.github.io/ のものですが、あわよくばせしめてやらう、と云ふ魂胆です。
Composerを落として、./composer.phar init
する。
{ "name": "vagrant/ray", "description": "Ray test.", "require": { "ray/di": "dev-develop", "ray/aop": "dev-develop" }, "license": "Public Domain", "authors": [ { "name": "Sachirou Inoue", "email": "utakata.c4se@gmail.com" } ] }
(因みに何かする時は直ぐVMを作ります。vagrant upで作ってvagrant destroyで消せば好い丈です。)
Ray.Di
Ruby使ってるとinstance_evalしたりしますが、PHPは存外静的で、DI (Dependency Injection) が有ると助かります。Pimpleも好く使ひますが、阿れはただのコンテナで、少し力不足な事があります。
cf. Pimple - A simple Dependency Injection Container for PHP 5.3 http://pimple.sensiolabs.org/
cf. koriym/Ray.Di https://github.com/koriym/Ray.Di
cf. PHP5.4+フレームワーク BEAR.Sundayを理解するためにRay.Diを触ってみるの巻 其の弐 : 今日も適当ダイアリー http://blog.madapaja.net/2012/07/php54-bearsundayraydi_11.html
Injectionだけする例。
<?php $loader = require __dir__ . '/vendor/autoload.php'; use Doctrine\Common\Annotations\AnnotationRegistry; use Ray\Di\Di\Inject; use Ray\Di\Di\Named; use Ray\Di\AbstractModule; use Ray\Di\Injector; class RModel { private $obj; /** * @Inject * @Named("obj=obj_sample") */ public function __construct(\RObj $obj) { $this->obj = $obj; } public function lookin() { var_dump($this->obj); } } class RObj { } class RModule extends AbstractModule { public function configure() { $obj = new RObj; $this->bind('RObj')->annotatedWith('obj_sample')->toInstance($obj); } } AnnotationRegistry::registerLoader([$loader, 'loadClass']); $injector = Injector::create([new RModule]); $model = $injector->getInstance('RModel'); $model->lookin();
RObjをRModelにinjectします。Injector::create([new RModule])
でRModuleのconfigureが実行され、$injector->getInstance('RModel')
でinjectします。
次はinterfaceでやるね……。
然ふ云へば此の引数名でinjectするやつ、AngularJSのinjectionで最近見たな……。AngularJSと言へばAngularDartも出ましたね。Dartって何が嬉しいの。
Ray.Aop
まあRubyはclass書き換えるの簡単ですし、今ならModule#prependもできましたから余りAOP (Aspect Oriented Programming) する事はありませんが、AspectRを一度だけ使った事が有ります。なんでだったかな……。まぁAspectRは、methodの上書きと余り変わらない事をやってゐるし (実際はもっとdirtyで文字列evalしてる)。
cf. Module: AspectR http://www.ruby-doc.org/gems/docs/a/aspectr-0.3.7/AspectR.html
cf. koriym/Ray.Aop https://github.com/koriym/Ray.Aop
Aspect追加のみする例。Ray.Diを使った。
<?php $loader = require __dir__ . '/vendor/autoload.php'; use Doctrine\Common\Annotations\AnnotationRegistry; use Ray\Di\AbstractModule; use Ray\Di\Injector; use Ray\Aop\MethodInterceptor; use Ray\Aop\MethodInvocation; class RModel { public function command() { echo "Method body.\n"; } } class RIntercepter implements MethodInterceptor { public function invoke(MethodInvocation $invocation) { echo "Aspect before.\n"; $result = $invocation->proceed(); echo "Aspect after.\n"; return $result; } } class RModule extends AbstractModule { public function configure() { $this->bindInterceptor( $this->matcher->any(), $this->matcher->any(), [new RIntercepter]); } } AnnotationRegistry::registerLoader([$loader, 'loadClass']); $injector = Injector::create([new RModule]); $model = $injector->getInstance('RModel'); $model->command();
RModuleで指定されたclassの全てのmethodにRIntercepterを被せる様指定し、$injector->getInstance('RModel')
でRModelに適用してゐる。
DIとAOPとの関係は、直感的には解ってゐない。余りに使い慣れないからだ。JavaScriptではSingletonは標準的な存在だからな……。
明日は @kokkekun さんです。