定制 tholabs/creator 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

tholabs/creator

Composer 安装命令:

composer require tholabs/creator

包简介

creator is a simple PHP dependency injection that works with typehints

README 文档

README

creator is a simple PHP dependency injection that works with typehints and Reflection classes

Installation

via composer

$ composer require tholabs/creator

Testing

via phpunit

$ phpunit

Basic Usage

With class constant

<?php
    $creator = new \Creator\Creator;
    $myInstance = $creator->create(MyClass::class);

With recursive dependencies

assuming our MyClass looks like this:

<?php
    class MyClass {
        function __construct(AnotherClass $anotherClass) {
            $this->anotherClass = $anotherClass;
        }
    }

Creator will walk up the dependency tree and resolve any class which has no known instance yet.

PSR-11 Container usage

Creator supports the PSR-11 psr/container standard.

<?php

    $container = new Creator\Container(new Creator\Creator());
    $myInstance = $container->get(MyClass::class);

The $container->has() will return true if:

  • a primitive resource with given $identifier has been registered using $creator->registerPrimitiveResource()
  • a class resource with given $identifier has been registered using $creator->registerClassResource()
  • a class resource factory for $identifier has been registered using $creator->registerFactory()
  • $identifier is an interface or abstract class that can be fulfilled (because another instance implementing or inheriting has been registered earlier)

It will not return true if a given $identifier might be instantiable.

Injected Instances

Creator is able to use an independent resource registry for a single creation process.

<?php
    $anotherClass = new AnotherClass();
    
    $creator = new Creator\Creator;
    $myClass = $creator->createInjected(MyClass::class)->with($anotherClass)->create();
    
    if ($myClass->anotherClass === $anotherClass) {
        echo 'We are the same!';
    }

Any other dependency of MyClass is being resolved the usual way (i.e. looked up via the ResourceRegistry and, if there is no instance yet, it's being created with the same injected resources.) Creator collects dependency signatures and thus only re-creates instances that really require an injected dependency.

Invoke Closures / Callables

Creator is able to resolve parameters of closures and callables (invokables). It supports closures and method-context array callables with the object at index 0 (using the class name at index 0 is currently not supported and will cause Creator to throw an Unresolvable exception).

Injecting instances is supported as well.

<?php

    // Default invocation
    $creator->invoke(function(SimpleClass $simpleClass) {
        if ($simpleClass instanceof SimpleClass) {
            echo 'Everything works as expected.';
        }
    }
    
    // Injected invocation
    $simpleClassRoleModel = new SimpleClass();
    $creator->invokeInjected(function(SimpleClass $simpleClass) use($simpleClassRoleModel) {
        if ($simpleClass === $simpleClassRoleModel) {
            echo 'Injection is great';
        }
    })->with($simpleClassRoleModel)->invoke();

Factories

If you have resources that can not be created without additional logic, but also should only be created once another component depends them, you can register a factory for this factory. A factory can be a callable, an instance of Creator\Interfaces\Factory or a class string of a Factory (see lazy bound factories) and can be registered for any class resource, i.e. interfaces, abstracts or normal classes.

Global Factories

<?php

    $simpleClass = new SimpleClass();
    $factory = function() use ($simpleClass) {
        return new ExtendedClass($simpleClass);
    };
    
    $creator->registerFactory($factory, ExtendedClass::class);
    
    $extendedClass = $creator->create(ExtendedClass::class);
    if ($extendedClass->getSimpleClass() === $simpleClass) {
        echo 'Factories are awesome!';
    }

This comes in especially handy for stuff like database connections where you only want to create a connection if a component really depends on it.

Injected Factories

Of course, you can also register a factory as injection:

<?php
    $simpleClass = new SimpleClass();
    $factory = function() use ($simpleClass) {
        return new ExtendedClass($simpleClass);
    };
    
    $extendedClass = $creator->createInjected(ExtendedClass:class)
        ->withFactory($factory, ExtendedClass::class)
        ->create();
    
    if ($extendedClass->getSimpleClass() === $simpleClass) {
        echo 'Factories are awesome!';
    }

Injected factories overrule globally registered factories and even globally registered resources. However, they do not overrule injected resources. (Creation order routine is: Injected Instance -> Injected Factory -> Global Instance -> Global Factory -> Create Instance)

Self Factories

To avoid the need of a global factory, classes can also implement the Creator\Interfaces\SelfFactory interface. All classes implementing this interface will not be built using their constructor; instead, they have to return a factory closure:

class MyDependency implements Creator\Interfaces\SelfFactory {

    static function createSelf () : callable {
        return function(AnotherDependency $a) : MyDependency {
            return new static($a->getFoo());
        }
    }
    
    function __construct (Foo $foo) {
        $this->foo = $foo;
    }
    
}

It is worth nothing here that not returning an instance of the class will throw an InvalidFactoryResult exception.

Lazy Bound Factories

If you have factories that should not be created until they are required, you can register a lazy factory by using it's class name:

<?php

    $creator->registerFactory(SimpleFactory::class, SimpleClass::class);
    
    // Creates a SimpleClass with SimpleFactory
    $simpleClass = $creator->create(SimpleClass::class);

All lazy bound factories are stored to and read from the ResourceRegistry that defined them:

<?php

    class SimpleFactory implements Factory {
        
        function __construct(SimpleClass $simpleClass) {
            $this->simpleClass = $simpleClass;
        }
        
        function createInstance() {
            return $this->simpleClass;
        }
        
    }

    $creator->registerFactory(SimpleFactory::class, SimpleClass::class);
    
    $simpleClass = new SimpleClass();
    $simpleFactory = new SimpleFactory($simpleClass);
    
    $creator->registerClassResource($simpleFactory);
    
    $generatedSimpleClass = $creator->create(SimpleClass::class);
    if ($simpleClass === $generatedSimpleClass) {
        echo 'Congratulations, this example is completely useless and works!';
    }

Factory Result Caching

All factory results are registered to their corresponding ResourceRegistry, i.e. a injected factory will store it's result to the injected registry and thereby make it's created resource available during this creation process only. The only exception is a lazy bound factory with an injected dependency; in that case, the result of the factory is cached in the injection registry.

<?php

    class ArbitraryFactory implements Factory {
        
        function __construct(SimpleClass $simpleClass) {
            $this->simpleClass = $simpleClass;
        }
        
        function createInstance() {
            return $this->simpleClass;
        }
        
    }

    $creator->registerFactory(ArbitraryFactory::class, ArbitraryClassWithSimpleClassDependency::class);
    
    $injectedArbitraryClass = $creator->createInjected(ArbitraryClassWithSimpleClassDependency::class)
        ->with(new SimpleClass())
        ->create();
    
    $anyArbitraryClass = $creator->create(ArbitraryClassWithSimpleClassDependency::class);

In the example above, the instances of ArbitraryClassWithSimpleClassDependency will not be the same. Creator detects that SimpleClass is a dependency of the registered factory and therefore create a new instance of ArbitraryFactory with the injected SimpleClass. This new factory instance is stored to the injected registry and will not affect other creations.

Uninstantiable Classes

Singletons

Singletons can be resolved if they implement the Creator\Interfaces\Singleton interface.

Abstracts, Interfaces

If Creator stumbles upon an interface or an abstract class, it will try to look up the resource registry if any resource implements the interface / abstract class. First one is being served.

Registering Resources

Classes

If you want creator to use a certain instance of a class, you can register any object to Creator. It will then use this instance for any upcoming creation - a more "persistent" injection.

<?php
    $a = new stdClass;
    $a->foo = 'bar';
    
    $creator = new Creator\Creator;
    $creator->registerClassResource($a);
    
    $instance = $creator->create('stdClass'); // you should not use hardcoded strings as class names; always prefer the class constant
    echo $instance->foo; // bar

The optional second parameter $classResourceKey of the method registerClassResource bypasses a get_class determination of the object. This might break code completion and type hinting, so use it wisely.

Primitive (scalar) Resources

Creator supports registering scalar values by variable name.

<?php

    class A {
        function __construct($foo) {
            echo $foo;
        }
    }
    
    $creator = new Creator\Creator;
    $creator->createInjected(A::class)
        ->with('bar', 'foo') // first value is the injection, second the resource key
        ->create();

In previous versions of Creator, there was a method to register primitive resources in the global registry. This has been removed as it might cause unexpected behaviour and hinder future development.

However, if you really need it (but don't say nobody told you it's a bad idea), you can still achieve this by registering the scalar value to a ResourceRegistry and pass this registry while constructing your Creator\Creator instance. See the tests for example code.

Primitive resource specifics

  • If an argument has a default value and Creator can not find a matching scalar value, it will use the default value.
  • Registering an object with Creation::with() will always result in a class resource registration, i.e. registering $creation->with($myInstance, 'foo'); will only register $myInstance as class foo, but never as primitive resource.

Exceptions

All exceptions derive from Creator\Exceptions\CreatorException. Use this class in your catch block to catch all Creator-related exceptions.

Additionally, there are more specific exceptions:

  • If Creator is unable to resolve a dependency, it will throw Creator\Exceptions\Unresolvable.
  • If you are registering a factory which is not a callable or an instance of / a class name of a class that implements Creator\Interfaces\Factory, it will throw Creator\Exceptions\InvalidFactory.
  • If a self-factory returns a class which is not an instance of self, it will throw Creator\Exceptions\InvalidFactoryResult.

tholabs/creator 适用场景与选型建议

tholabs/creator 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 39.95k 次下载、GitHub Stars 达 5, 最近一次更新时间为 2015 年 12 月 23 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 tholabs/creator 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 tholabs/creator 我们能提供哪些服务?
定制开发 / 二次开发

基于 tholabs/creator 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 39.95k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 6
  • 点击次数: 3
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 5
  • Watchers: 1
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2015-12-23