简单轻量级的PHP依赖注入

1.0.4 2014-08-24 09:54 UTC

This package is not auto-updated.

Last update: 2024-09-24 01:43:15 UTC


README

主分支: 构建状态

简单轻量级的PHP依赖注入,支持参数覆盖等功能。

它受到了Guzzle 3中的ServiceBuilder的启发,但在此基础上进行了简化和扩展。

查看wiki

为什么选择squirt?

  • 提供了依赖注入的所有好处
  • 将配置与代码分离。与大多数DI框架不同,它们在配置时使用容器对象及其方法;Squirt的所有配置都是纯数据。这意味着它可以像数据一样进行操作/合并,并且配置中没有全局对象。此外,构造和配置服务实例的工厂代码与配置参数解耦,提供了更好的可测试性和代码重用性。
  • 保持代码DRY。服务配置可以相互扩展,以减少重复。依赖关系会自动按名称递归注入。
  • 支持三种参数覆盖注入模式
    • 服务配置可以相互扩展和覆盖,提供共享默认参数。如果需要,也可以使用子类覆盖实例化的类。
    • 配置文件可以相互包含和覆盖
      • 组织配置,分离相关服务
      • 使集成测试更容易:包含生产配置,只覆盖你需要的内容。
    • 最终用户代码可以在实例化时提供选择性覆盖,以帮助测试(非常适合快速调试标志)和故障排除。
  • 使单元测试更容易/可行。在单元测试时,可以将模拟对象注入实例。配置文件覆盖简化了集成测试。
  • 设计用于简单性。注入参数以自然的方式包括注入对象和注入配置值。只有一个方法要学习:$squirtServiceBuilder->get(),与大多数依赖注入容器中的所有方法相比。没有要学习的注释,也没有XML或YAML。
  • 设计用于性能。Squirt配置文件是用PHP编写的,因此opcache已经优化了它们。Squirt还支持在整个配置上的Doctrine缓存
  • 设计用于兼容性。如果你使用外部库(你应该这样做),编写一个包装类以添加squirt支持非常容易。Squirt兼容性所需的所有内容是一个接受参数数组(包括注入的依赖关系和配置值)并返回实例的静态factory()函数。

基本示例

app_config.php - squirt配置文件

return array(
    'services' => array(
        'LOGGER' => array(
            'class' => 'MyApp\Logger',
            'params' => array(
                'logFile' => '/var/log/app.log'
            )
        ),
        'GUZZLE_CLIENT' => array(
            'class' => 'MyApp\GuzzleClient'
        ),
        'APP' => array(
            'class' => 'MyApp\App',
            'params' => array(
                'logger' => '{LOGGER}',
                'client' => '{GUZZLE_CLIENT}',
                'url' => 'https://github.com'
            )
        )
    )
);

注意,这足以定义应用程序的连接方式。没有DI容器和新的方法要学习。注意,注入的配置值(如日志文件位置)与注入的服务一起自然表示。

MyApp/App.php - squirt兼容的最终用户类

namespace MyApp;

use Monlog\Logger;
use GuzzleHttp\Client;

class App
{
    private $logger;

    private $client;

    private $url;

    public static function factory(array $params=array())
    {
        return new static($params);
    }

    protected function __construct(array $params)
    {
        /*
         * Read in and validate all of our injected dependencies
         * Note that the Squirt\Common\SquirtUtil class contains helper functions
         * which can reduce the repetition below.
         */

        if (isset($params['logger']) && ($params['logger'] instanceof Logger)) {
            $this->logger = $params['logger'];
        } else {
            throw new \InvalidArgumentException('Invalid or missing logger');
        }

        if (isset($params['client']) && ($params['client'] instanceof Client)) {
            $this->client = $params['client'];
        } else {
            throw new \InvalidArgumentException('Invalid or missing client');
        }

        if (! empty($params['url'])) {
            $this->url = $params['url'];
        } else {
            throw new \InvalidArgumentException('Missing url');
        }
    }

    public function run()
    {
        $response = $this->client->get($this->url);

        $this->logger->info('Got result: ' . $response->getBody());
    }
}

* 注意:代码中没有配置,以确保适当的分离

MyApp/Logger.php - Monolog Logger 的 squirt 兼容包装器

namespace MyApp;

use Monolog\Logger as MonologLogger;
use Monolog\Handler\StreamHandler;

class Logger extends MonologLogger
{
    public static function factory(array $params=array())
    {
        $logFile = $params['logFile'];

        $instance = new static();
        $instance->pushHandler(new StreamHandler($logFile));

        return $instance;
    }
}

MyApp/GuzzleClient.php - Guzzle 4 客户端的 squirt 兼容包装器

namespace MyApp;

use GuzzleHttp\Client;

class GuzzleClient extends Client
{
    public static function factory(array $params=array())
    {
        return new static($params);
    }
}

run.php - 普通squirt服务消费脚本

use Squirt\ServiceBuilder\SquirtServiceBuilder;

require 'vendor/autoload.php'; // Composer class autoloader

$squirtServiceBuilder = SquirtServiceBuilder::factory(array(
    'fileName' => 'app_config.php'
));

// Note that only one service needs to be requested.  All required dependencies
// are lazily created and injected.
$app = $squirtServiceBuilder->get('APP');

$app->run();

run_nonsquirt.php - 这展示了 Squirt 在底层是如何工作的。

use MyApp\App;
use MyApp\Logger;
use MyApp\GuzzleClient;

require 'vendor/autoload.php'; // Composer class autoloader

$logger = Logger::factory(array(
    'logFile' => '/var/log/app.log'
));

$client = GuzzleClient::factory();

$app = App::factory(array(
    'logger' => $logger,
    'client' => $client,
    'url' => 'https://github.com'
));

$app->run();

安装

使用 composer 安装 squirt。创建一个名为 composer.json 的文件

{
    "require": {
        "phlogisticfugu/squirt": "~1.0"
    }
}

然后按照 composer 的安装说明进行。

特性

配置文件包含、服务扩展和覆盖

当在复杂的应用程序中使用 squirt 时,随着配置的服务越来越多,配置文件自然会变得更大。为了帮助组织这些文件,配置文件可以相互包含。

示例

return array(
    'includes' => array(
        'aws_config.php',
        'database_config.php',
        'production_logger_config.php'
    ),
    'services' => array(
        // service definitions which depend on services defined elsewhere
    )
);

Squirt 服务也可以相互扩展,以便以合理的方式重用配置并级联默认值。

示例

return array(
    'includes' => array(
        'production_logger_config.php'
    ),
    'services' => array(
        'ABSTRACT_HTTP_CLIENT' => array(
            'class' => 'MyApp\HttpClient',
            'params' => array(
                'logger' => '{LOGGER}',
                'http_options' => array(
                    'timeout' => 10
                )
            )
        ),
        'GITHUB_HTTP_CLIENT' => array(
            'extends' => 'ABSTRACT_HTTP_CLIENT',
            'params' => array(
                'url' => 'https://github.com'
            )
        ),
        'AMAZON_HTTP_CLIENT' => array(
            'extends' => 'ABSTRACT_HTTP_CLIENT',
            'params' => array(
                'url' => 'https://www.amazon.com',
                'http_options' => array(
                    // overrides value from ABSTRACT_HTTP_CLIENT
                    'timeout' => 60
                )
            )
        )
    )
);

* 注意:squirt 支持对配置参数的深度覆盖

最后,消费脚本可以使用所需的任何附加覆盖来覆盖配置中设置的任何内容,也许有助于一些调试或其他配置。

示例

$amazonHttpClient = $squirtServiceBuilder->get('AMAZON_HTTP_CLIENT', array(
    'http_options' => array(
        'timeout' => 90
    )
));

* 注意:通常,squirt 配置的服务会被缓存,它们的行为就像单例一样,防止不必要的实例化。然而,向 get() 方法提供覆盖参数将禁用该缓存。也可以通过 get($serviceName,null,false) 来禁用缓存。