Skip to content

Commit

Permalink
Merge pull request nelmio#68 from WishCow/static-construct
Browse files Browse the repository at this point in the history
Allow calling static methods as constructors
  • Loading branch information
Seldaek committed Oct 30, 2013
2 parents 3cc6f43 + d46d74f commit ab2669d
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 3 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,15 @@ Nelmio\Entity\User:
__construct: [<username()>]
```

If you want to call a static method, instead of a constructor, you can specify
a hash as the constructor:

```yaml
Nelmio\Entity\User:
user1:
__construct: { create: [<username()>] }
```

If you specify `false` in place of constructor arguments, Alice will
instantiate the object without executing the constructor:

Expand Down
34 changes: 31 additions & 3 deletions src/Nelmio/Alice/Loader/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -395,18 +395,46 @@ private function createInstance($class, $name, array &$data)
return $this->references[$name] = $reflClass->newInstanceWithoutConstructor();
}

if (!is_array($args)) {
/**
* Sequential arrays call the constructor, hashes call a static method
*
* array('foo', 'bar') => new $class('foo', 'bar')
* array('foo' => array('bar')) => $class::foo('bar')
*/
if (is_array($args)) {
$constructor = '__construct';
list($index, $values) = each($args);
if ($index !== 0) {
if (!is_array($values)) {
throw new \UnexpectedValueException("The static '$index' call in object '$name' must be given an array");
}
if (!is_callable(array($class, $index))) {
throw new \UnexpectedValueException("Cannot call static method '$index' on class '$class' as a constructor for object '$name'");
}
$constructor = $index;
$args = $values;
}
} else {
throw new \UnexpectedValueException('The __construct call in object '.$name.' must be defined as an array of arguments or false to bypass it');
}

// create object with given args
$reflClass = new \ReflectionClass($class);
$args = $this->process($args, array());
foreach ($args as $num => $param) {
$args[$num] = $this->checkTypeHints($class, '__construct', $param, $num);
$args[$num] = $this->checkTypeHints($class, $constructor, $param, $num);
}

if ($constructor === '__construct') {
$instance = $reflClass->newInstanceArgs($args);
} else {
$instance = forward_static_call_array(array($class, $constructor), $args);
if (!($instance instanceof $class)) {
throw new \UnexpectedValueException("The static constructor '$constructor' for object '$name' returned an object that is not an instance of '$class'");
}
}

return $this->references[$name] = $reflClass->newInstanceArgs($args);
return $this->references[$name] = $instance;
}

// call the constructor if it contains optional params only
Expand Down
55 changes: 55 additions & 0 deletions tests/Nelmio/Alice/Loader/BaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,61 @@ public function testLoadCallsConstructorIfProvided()
);
}

public function testLoadCallsStaticConstructorIfProvided()
{
$res = $this->loadData(array(
self::USER => array(
'user' => array(
'__construct' => array('create' => array('alice')),
),
),
));
$this->assertInstanceOf(self::USER, $res[0]);
$this->assertSame('alice', $res[0]->username);
}

/**
* @expectedException UnexpectedValueException
* @expectedExceptionMessage
*/
public function testLoadFailsOnInvalidStaticConstructor() {
$res = $this->loadData(array(
self::USER => array(
'user' => array(
'__construct' => array('invalidMethod' => array('[email protected]')),
),
),
));
}

/**
* @expectedException UnexpectedValueException
* @expectedExceptionMessage
*/
public function testLoadFailsOnScalarStaticConstructorArgs() {
$res = $this->loadData(array(
self::USER => array(
'user' => array(
'__construct' => array('create' => '[email protected]'),
),
),
));
}

/**
* @expectedException UnexpectedValueException
* @expectedExceptionMessage
*/
public function testLoadFailsIfStaticMethodDoesntReturnAnInstance() {
$res = $this->loadData(array(
self::USER => array(
'user' => array(
'__construct' => array('bogusCreate' => array('[email protected]')),
),
),
));
}

public function testLoadCallsCustomMethodAfterCtor()
{
$res = $this->loadData(array(
Expand Down
9 changes: 9 additions & 0 deletions tests/Nelmio/Alice/fixtures/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ public function __construct($username = null, $email = null, \DateTime $birthDat
$this->birthDate = $birthDate;
}

public static function create($username = null, $email = null, \DateTime $birthDate = null)
{
return new static($username, $email, $birthDate);
}

public static function bogusCreate($username = null, $email = null, \DateTime $birthDate = null)
{
}

public function getAge()
{
return 25;
Expand Down

0 comments on commit ab2669d

Please sign in to comment.