Git Product home page Git Product logo

dice's People

Contributors

alessandro-morelli avatar benparizek avatar daniel-meister avatar kenjis avatar lkrms avatar maxwilms avatar pixelfck avatar solleer avatar thisispiers avatar trpb avatar veeenex avatar wyrihaximus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dice's Issues

Dice managed DI results in server error because of loops

If I try to pass the instance of the same classes into constructors, I face an error (PHP: Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 32 bytes) in .... )

Example:

Class Session:

class Session
  {
    private $config;
    private $cookie;
    private $db;
    private $page;
    private $statistics;
    private $filter;
    private $server;
    function __construct( Config $config, Cookie $cookie, Database $db, Language $language, Page $page, Statistics $statistics, Filter $filter, Server $server )
      {
        $this->config = $config->param;
        $this->cookie = $cookie;
        $this->db = $db;
        $this->page = $page;
        $this->statistics = $statistics;
        $this->filter = $filter;
        $this->server = $server;
      }

//.....

  }

Class Language:

class Language
  {
    private $config;
    private $db;
    private $filter;
    private $page;
    private $server;
    private $session;
    private static $language;
    private static $session_language;
    function __construct( Config $config, Database $db, Filter $filter, Page $page, Server $server, Session $session )
      {
        $this->config = $config->param;
        $this->db = $db;
        $this->filter = $filter;
        $this->page = $page;
        $this->server = $server;
        $this->session = $session;
      }

//.....

  }

It happens when instances of Language has Session and Session has Language, will finally result in fatal error. I think it falls into the loop. Could you fix you code please?

PHP 5.3 legacy branch issue

Hi Tom,

thanks for sharing your work. Dice is really easy to use yet powerfull tool.

I'm using PHP 5.3 legacy branch and I noticed one issue on 2 places lines 46 and 62.

You're using (new Object())->method() syntax but as far as I understand this is introduced in PHP 5.4.

Thanks

Support new xml format more readable

The actual grammar used in the XML file is very verbose and doesn't use very well XML features (like attributes).

So, this example with the actual grammar:

<?xml version="1.0"?>
<dice>
  <rule>
    <name>Foo</name>
    <!-- Shared: either true or false. If the tag is omitted it defaults to false -->
    <shared>true</shared>
    <!-- Multiple substitutions can be defined using the substitute tag: -->
    <substitute>
      <use>BIterator</use>
      <as>Iterator</as>
    </substitute>
    <substitute>
      <use>X</use>
      <as>Y</as>
    </substitute>
    <!-- Supports calling parameters after construction using the call tag: -->
    <call>
      <method>setAttribute</method>
      <params>
        <param>Foo</param>
        <param>Bar</param>
      </params>
    </call>
    <call>
      <method>setAttribute</method>
      <params>
        <param>Bar</param>
        <param>Baz</param>
      </params>
    </call>
    <!-- Multiple call tags can be defined. Instances of a specified class name are
        supported by using the <instance> tag. This will supply an instance of "X" 
        equivilant to $dice->create('X'); (using rules for X) to the setDependency method.
        Instance supports named instances as well as class names.
        -->
    <call>
      <method>setDependency</method>
      <params>
        <instance>X</instance>
      </params>
    </call>
    <!-- For named instances, supports the instanceof tag. 
             See documentation on Named Instances 
        -->
    <instanceof>Bar</instanceof>
    <!-- Comma separated list of dependencies which will be created new, ignoring their shared rules -->
    <newinstances>Foo,Bar</newinstances>
    <!-- List of constructor parameters -->
    <construct>
      <param>123</param>
      <param>XYZ</param>
    </construct>
  </rule>
  <rule>
    <name>Bar</name>
    <!-- Define rules for the next component -->
  </rule>
</dice>

could be, with a more intelligent parser:

<?xml version="1.0"?>
<dice>
  <rule name="foo" shared="true">
    <substitute use="BIterator" as="Iterator" />
    <substitute use="X" as="Y" />
    <!-- Supports calling parameters after construction using the call tag: -->
    <call method="setAttribute">
        <param>Foo</param>
        <param>Bar</param>
    </call>
    <call method="setAttribute">
        <param>Bar</param>
        <param>Baz</param>
    </call>
    <!-- Multiple call tags can be defined. Instances of a specified class name are
        supported by using the <instance> tag. This will supply an instance of "X" 
        equivilant to $dice->create('X'); (using rules for X) to the setDependency method.
        Instance supports named instances as well as class names.
        -->
    <call method="setDependency">
        <instance>X</instance>
    </call>
    <!-- For named instances, supports the instanceof tag. 
             See documentation on Named Instances 
        -->
    <instanceof>Bar</instanceof>
    <!-- Comma separated list of dependencies which will be created new, ignoring their shared rules -->
    <newinstances>Foo,Bar</newinstances>
    <!-- List of constructor parameters -->
    <construct>
      <param>123</param>
      <param>XYZ</param>
    </construct>
  </rule>
  <rule name="bar">
    <!-- Define rules for the next component -->
  </rule>
</dice>

It's more readable and easier to write, isn't it?

Caching

As the DIC relies on Reflection, and Relfection is the slowest part, until Reflection can be properly cached (serialized) adding a caching layer would add little benefit here.

Configuration could be cached, but as PHP does not allow caching of closures, any rules which relied on closures would not be cacheable. However, it's worth adding a caching layer with the caveat that rules cannot use closures if config caching is required.

2.0 Branch

I've just added a 2.0 branch with several changes. The main change is the use of arrays for configuration instead of the rule object as discussed in #56.

The other major change is structural one that gives a performance increase that actually makes Dice faster than containers that don't use reflection such as Pimple when a class is instantiated more than once by reducing the logic in each create() call.

This is a proof-of-concept more than anything but I think it's the right direction to go in.

PHP5.4

I've been informed the current 5.4-5.5 branch does not work in PHP5.4 Unfortunately I do not currently have a php5.4 install I can test with.

It looks as if the issue is the foreach ($arr as list()) that was added in #30.

I'm considering whether it's better to have a 5.4 branch or changing the PHP5.4-5.5 branch so it's compatible with 5.4. From a maintainability point of view keeping a single branch makes life easier.

The 5.4-5.5 branch also needs to have several improvements backported. As PHP5.4 is supported officially until at least september 2015, keeping 5.4 support is important.

Repo name

I'm slowly building all my components into a mini-framework based on Aphplication and for standardisaiton/better professionalism I'm tempted to move a the projects (Aphplication, Maphper, Axel and Dice) into its own organisation ( https://github.com/Level-2 )

As Dice doesn't use the vendor name in the namespace this won't be an issue for the PHP code, however, there's a couple of problems with composer.

Before moving anything, I just wanted to check nobody here would have an issue with TomBZombie\Dice becoming Level-2\Dice

What PHP 5.6 features are being used?

I wonder what of PHP 5.6 is being used, because right now I'm on PHP 5.4 want to use Dice anyway.

I've scanned Dice.php using the PhpStorm and it reported following issues:

  • $this in Closure is allowed in PHP 5.4 only
  • Argument unpacking is available in PHP 5.6 only
  • List in foreach is allowed in PHP 5.5 only

Technically these later PHP version features can be achieved in different syntax on PHP 5.4 as well. I wonder if that would result in performance penalty.

Namespaces

It's hard (perhaps impossible) to work with namespaces

Header comment formatting on github

This has been broken for a while. Not sure why. Both Sublime and Eclipse show the formatting as:

image

Yet github shows it with broken formatting.

Change composer to require php 5.4

You say this version is compatible with 5.4 and up, but in your composer you require 5.6. Can you change this? I am using php 5.5 and composer won't let me install it.

Dice not passing class instance to construct params

Hi Tom,

I can't seem to get Dice to work. Instead of passing an instance of a class to the constructor, it is passing an array. Below is an example of the issue I'm facing.

Code:

require_once 'dice.php';
class A
{
    public function __construct()
    {
    echo 'A init<br>';
    }

    public function test()
    {
        echo '<h1>Hello World</h1>';
    }
}

class B
{

    public function __construct(A $a)
    {
        echo 'B init<br>';
        $a->test();
    }
}

$dice = new Dice\Dice;
$b = $dice->create('B');

Outputs:

A init

Catchable fatal error: Argument 1 passed to B::__construct() must be an instance of A, array given, called in D:\xampp\htdocs\test\dice.php on line 39 and defined in D:\xampp\htdocs\test\index.php on line 20

Feature Request

@TomBZombie
Hi Tom,
it would be nice to reduce the number of lines of code when implementing a rule.
A couple of simple ways to achieve that:

//1. simply allowing rule definitions with arrays
$dice->addRule('PDO', [
    'shared' => true,
    'constructParams' => ['mysql:host=127.0.0.1;dbname=mydb', 'username', 'password'],
]);
//2. adding setters into the Rule class to obtain a fluent interface
$dice->addRule('PDO', (new Rule())->setShared(true)->setContructParams([]));

Class won't be shared if backslash is added before name.

I am not sure if this behavior is expected or not:

    Class SharedClass{
        public function __construct()
        {
            echo 'new Instance!';
        }
    }

    $dice = new \Dice\Dice;

    $rule = new \Dice\Rule;
    $rule->shared = true;
    $dice->addRule('\SharedClass', $rule); //<- notice the backslash

    $sc1 = $dice->create('\SharedClass');
    $sc2 = $dice->create('\SharedClass');
    var_dump($sc1 === $sc2); //OBVIOUSLY TRUE

    $sc3 = $dice->create('SharedClass');
    $sc4 = $dice->create('SharedClass');
    var_dump($sc3 === $sc4); //ALSO TRUE

    $sc5 = $dice->create('SharedClass');
    $sc6 = $dice->create('\SharedClass');
    var_dump($sc5 === $sc6); //FALSE! <- this kind of surprises me

Not sure if I just don't understand namespaces correctly, but we are in the root namespace here anyways. From my perspective \SharedClass and SharedClass are the same... Especially since it understood my instructions to share \SharedClass correctly when I made the rule, even when I then created an instance without the backslash.

Fatal Error: Uncaught exception 'ReflectionException'

To start off, thanks for your excellent library, maybe this is more of a user-error, would be kind of you to help me out.

Fatal error: Uncaught exception 'ReflectionException' with message 'Class Db is an internal class that cannot be instantiated without invoking its constructor'

Using PHP Version 5.5.9

Using Dice 1.3.2 this works:

$rule = new \Dice\Rule;
$rule->shared = true;
$dice->addRule('Db', $rule);
$db = $dice->create('Db', [$dbHost, $dbUser, $dbPass, $dbName]);

Using Dice 2.0-legacy this doesn't work: (producing forementioned error)

$rule1 = ['shared' => true];
$rule2 = ['constructParams' => [$dbHost, $dbUser, $dbPass, $dbName]];
$dice->addRule('Db', $rule1);
$dice->addRule('Db', $rule2);
$db = $dice->create('Db');

What am i missing here?
Important to notice on 5.6.4 everything works without a hickup, leading to believe i'm simply using the wrong library but i've double checked that twice.
On that note it may be usefull to include the "legacy" version information in the code comment on top

Shared objects and namespaces

I've had a few emails about this. I'll add a commit for this on the 2.0 branch so I'm just adding this issue as a a changelog really

namespace Foo;
class A {
   public function __construct(B $b) {
       $this->b = $b;
   }
}

class B {

}
$dice = new \Dice\Dice;
$dice->addRule('\Foo\A', ['shared' => true]);

$b1 = $dice->create('\Foo\B');
$b2 = $dice->create('\Foo\B');

var_dump($b1 === $b2); // TRUE

It looks like Dice is correctly giving the same instance each time.

However, extending the above:

$b1 = $dice->create('\Foo\B');
$b2 = $dice->create('\Foo\B');

$a = $dice->create('\Foo\A');

var_dump($b1 === $b2); // TRUE
var_dump($a->b === $b1); //FALSE

This is fixed by removing all the namespace prefix slashes:

$b1 = $dice->create('Foo\B');
$b2 = $dice->create('Foo\B');

$a = $dice->create('Foo\A');

var_dump($b1 === $b2); // TRUE
var_dump($a->b === $b1); //TRUE

This is needlessly confusing, in the past I've highlighted that fixing this causes a fairly substantial (10-15%) performance drop because it has to trim the slash and 99% of the time it doesn't need to. I've applied a separate fix, rather than trimming the slash each time create is called:

    public function create($name, array $args = [], array $share = []) {
        $name = ltrim($name, '\\');
        if (!empty($this->instances[$name])) return $this->instances[$name];
        if (empty($this->cache[$name])) $this->cache[$name] = $this->getClosure($name, $this->getRule($name));
        return $this->cache[$name]($args, $share);
    }

and adding overhead to the create() call, instead I've changed it so that the reference to the instance is to both the slash, and non-slash index in the $instances array.

$this->instances[$name] = $this->instances[ltrim($name, '\\')] = $class->newInstanceWithoutConstructor();

While not the cleanest code, this solves the bug and doesn't introduce the performance drop at the slight expense of memory usage (one extra reference).

Test class structure

The single test class has become far too big to the point where it's difficult to work with, For the 2.0 branch I'm going to split it up into different files for various tests:

-Namespaces
-Constructor arguments using the second argument in the create method
-Substitutions
-Construct Parameters
-Shared Instances
-Call
-Misc

And split up the testclasses.php accordingly. This will make the test cases a lot easer to work with.

Best Practices

I am just getting started with Dice and I have some questions about best practices. In our Stack we are using Tonic for our REST endpoints, and Doctrine as our ORM. These two programs have presented some problems with how I implemet Dice and I am trying to work around them.

First Tonic (https://github.com/peej/tonic). The way it is designed to handle DI is by adding the container to its application object which it passes to the endpoints. Here is an example of how I have implemented it.

    $app = new Tonic\Application();
    $app ->di_container = new \Dice\Dice;

    $request = new Tonic\Request();
    $resource = $app->getResource($request);

    $response = $resource->exec();

and on the endpoint it looks like this

public function __construct(Application $app, Request $request)
    {
        $this->user_service = $app->di_container->create('namespace\UserService');
        $this->profile_service = $app->di_container->create('namespace\ProfileService');
        ...
    }

Does this seem like the best way? This could end up being a decent list because we do access Doctrine entities here and as I explain below we have a lot of Doctrine entities.

Now Doctrine (http://www.doctrine-project.org/). Doctrine creates an entity and manager for each table in our database. So if I have 10 tables Doctrine creates 20 classes. If I have a class that needs to use 5 of those tables I have to list 10 classes in my constructor.

   public function __construct(
        UserManager $user_manager,
        UserEntity $user_entity,
        ProfileManager $profile_manager,
        ProfileEntity $profile_entity,
        TestManager $test_manager,
        TestEntity $test_entity,
        CompanyManager $company_manager,
        CompanyEntity $company_entity,
        StateManager $state_manager,
        StateEntity $state_entity
    )
    {
        $this->user_manager = $user_manager;
        $this->user_entity = $user_entity;
        $this->profile_manager = $profile_manager;
        $this->profile_entity = $profile_entity;
        $this->test_manager = $test_manager;
        $this->test_entity = $test_entity;
        $this->company_manager = $company_manager;
        $this->company_entity = $company_entity;
        $this->state_manager = $state_manager;
        $this->state_entity = $state_entity;
    }

My concern is that this is going to become a really long list and a real big pain to maintain.

I have thought of moving them all to a parent class that just defines all possible dependencies, but that seems like a bad idea. If Dice creates these objects in the constructor then that would cause a lot of unneeded classes to be put in memory.

If I setup a shared rule for the classes then that might help, but I would have to maintain a huge list all my classes (around 100) just so I can assign them all a shared rule. Is there a way to have the classes be shared by default so I don't have to setup all the rules?

Anyways I could use a little direction on the best way to implement Dice. Otherwise I may just keep using our current method, which is a service locator that only creates classes when we need them and caches each class when it is created so that is does not take up a lot of memory.

public function getTestService()
    {
        if (empty($this->test_service)) {
            $this->test_service = new TestService();
        }
        return $this->test_service;
    }

How to add existing objects to a container?

Sorry for asking that probably simple question, but how can I add an already existing object to the container?

My scenario is that I've implemented a custom service layer within my app written with the CakePHP framework and that I want to get the request and response objects in my dispatcher filter and want to add them there to the container.

I've wrapped a Dice instance with a singleton class to have a single, globally available container for some of the "global" classes. Cake is unfortunately not using any dependency injection but internally has a lot collections or loader methods for each kind of object.

reading values from classes in XML config passes array instead of the value

dice_test.xml:

<?xml version="1.0"?>
<dice>
    <rule>
        <name>PDO</name>
        <shared>true</shared>
        <construct>
            <param>{Config::PDO_DSN</param>
            <param>{Config::PDO_USER}</param>
            <param>{Config::PDO_PASS</param>
        </construct>
    </rule>
</dice>

dice_test.php:

<?php
require __DIR__ . '/vendor/autoload.php';

$dice = new \Dice\Dice();

$xmlLoader = new Dice\Loader\Xml();
$xmlLoader->load(__DIR__ . '/dice_test.xml', $dice);

$pdo = $dice->create('PDO');

class Config {
    public $PDO_DSN = 'mysql:host=localhost';
    public $PDO_USER = '';
    public $PDO_PASS = '';
}

php dice_test.php output:
Warning: PDO::__construct() expects parameter 1 to be string, array given in /home/justin/gitdevel/beanstalk-provisioner/vendor/tombzombie/dice/Dice.php on line 36

Issue with constructor params of injected class

I have a class called A that injects another class called B. Class B has a constructor param that is just a plain string, but when that class is created it is being given an object.

class A {
    public function __construct(B $b) {
    }
}

class B {
    public function __construct($string = null) {
    }
}

How can I get class B created without a param, or with a null param?

I tried putting in a rule like this, but it didn't work.

$rule = new \Dice\Rule;
$rule->constructParams = [null];
$dice->addRule('B', $rule);

Share instances with Dice

Just curious, can I share a standard instantiated class into Dice's rule?

For instance, I make an instance of $Database in a standard approach, but then I want to share this instance with other classes that will be instantiated in Dice.

Is it possible?

My test,

// Load Database class only
// Instance of Database.
$Database = new Database(DSN,DB_USER,DB_PASS);

// Make connection.
$Database->connect();

// Load subsequent classes

// Load Dice
// Instance of Dice.
$Dice = new \Dice\Dice;

//create a rule to apply to shared object
$Rule = new \Dice\Rule;
$Rule->shared = true;

//Set rules
$rule->shareInstances = [$Database];

//Apply the rule to instances of Database
$Dice->addRule('Database', $Rule);

//And any class which asks for an instance of Database will be given the same instance:
class MyClass {
    public $pdo;
    public function __construct(Database $pdo) {
        $this->pdo = $pdo;
    }
}

$myobj = $Dice->create('MyClass');
var_dump($myobj->pdo  === $Database);

error,

Warning: Creating default object from empty value in C:...

Unable to assign() an instance to a particular class

We have a situation where we are using the Mako framework with Dice and would like to use Dice to manage all of the Mako dependencies our libraries have. Mako uses an adapter pattern where there are several factory classes that return a specific subclass of a generic Adapter class for each.

What I would like to be able to do is to call assign and specify the parent-class that we would like that instance to be registered under:

$dice = new \Dice\Dice();
$dice->assign('mako\cache\Adapter', Cache::instance('memcache'));

This would then be consumed by the dependee class we are writing as such:

class Content {
  private $cache;
  public function __construct(\mako\cache\Adapter $cache) {
    $this->cache = $cache;
  }
}

I believe the implementation of the method would be as simple as:

public function assign($name, $object) {
    $this->instances[$name] = $object;
}

dice fails in line 38

Hi Tom,
your articles on your blog are very interesting for me. I now wanted to start with DICE.

When I download it and open it in Eclipse PDT, I get the error message that '(...$params(' on line 38 is invalid. Also when I upload it and try it fails with "unexpected ."

So I removed the three dots. Now I get one step further. I added the rules for a shared instance of PDO as described in your documentation. I changed the constructor of my class to "public function __construct(PDO $pdo)" (line 15) - now I get the following error message:

"Catchable fatal error: Argument 1 passed to SitzungsHandler::__construct() must be an instance of PDO, array given, called in /var/wwwdev/library/dice.php on line 38 and defined in /var/wwwdev/library/sitzungshandler.class.php on line 15"

Is it my failure or is it due to the three dots there?

Regards
UZi!

Private constructors

Thanks to Simon Jhnag for pointing this out. The following code:

class MyClass {
    private function __construct() {

    }

    public function forge() {
        return new MyClass();
    }
}


class NeedsMyClass {
    public $myClass;

    public function __construct(MyClass $myClass) {
        $this->myClass = $myClass;
    }
}

$dice = new \Dice\Dice;

$rule = new \Dice\Rule;
$rule->substitutions['MyClass'] = new \Dice\Instance(function() {
    return MyClass::forge();
});

$myobj = $dice->create('NeedsMyClass');
var_dump($myobj->myClass);

Should work. However, it gives the error:

PHP Fatal error:  Uncaught exception 'ReflectionException' with message 'Access to non-public constructor of class MyClass' in Dice.php:46

Packagist does not know about releases

packagist.org does not know about your releases. I'm not sure what's wrong here, though. Did you setup the packagist service hook for the repository?

Different Scopes

I'm working on something Top Secretโ„ข and need to introduce new scopes:

  • Per Session
  • Per Process
  • Shared (Among all processes)

This is going to have to break BC in some way, however this change will make dice a lot more extensible. I was thinking something like this:

$rule->scope = new \Dice\Shared();
$rule->scope = new \Dice\Process();

This will have problems with performance and will need some tweaking but here's what I have so far (WIP). Essentially I want people to be able to define their own scopes and only provide prototype/shared by default.

class Shared {
    private $instance;

    public function __invoke(\ReflectionClass $class, \ReflectionMethod $constructor = null, $args) {
        $new = !isset($this->instance);
        if ($new) {
            $this->instance = $class->newInstanceWithoutConstructor();
            if ($constructor) $constructor->invokeArgs($object, $args);
        }       
        return [$new, $this->instance];
    }   
}

class Prototype {
    public function __invoke(\ReflectionClass $class, \ReflectionMethod $constructor = null, $args) {
        return [true, $params ? new $class->name(...$params($args, $share)) : new $class->name];
    }
}

Then in $dice->create():

    public function create($component, array $args = [], $forceNewInstance = false, $share = []) {
        if (empty($this->cache[$component])) {
            $rule = $this->getRule($component);
            $class = new \ReflectionClass($rule->instanceOf ?: $component);
            $constructor = $class->getConstructor();            
            $params = $constructor ? $this->getParams($constructor, $rule) : null;

            $this->cache[$component] = function($args, $share) use ($component, $rule, $class, $constructor, $params) {
                $scope = $rule->scope($class, $constructor, $params ? $params($args, $share) : null);
                if ($rule->call && $scope[0]) foreach ($rule->call as $call) $class->getMethod($call[0])->invokeArgs($scope[1], call_user_func($this->getParams($class->getMethod($call[0]), $rule), $this->expand($call[1])));
                return $scope[1];
            };
        }
        return $this->cache[$component]($args, $share);
    }

Namespace with a PDO class

It seems that there is a bug when the namespace is being used with a PDO class,

Database.php,

namespace Namie;
class Database
{
    // Set local props.
    protected $Database = null;
    protected $dsn,$username,$password;

    /**
     * Set the class contructor.
     */
    public function __construct($dsn,$username,$password)
    {

        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;

    }

    /**
     * make the pdo connection.
     * @return object $Database
     */
    public function connect()
    {
        try
        {
            $this->Database = new \PDO($this->dsn, $this->username, $this->password, array(\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
            $this->Database->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); 

            unset($this->dsn);
            unset($this->username);
            unset($this->password);

        } 
        catch (PDOException $e) 
        {
            // call the get_error function
            $this->get_error($e);
        }
    }
}

index.php,

//create a rule to apply to shared object
$rule = new \Dice\Rule;
$rule->shared = true;

//Database will be constructed by the container with these variables supplied to Database::__construct
$rule->constructParams = [DSN,DB_USER,DB_PASS];

//Apply the rule to instances of Database
$dice->addRule('\Namie\Database', $rule);

//Now any time Database is requested from Dice, the same instance will be returned
$pdo = $dice->create('\Namie\Database');

class MyClass {
    public $pdo;
    public function __construct(\Namie\Database $pdo) {
        $this->pdo = $pdo;
    }
}

// Make connection.
$pdo->connect();
var_dump($pdo); // protected 'Database' =>  object(PDO)[24] ---> correct

$myobj = $dice->create('MyClass');
var_dump($myobj->pdo); // protected 'Database' => null ---> incorrect

result,

object(Namie\Database)[21]
  protected 'Database' => 
    object(PDO)[24]

object(Namie\Database)[32]
  protected 'Database' => null
  protected 'dsn' => string 'mysql:host=localhost;dbname=xxx' (length=38) // this should not be seen.
  protected 'username' => string 'xxx' (length=4) // this should not be seen.
  protected 'password' => string 'xxx' (length=5) // this should not be seen.

Regarding releases...

  1. What ever happened to v1.3.1? Did it ever exist?
  2. Do you think it's about time to do a new release, with all of the changes that have been made since v1.3.2?

Constructor arguments swapping if one (not the first) of them is NULL

Branch: PHP5.4---PHP5.5
I believe the master branch is also affected, but didn't test it.

Code:

class A {
  function __construct($must_be_string, $may_be_null)
  {
    var_export(func_get_args());
  }
}
$dice = new \Dice\Dice();
$dice->create('A', ['string', NULL]);

Output:

array (
  0 => NULL,
  1 => 'string',
)

Expected output:

array (
  0 => 'string',
  1 => NULL,
)

Plausible cause:
I think the problem is on line 74 of dice.php:

if ($class && $args[$i] instanceof $class || !$args[$i] && $allowsNull) {
    $parameters[] = array_splice($args, $i, 1)[0];
    continue 2;
}

!$args[$i] && $allowsNull is evaluated to TRUE when $i is 1 and foreach loop above handles the first item of $paramInfo (so $parameters is empty) and the next line splices the second NULL argument from $args.
To be honest, I failed to fully understand, why !$args[$i] && $allowsNull is needed. I think it may be not needed at all, because NULL arguments should be correctly handled by $this->expand(array_shift($args)) below.

PDO class with namespace

It seems there is a problem with to pass params into the a PDO class from constructParams when a namespace is used in my db class.

Database.php,

namespace MyName;
class Database
{
    protected $Database = null;
    protected $dsn,$username,$password;
    public function __construct($dsn,$username,$password)
    {

        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;
    }

index.php,

// Load Dice
// Instance of Dice.
$Dice = new \Dice\Dice;

//create a rule to apply to shared object
$Rule = new \Dice\Rule;
$Rule->shared = true;

//Database will be constructed by the container with these variables supplied to Database::__construct
$Rule->constructParams = [DSN,DB_USER,DB_PASS];

//Apply the rule to instances of Database
$Dice->addRule('Database', $Rule);

//Now any time Database is requested from Dice, the same instance will be returned
$Database = $Dice->create('MyName\Database');

error,

Warning: Missing argument 1 for MyName\Database::__construct() in C:...
Warning: Missing argument 2 for MyName\Database::__construct() in C:...
Warning: Missing argument 3 for MyName\Database::__construct() in C:...

Any ideas what have I missed and what should I do?

Injecting interfaces via call[] setters

I could not make it work.

Tryied with some simple case, like Namespace\TestA and Namespace\TestB, and setA() method inside TestB class (TestA class has constructor injection which is working ok).

If setA method looks like this:

function setA(\Namespace\TestA $a) 
{
$this->a = $a;
}

Than it works ok and everything is cool. However, if i introduce an interface (implemented by TestA class):

interface TestAInterface 
{
}

And now wanna to do it like this:

function setA(\Namespace\TestAInterface $a)
{
$this->a = $a;
}

Of course there is also (in both cases):

$DefaultRule->substitutions['Namespace\\TestAInterface'] = new \Dice\Instance('Namespace\\TestA');
$container->addRule('*', $DefaultRule);

$TestARule = clone $dice->getRule('*');
$TestARule->call[] = ['setA', [new \Dice\Instance('Namespace\\TestA')]]; //(for the first case)
$TestARule->call[] = ['setA', [new \Dice\Instance('Namespace\\TestAInterface')]]; //(for the second case)

And after $dice->create() it breaks in second case. Sometimes it shows an fatal about Dice\Instance not having setA method, and sometimes it is not, but even then $objectB->a is not instance of TestA class, but Dice\Instance class instead.

So it looks like interface resolution is not working when passing it to call array.

Examples are brain_var_dump(), so in case if you couldn't reproduce it i'm gonna paste some real code (i will try to do it tommorow).

Or maybe it's my mistake - i found about dice just few days ago and could miss something in the doc. In that case i would be really thankful for your help, as without working interfaces it kills the purpouse for me.

Any way - dice is really a great tool, keep it up :)

Notice: Undefined property: Dice\Rule::$instanceof in

Tom, hi! I recently wanted to update Dice and have the following warning shown, which weren't the case before. I know it's me missing something but not sure what?

Notice: Undefined property: Dice\Rule::$instanceof in /home/project.development/public_html/plugins/dice/dice.class.php on line 61

Notice: Undefined property: Dice\Rule::$instanceof in /home/project.development/public_html/plugins/dice/dice.class.php on line 77

Dice IS the dependency

How would I go about giving NotifyUser the same instance of Dice that was used to create it in the first place so my rules are maintianed? My code:

$dice = new \Dice\Dice();
$shareRule = new \Dice\Rule();
$shareRule->shared = true;
$dice->addRule('\\PDO', $shareRule);
$model = $dice->create('NotifyUser');

class NotifyUser {
    public function __construct(\PDO $db, \Dice\Dice $dice){
        $this->db = $db;
        $this->dice = $dice;
    }

    public function logEmailSent(){
        $logger = $this->dice->create('\\EmailLogger');
        $logger->log($emailBody);
    }    
}

class EmailLogger {
    public function __construct(\PDO $db){
        $this->db = $db;
    }

    public function log($emailBody){
        $this->db->prepare('INSERT INTO...');
    }    
}

Case sensitivity in Dice rule names

I recently made a case error while configuring a Dice rule, with the result that the rule was not applied when I later created the (intended) target object via Dice. My bad. But it made me wonder whether Dice rule names should be handled in a case-insensitive fashion, since they (always?) refer to case-insensitive PHP class names.

There is no way to make Dice intelligently warn about unused rules, or rules that name non-existent classes, so users really just need to avoid mistakes while typing rule names and always consider a rule name error when troubleshooting. But it seems to me that perhaps errors in case could easily and consistently be solved by Dice with no ill effects.

$forceNewInstance /newInstances

I'm tempted to remove this functionality as it's something I've never needed. Considering it's also possible with named instances it's quite redundant. Removing this lowers the complexity/overhead of Dice.

Is anyone using this feature? In all the comments, bug reports and questions I've had about Dice, none have been about this feature despite the fact that it only works with $shared, which is one of the most common questions I get.

If anyone can provide a real life use-case I'm happy to keep it in, but in the interest of avoiding unnecessary bloat I'd rather remove it.

Namespaces

I am a little confused from the docs on using namespaces.

Reading it leads me to believe I would need to preconfigure all the classes.

I was hoping to have a zero config option with namespace support.

Is that the case or am I misreading it?

[RFC] PSR-2

I think its better to improve code readability by following PSR-2 coding convention. I would love to send PR on this issue

How can I get this Dice + Twig + Symfony Translator example working?

I've been trying to figure this out for the past day but am not having any luck. I keep getting stuck by an error that appears when I try to substitute Twig_ExtensionInterface. It seems like Dice isn't even attempting to do the substitution because my debug "echo" isn't running. If you have any advice on how to get this working, I would very much appreciate it!

composer.json:

{
    "require": {
        "php": ">=5.6.0",
        "tombzombie/dice": "dev-PHP5.6",
        "symfony/twig-bridge": "~2.5.4",
        "symfony/translation": "~2.5.4"
    }
}

index.php:

<?php

use Dice\Dice;
use Dice\Rule;
use Dice\Instance;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Bridge\Twig\Extension\TranslationExtension;

require __DIR__.'/vendor/autoload.php';

class Example
{
    public function __construct(Twig_Environment $twig)
    {
        echo $twig->render(
            "{{ 'greeting' | trans }}, TomBZombie! I <3 Dice!"
        );
    }
}

$locale = 'fr'; // or 'en'

// Using plain PHP

$translator = new Translator($locale);
$translator->setFallbackLocales(['en']);
$translator->addLoader('array', new ArrayLoader);
$translator->addResource('array', [ 'greeting' => 'Hello'], 'en');
$translator->addResource('array', [ 'greeting' => 'Bonjour'], 'fr');

$twig = new Twig_Environment(new Twig_Loader_String);
$twig->addExtension(new TranslationExtension($translator));

echo "*** Using plain PHP:\n";
new Example($twig);
echo "\n\n";

// Using Dice

$dice = new Dice;

$rule = new Rule;
$rule->constructParams = [$locale];
$rule->call = [
    ['setFallbackLocales', [['en']]],
    ['addLoader', ['array', new Instance('Symfony\\Component\\Translation\\Loader\\ArrayLoader')]],
    ['addResource', ['array', [ 'greeting' => 'Hello'], 'en']],
    ['addResource', ['array', [ 'greeting' => 'Bonjour'], 'fr']],
];
$dice->addRule('Symfony\\Component\\Translation\\Translator', $rule);

$rule = new Rule;
$rule->call = [
    ['addExtension', [new Instance('Symfony\\Bridge\\Twig\\Extension\\TranslationExtension')]],
];
$rule->substitutions['Twig_LoaderInterface'] = new Instance('Twig_Loader_String');
$rule->substitutions['Twig_ExtensionInterface'] = function () use ($dice) {
    echo "Substituting Twig_ExtensionInterface => TranslationExtension\n";
    return new Symfony\Bridge\Twig\Extension\TranslationExtension(
        $dice->create('Symfony\\Component\\Translation\\Translator')
    );
};
$dice->addRule('Twig_Environment', $rule);

echo "*** Using Dice:\n";
$dice->create('Example');
echo "\n\n";

Default rule only work if there is not a more specific one

I was reading the documentation about the default rule, and thought about what happen when there is a more specific rule. And indeed only the latest one is applied, even if it doesn't overwrite some rule properties.

Here is my test:

class MyPDO extends PDO {}

class Foo {
  public $pdo;
  public function __construct(PDO $pdo) {
    $this->pdo = $pdo;
  }
}

$rule = new \Dice\Rule;
$rule->substitutions['PDO'] = new \Dice\Instance('MyPDO');

$shared = new \Dice\Rule;
$shared->shared = true;

$pdorule = new \Dice\Rule;
$pdorule->constructParams = ['sqlite::memory:'];

$dice = new \Dice\Dice;
$dice->addRule('*', $rule);
$dice->addRule('Foo', $shared);
$dice->addRule('PDO', $pdorule);

$foo = $dice->create('Foo');

echo get_class($foo->pdo);

This will output PDO instead the expected (to me) MyPDO.

Dice.php not compatible with php 5.4.44

I run php -l Dice.php and I got :

PHP Parse error:  syntax error, unexpected '.', expecting ')' in Dice.php on line 66

Parse error: syntax error, unexpected '.', expecting ')' in Dice.php on line 66

Errors parsing Dice.php

php -v : PHP 5.4.44-0+deb7u1 (cli) (built: Aug 26 2015 12:27:40)
uname -a : Linux jhary 3.8.13-bone50 #1 SMP Tue May 13 13:24:52 UTC 2014 armv7l GNU/Linux
(yes, I'm on BeagleBone)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.