PHP Composer,用于包含带命名空间的建议模块的良好设计模式

时间:2013-03-03 01:30:27

标签: php namespaces composer-php

我正在编写一些代码(一个数据库抽象层,用于我的其他代码模块),我希望将其作为独立模块发布,可以包含在具有composer的项目中。我想在作曲家定义中包含一些可以提高模块性能的建议模块,但这并不是必需的。

我遇到的问题是如何以一种完全可怕的方式做到这一点。

因此,在此特定示例中,模块在namespace Intahwebz\DB;中声明,然后尝试包含可选模块Intahwebz\Log\Log,然后尝试使用可选模块Monolog。< / p>

到目前为止我所拥有的是模块代码ConnectionWrapper.php

namespace Intahwebz\DB;

use Intahwebz\Log\Log;


if(trait_exists("Intahwebz\Log\Log") == false){
    require_once("Log.php");
}


class ConnectionWrapper{

    use Log;

    function __construct(){
        $this->initLog();

        // Rest of constructor code.

        $this->log->trace("ConnectionWrapper has been constructed.");
    }   

    // Lots of other functions here.
}


?>  

然后在Log.php中我检查Monolog是否可用,如果包含它,否则定义一个非常轻量级的记录器。

<?php

namespace Intahwebz\Log;

if (class_exists('Monolog\Logger') &&
    class_exists('Monolog\Handler\StreamHandler')) {

require_once "MonologWrapper.php";

}
else{

    class Logger{
        public function debug($message, array $context = array()){
            echo $message."\n";
        }
        public function log($level, $message, array $context = array()){
            echo $message."\n";
        }
        public function info($message, array $context = array()){
            echo $message."\n";
        }
        public function notice($message, array $context = array()){
            echo $message."\n";
        }
        public function warning($message, array $context = array()){
            echo $message."\n";
        }
        public function error($message, array $context = array()){
            echo $message."\n";
        }
        public function critical($message, array $context = array()){
            echo $message."\n";
        }
        public function alert($message, array $context = array()){
            echo $message."\n";
        }
        public function emergency($message, array $context = array()){
            echo $message."\n";
        }
    }

    trait Log{

        var $log;
        function initLog(){
            $this->log = new Logger(__CLASS__);
        }
    }
}

如果Monolog可用,我们通过包含MonologWrapper.php

来使用它
<?php

namespace Intahwebz\Log;

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

trait Log{

    var $log;

    function    initLog(){
        $this->log = new Logger(__CLASS__);
        //Todo - get log handler from config file automagically.
        $this->log->pushHandler(new StreamHandler(PATH_TO_ROOT.'var/log/Admin.log', Logger::WARNING));
    }
}


?>

问题是:

1)它非常难看,并且每个建议的模块需要额外的文件。

2)它不允许人们在不重写代码的情况下切换monolog以外的其他记录器。

3)它有重复的类/特征定义,仅由if语句分隔,这完全混淆了我的IDE。

我知道Symfony等人解决这个问题的方法是通过提供服务层。但是我无法看到如何使用该设计模式而不强制依赖包含比它们应该引入可选模块要复杂得多。

任何人都可以描述一个体面的设计模式,包括可选模块,或用其他兼容模块替换它们吗?或者这是一种只能在实际框架内很好地定义的东西?

1 个答案:

答案 0 :(得分:4)

依赖注入:)

例如,而不是

function    initLog(){
    $this->log = new Logger(__CLASS__);
    // ..
}

使用

function setLogger(Logger $logger) {
    $this->log = $logger;
    // ...
}

等等。这些硬编码依赖项太多了,我不会提及所有人。

class MonologLoggingConnectionWrapperWrapper extends ConnectionWrapper {
    public function __construct (ConnectionWrapper $wrapper, Logger $logger) {
        // ..
    }
}

现在只需要Monolog,当有人试图实现这个类,然后很明显,缺少某些东西。如果您愿意,可以在类定义之前向文件添加测试,这应该(在这种情况下)抛出异常,或触发错误。

(类名由以下事实产生:ConnectionWrapper实际上不是包装器,而是它自己的类:包装器必须扩展包装类,或实现相应的接口,因此它可以干净地交换)

值得一提的是:避免使用条件类或函数定义:)一个类名,一个类(不多),当我尝试实例化一个类时,它是存在还是不存在,但不是“有时”。 / p>