内容的には Phalcon\LoggerをPSR-3として扱う - Qiita の続編になります。
Phalcon\Logger は Phalcon 1.3 から PSR-3 Logger Interface に対応しているはずでしたが、Phalcon 2 にはこの機能が搭載されていません。
1.3 にあった phalcon.register_psr3_classes
ディレクティブが…。
2.0 には存在しない!?
現状 \Psr\Log\LoggerInterface
に依存したクラスが動いているサイトで、何も考えずに Phalcon 1.3.4 → Phalcon 2.0.8 に入れ替えてみたところ、当然こんな感じのエラーが発生しました。
'Argument 1 passed to Acme\Domain\Service\AbstractService::setLogger() must be an instance of Psr\Log\LoggerInterface, instance of Phalcon\Logger\Adapter\File given, called in...
Phalcon2がリリースされてから随分と経ちますが、何も問題視されていないということは…。
PSR-3を実装したクラスを別途用意してエラーを回避する
取り急ぎの対策として、PSR-3インタフェースを実装したクラスを作成し、これに代えることにします。
まず、PSR-3インタフェースが定義されたファイルをComposerでインストールします。
{
…中略…
"require": {
…中略…
"psr/log": "~1.0"
}
}
これでインストールすると、vendorディレクトリ以下に Psr\Log 名前空間以下のクラスやインタフェース、トレイトの定義されたファイルが配置されます。
<?php
namespace Psr\Log;
/**
* Describes a logger instance
*
* The message MUST be a string or object implementing __toString().
*
* The message MAY contain placeholders in the form: {foo} where foo
* will be replaced by the context data in key "foo".
*
* The context array can contain arbitrary data, the only assumption that
* can be made by implementors is that if an Exception instance is given
* to produce a stack trace, it MUST be in a key named "exception".
*
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
* for the full interface specification.
*/
interface LoggerInterface
{
/**
* System is unusable.
*
* @param string $message
* @param array $context
* @return null
*/
public function emergency($message, array $context = array());
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
* @return null
*/
public function alert($message, array $context = array());
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
* @return null
*/
public function critical($message, array $context = array());
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
* @return null
*/
public function error($message, array $context = array());
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
* @return null
*/
public function warning($message, array $context = array());
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
* @return null
*/
public function notice($message, array $context = array());
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
* @return null
*/
public function info($message, array $context = array());
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
* @return null
*/
public function debug($message, array $context = array());
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
* @return null
*/
public function log($level, $message, array $context = array());
}
こいつを実装したクラスを作成すれば良いんですが、PHP 5.4 以上であれば LoggerTrait を利用するのが手っ取り早いです。
<?php
/**
* Phalcon フレームワーク用ライブラリ
*
* @copyright k-holy <k.holy74@gmail.com>
* @license The MIT License (MIT)
*/
namespace Acme\Phalcon;
/**
* Logger
*
* @author k.holy74@gmail.com
*/
class Logger implements \Psr\Log\LoggerInterface
{
use \Psr\Log\LoggerTrait;
/**
* @var Phalcon\Logger\AdapterInterface
*/
private $adapter;
public function __construct(\Phalcon\Logger\AdapterInterface $adapter)
{
$this->adapter = $adapter;
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
* @return null
*/
public function log($level, $message, array $context = [])
{
$this->adapter->log($level, $message, $context);
}
}
基本はこれでOKですが、このままでは Phalcon\Logger\Adapter\File
独自のメソッドを利用できなくなってしまいますので、その対応も入れます。
PHP 5.4 or 5.5 の場合はこうなりますね…。
<?php
…中略…
/**
* __call
*
* @param string
* @param array
*/
public function __call($method, $args)
{
if (method_exists($this->adapter, $method)) {
return call_user_func_array([$this->adapter, $method], $args);
}
throw new \BadMethodCallException(
sprintf('Undefined Method "%s" called.', $method)
);
}
PHP 5.6 以上なら Argument Unpacking / 可変長引数リスト の出番です。
<?php
…中略…
/**
* __call
*
* @param string
* @param array
*/
public function __call($method, $args)
{
if (method_exists($this->adapter, $method)) {
return $this->adapter->{$method}(...$args);
}
throw new \BadMethodCallException(
sprintf('Undefined Method "%s" called.', $method)
);
}
あとはDIでの生成部分を書き換えます。
$di->setShared('logger', function() {
return new \Phalcon\Logger\Adapter\File('/path/to/log/file');
});
$di->setShared('logger', function() {
return new \Acme\Phalcon\Logger(
new \Phalcon\Logger\Adapter\File('/path/to/log/file')
);
});
これでエラーが消えました。こんな時にコード修正が少なくて済むのもDIの恩恵ですね。