LoginSignup
3
3

More than 5 years have passed since last update.

Phalcon2のLoggerがPSR-3インタフェースを実装してくれない件への対応

Posted at

内容的には Phalcon\LoggerをPSR-3として扱う - Qiita の続編になります。

Phalcon\Logger は Phalcon 1.3 から PSR-3 Logger Interface に対応しているはずでしたが、Phalcon 2 にはこの機能が搭載されていません。

Phalcon 1.3.4 のディレクティブ

1.3 にあった phalcon.register_psr3_classes ディレクティブが…。

Phalcon 2.0.8 のディレクティブ

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でインストールします。

composer.json
{

    …中略…

    "require": {

        …中略…

        "psr/log": "~1.0"
    }
}

これでインストールすると、vendorディレクトリ以下に Psr\Log 名前空間以下のクラスやインタフェース、トレイトの定義されたファイルが配置されます。

Psr\Log\LoggerInterface.php
<?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 を利用するのが手っ取り早いです。

Acme\Phalcon\Logger.php
<?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 の場合はこうなりますね…。

Acme\Phalcon\Logger.php
<?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 / 可変長引数リスト の出番です。

Acme\Phalcon\Logger.php
<?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の恩恵ですね。

3
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3