Skip to content

Commit

Permalink
add global factory
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Jun 4, 2020
1 parent b80b1b1 commit 6491150
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 161 deletions.
25 changes: 4 additions & 21 deletions src/DIContainerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,23 +108,6 @@ public static function checkInstanceOf(object $object)// :static supported by PH
return $object;
}

/** @var FactoryTrait */
private static $_defaultFactory; // @TODO must be globally static, not trait static

/**
* @return FactoryTrait
*/
private static function _getDefaultFactory()
{
if (self::$_defaultFactory === null) {
self::$_defaultFactory = new class() {
use FactoryTrait;
};
}

return self::$_defaultFactory;
}

/**
* Create new object and check if it is instance of current class. Typehinting-friendly.
*
Expand All @@ -135,8 +118,8 @@ private static function _getDefaultFactory()
* @return static
*/
public static function fromSeed($seed = [])// :static supported by PHP8+
{
return static::addTo(self::_getDefaultFactory(), $seed, [], true);
{// @TODO move code from addTo here
return static::addTo(GlobalInternal::getDefaultFactory(), $seed, [], true);
}

/**
Expand All @@ -147,7 +130,7 @@ public static function fromSeed($seed = [])// :static supported by PHP8+
* @return static
*/
public static function fromSeedWithCl($seed = [])// :static supported by PHP8+
{
return static::addToWithCl(self::_getDefaultFactory(), $seed, [], true);
{// @TODO move code from addTo here
return static::addToWithCl(GlobalInternal::getDefaultFactory(), $seed, [], true);
}
}
181 changes: 181 additions & 0 deletions src/Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
<?php

declare(strict_types=1);

namespace atk4\core;

class Factory
{
/**
* Given two seeds (or more) will merge them, prioritizing the first argument.
* If object is passed on either of arguments, then it will setDefaults() remaining
* arguments, respecting their positioning.
*
* See full documentation.
*
* @param array|object|mixed $seed
* @param array|object|mixed $seed2
* @param array $more_seeds
*
* @return object|array if at least one seed is an object, will return object
*/
public function mergeSeeds($seed, $seed2, ...$more_seeds)
{
// recursively merge extra seeds
if ($more_seeds) {
$seed2 = $this->mergeSeeds($seed2, ...$more_seeds);
}

if (is_object($seed)) {
if (is_array($seed2)) {
// set defaults but don't override existing properties
$arguments = array_filter($seed2, 'is_numeric', ARRAY_FILTER_USE_KEY); // with numeric keys
$injection = array_diff_key($seed2, $arguments); // with string keys
if ($injection) {
if (isset($seed->_DIContainerTrait)) {
$seed->setDefaults($injection, true);
} else {
throw new Exception([
'factory() requested to passively inject some properties into existing object that does not use \atk4\core\DIContainerTrait',
'object' => $seed,
'injection' => $injection,
]);
}
}
}

return $seed;
}

if (is_object($seed2)) {
// seed is not object, and setDefaults will complain if it's not array
if (is_array($seed)) {
$arguments = array_filter($seed, 'is_numeric', ARRAY_FILTER_USE_KEY); // with numeric keys
$injection = array_diff_key($seed, $arguments); // with string keys
if ($injection) {
if (isset($seed2->_DIContainerTrait)) {
$seed2->setDefaults($injection);
} else {
throw new Exception([
'factory() requested to inject some properties into existing object that does not use \atk4\core\DIContainerTrait',
'object' => $seed2,
'injection' => $seed,
]);
}
}
}

return $seed2;
}

if (!is_array($seed)) {
$seed = [$seed];
}

if (!is_array($seed2)) {
$seed2 = [$seed2];
}

// merge seeds but prefer seed over seed2
// move numerical keys to the beginning and sort them
$res = [];
$res2 = [];
foreach ($seed as $k => $v) {
if (is_numeric($k)) {
$res[$k] = $v;
} elseif ($v !== null) {
$res2[$k] = $v;
}
}
foreach ($seed2 as $k => $v) {
if (is_numeric($k)) {
if (!isset($res[$k])) {
$res[$k] = $v;
}
} elseif ($v !== null) {
if (!isset($res2[$k])) {
$res2[$k] = $v;
}
}
}
ksort($res, SORT_NUMERIC);
$res = $res + $res2;

return $res;
}

/**
* Given a Seed (see doc) as a first argument, will create object of a corresponding
* class, call constructor with numerical arguments of a seed and inject key/value
* arguments.
*
* Argument $defaults has the same effect as the seed, but will not contain the class.
* Class is always determined by seed, except if you pass object into defaults.
*
* To learn more about mechanics of factory trait, see documentation
*
* @param mixed $seed
* @param array $defaults
* @param string $prefix No longer supported
*/
public function factory($seed, $defaults = [], string $prefix = null): object
{
if ($prefix !== null) { // remove in 2021-june
throw new Exception(['Factory does no longer support relative names, pass full class name without prefix']);
}

if ($defaults === null) {
$defaults = [];
}

if (!$seed) {
$seed = [];
}

if (!is_array($seed)) {
$seed = [$seed];
}

if (is_array($defaults)) {
array_unshift($defaults, null); // insert argument 0
} elseif (!is_object($defaults)) {
$defaults = [null, $defaults];
}
$seed = $this->mergeSeeds($seed, $defaults);

if (is_object($seed)) {
// setDefaults already called
return $seed;
}

$arguments = array_filter($seed, 'is_numeric', ARRAY_FILTER_USE_KEY); // with numeric keys
$injection = array_diff_key($seed, $arguments); // with string keys
$object = array_shift($arguments); // first numeric key argument is object

if (!is_object($object)) {
if (!is_string($object)) {
throw new Exception([
'Class name was not specified by the seed',
'seed' => $seed,
]);
}

$object = new $object(...$arguments);
}

if ($injection) {
if (isset($object->_DIContainerTrait)) {
$object->setDefaults($injection);
} else {
throw new Exception([
'factory() could not inject properties into new object. It does not use \atk4\core\DIContainerTrait',
'object' => $object,
'seed' => $seed,
'injection' => $injection,
]);
}
}

return $object;
}
}
143 changes: 3 additions & 140 deletions src/FactoryTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,87 +28,7 @@ trait FactoryTrait
*/
public function mergeSeeds($seed, $seed2, ...$more_seeds)
{
// recursively merge extra seeds
if ($more_seeds) {
$seed2 = $this->mergeSeeds($seed2, ...$more_seeds);
}

if (is_object($seed)) {
if (is_array($seed2)) {
// set defaults but don't override existing properties
$arguments = array_filter($seed2, 'is_numeric', ARRAY_FILTER_USE_KEY); // with numeric keys
$injection = array_diff_key($seed2, $arguments); // with string keys
if ($injection) {
if (isset($seed->_DIContainerTrait)) {
$seed->setDefaults($injection, true);
} else {
throw new Exception([
'factory() requested to passively inject some properties into existing object that does not use \atk4\core\DIContainerTrait',
'object' => $seed,
'injection' => $injection,
]);
}
}
}

return $seed;
}

if (is_object($seed2)) {
// seed is not object, and setDefaults will complain if it's not array
if (is_array($seed)) {
$arguments = array_filter($seed, 'is_numeric', ARRAY_FILTER_USE_KEY); // with numeric keys
$injection = array_diff_key($seed, $arguments); // with string keys
if ($injection) {
if (isset($seed2->_DIContainerTrait)) {
$seed2->setDefaults($injection);
} else {
throw new Exception([
'factory() requested to inject some properties into existing object that does not use \atk4\core\DIContainerTrait',
'object' => $seed2,
'injection' => $seed,
]);
}
}
}

return $seed2;
}

if (!is_array($seed)) {
$seed = [$seed];
}

if (!is_array($seed2)) {
$seed2 = [$seed2];
}

// merge seeds but prefer seed over seed2
// move numerical keys to the beginning and sort them
$res = [];
$res2 = [];
foreach ($seed as $k => $v) {
if (is_numeric($k)) {
$res[$k] = $v;
} elseif ($v !== null) {
$res2[$k] = $v;
}
}
foreach ($seed2 as $k => $v) {
if (is_numeric($k)) {
if (!isset($res[$k])) {
$res[$k] = $v;
}
} elseif ($v !== null) {
if (!isset($res2[$k])) {
$res2[$k] = $v;
}
}
}
ksort($res, SORT_NUMERIC);
$res = $res + $res2;

return $res;
return GlobalInternal::getDefaultFactory()->mergeSeeds($seed, $seed2, $more_seeds);
}

/**
Expand All @@ -123,66 +43,9 @@ public function mergeSeeds($seed, $seed2, ...$more_seeds)
*
* @param mixed $seed
* @param array $defaults
* @param string $prefix No longer supported
*/
public function factory($seed, $defaults = [], string $prefix = null): object
public function factory($seed, $defaults = []): object
{
if ($prefix !== null) { // remove in 2021-june
throw new Exception(['Factory does no longer support relative names, pass full class name without prefix']);
}

if ($defaults === null) {
$defaults = [];
}

if (!$seed) {
$seed = [];
}

if (!is_array($seed)) {
$seed = [$seed];
}

if (is_array($defaults)) {
array_unshift($defaults, null); // insert argument 0
} elseif (!is_object($defaults)) {
$defaults = [null, $defaults];
}
$seed = $this->mergeSeeds($seed, $defaults);

if (is_object($seed)) {
// setDefaults already called
return $seed;
}

$arguments = array_filter($seed, 'is_numeric', ARRAY_FILTER_USE_KEY); // with numeric keys
$injection = array_diff_key($seed, $arguments); // with string keys
$object = array_shift($arguments); // first numeric key argument is object

if (!is_object($object)) {
if (!is_string($object)) {
throw new Exception([
'Class name was not specified by the seed',
'seed' => $seed,
]);
}

$object = new $object(...$arguments);
}

if ($injection) {
if (isset($object->_DIContainerTrait)) {
$object->setDefaults($injection);
} else {
throw new Exception([
'factory() could not inject properties into new object. It does not use \atk4\core\DIContainerTrait',
'object' => $object,
'seed' => $seed,
'injection' => $injection,
]);
}
}

return $object;
return GlobalInternal::getDefaultFactory()->factory($seed, $defaults);
}
}
Loading

0 comments on commit 6491150

Please sign in to comment.