mirror of
https://github.com/marcostoll/FF-Factories.git
synced 2025-01-16 22:28:16 +01:00
initial commit
This commit is contained in:
commit
5dac7ad856
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.idea/
|
||||
|
||||
/vendor
|
||||
|
||||
todos.md
|
36
composer.json
Normal file
36
composer.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "fastforward/factories",
|
||||
"description": "A component providing a factory pattern implementation - part of the Fast Forward Components Collection",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"minimum-stability": "stable",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marco Stoll",
|
||||
"email": "marco@fast-forward-encoding.de",
|
||||
"homepage": "http://marcostoll.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"fastforward/utils": "^1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/marcostoll/FF-Utils"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FF\\Factories\\": "src/"
|
||||
},
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
}
|
1588
composer.lock
generated
Normal file
1588
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
license.md
Normal file
22
license.md
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
===============================================================================
|
||||
|
||||
Copyright (c) 2019-forever Marco Stoll <marco@fast-forward-encoding.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
120
readme.md
Normal file
120
readme.md
Normal file
@ -0,0 +1,120 @@
|
||||
FF\Factories | Fast Forward Components Collection
|
||||
===============================================================================
|
||||
|
||||
by Marco Stoll
|
||||
|
||||
- <marco.stoll@rocketmail.com>
|
||||
- <http://marcostoll.de>
|
||||
- <https://github.com/marcostoll>
|
||||
- <https://github.com/marcostoll/FF-Common>
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# What is the Fast Forward Components Collection?
|
||||
The Fast Forward Components Collection, in short **Fast Forward** or **FF**, is a loosely coupled collection of code
|
||||
repositories each addressing common problems while building web application. Multiple **FF** components may be used
|
||||
together if desired. And some more complex **FF** components depend on other rather basic **FF** components.
|
||||
|
||||
**FF** is not a framework in and of itself and therefore should not be called so.
|
||||
But you may orchestrate multiple **FF** components to build an web application skeleton that provides the most common
|
||||
tasks.
|
||||
|
||||
# Introduction
|
||||
|
||||
The Common component provides generic functionality used by many other **FF** components. You seldom want to require
|
||||
this library by itself. In most cases you will get it by requiring one of the more complex **FF** components.
|
||||
|
||||
# The Factories
|
||||
|
||||
The `AbstractFactory` provides logic to introduce factory-style object instantiation. By configuring it with a
|
||||
designated class locator you may invoke its `create()` method with a class identifier and the necessary constructor
|
||||
arguments to get a fresh instance of this class.
|
||||
|
||||
The factory will instruct the its class locator to detect a suitable class definition and after that will invoke the
|
||||
class's constructor and return the object.
|
||||
|
||||
An example:
|
||||
|
||||
namespace MyProject\Views\Helpers;
|
||||
|
||||
use FF\Factories\AbstractFactory;
|
||||
use FF\Factories\ClassLocators\NamespaceClassLocator;
|
||||
|
||||
/**
|
||||
* Definition of Helpers Factory
|
||||
*/
|
||||
class MyHelpersFactory extends AbstractFactory
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// configure the factory with its own namespace
|
||||
// to search for Helper class
|
||||
parent::__construct(new NamespaceClassLocator(__NAMESPACE__));
|
||||
}
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
// Will get a MyViewHelper instance assumed the a MyProject\Views\Helpers\MyViewHelper class exists.
|
||||
// In this example $arg1 and $arg2 will be passed to the MyViewHelper class's constructor in the given order.
|
||||
$myHelper = $myHelpersFactory->create('MyViewHelper', $arg1, $arg2);
|
||||
|
||||
The `AbstractSingletonFactory` extends this behaviour by adding a singleton pattern to its implementation that will
|
||||
ensure that an instance of a class identified by its identifier will only be instantiated a single time.
|
||||
So any additional call to its `create()` method will return the same instance as the first time.
|
||||
|
||||
**Beware**: Only the class identifier will be taken into account for identifying already instantiated objects. Varying
|
||||
any constructor arguments will not lead to generating new object instances.
|
||||
|
||||
# The Class Locators
|
||||
|
||||
Class locators implement various strategies to derive full-qualified class names based on a shorter class identifier.
|
||||
Each class locator defines a different convention for specifying class identifiers and the logic to detect suitable
|
||||
class definitions based on this.
|
||||
|
||||
## The Namespace Class Locator
|
||||
|
||||
This class locator can be configured with a list of base namespaces ro search for class definitions with a class
|
||||
identifier relative to one of its base namespaces.
|
||||
The base namespaces wil be search in the give order. So the locator will return the first match of an existing class.
|
||||
|
||||
An example:
|
||||
|
||||
use FF\Factories\ClassLocators\NamespaceClassLocator;
|
||||
|
||||
$myLocator = new NamespaceClassLocator('MyProject\Sub1', ''MyProject\Sub2');
|
||||
|
||||
// This will find MyProject\Sub1\MyClass or MyProject\Sub2\MyClass in this order.
|
||||
// If none of the above exist, $class will be null.
|
||||
$class = $myLocator->locateClass('MyClass');
|
||||
|
||||
// This will find MyProject\Sub1\Foo\MyClass or MyProject\Sub2\Foo\MyClass in this order.
|
||||
// If none of the above exist, $class will be null.
|
||||
$class = $myLocator->locateClass('Foo\MyClass');
|
||||
|
||||
## The Namespace Prefixed Class Locator
|
||||
|
||||
Sometimes you want to distribute class definitions of a certain kind (say Event classes) over various packages. To
|
||||
provide a factory capable of creating this events objects you may use the `NamespacePrefixedClassLocator`. This
|
||||
locator enforces you to place your various class definitions within a common class namespace prefix.
|
||||
|
||||
An example:
|
||||
|
||||
use FF\Factories\ClassLocators\NamespacePrefixedClassLocator;
|
||||
|
||||
// The first argumens ('Events') is the common class namespace prefix.
|
||||
// The following argumentgs are the base namespaces to inspect.
|
||||
$myLocator = new NamespaceClassLocator('Events', MyProject\Sub1', ''MyProject\Sub2');
|
||||
|
||||
// This will find MyProject\Sub1\Events\MyEvent or MyProject\Sub2\Events\MyEven in this order.
|
||||
// If none of the above exist, $class will be null.
|
||||
$class = $myLocator->locateClass('MyEvent');
|
||||
|
||||
// This will find MyProject\Sub1\Foo\Events\MyEvent or MyProject\Sub2\Foo\Events\MyEvent in this order.
|
||||
// If none of the above exist, $class will be null.
|
||||
$class = $myLocator->locateClass('Foo\MyEvent');
|
||||
|
||||
# Road map
|
||||
|
||||
The extend of the **Factories** component is mainly driven by the needs of other **FF** components, so not concrete
|
||||
features are planned at this time. The component surely will grow as other **FF** components require additional logic
|
||||
that is not part of their domain.
|
84
src/AbstractFactory.php
Normal file
84
src/AbstractFactory.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of AbstractFactory
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Factories;
|
||||
|
||||
use FF\Factories\Exceptions\ClassNotFoundException;
|
||||
use FF\Factories\Exceptions\InstantiationException;
|
||||
use FF\Factories\ClassLocators\ClassLocatorInterface;
|
||||
|
||||
/**
|
||||
* Class AbstractFactory
|
||||
*
|
||||
* @package FF\Factories
|
||||
*/
|
||||
abstract class AbstractFactory
|
||||
{
|
||||
/**
|
||||
* @var ClassLocatorInterface
|
||||
*/
|
||||
protected $classLocator;
|
||||
|
||||
/**
|
||||
* @param ClassLocatorInterface $classLocator
|
||||
*/
|
||||
public function __construct(ClassLocatorInterface $classLocator)
|
||||
{
|
||||
$this->classLocator = $classLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object instance of a suitable class
|
||||
*
|
||||
* Any arguments provided will be passed to the class's constructor.
|
||||
* The amount and order of the $args given must match the constructor signature of the class to instantiate.
|
||||
*
|
||||
* @param string $classIdentifier
|
||||
* @param array $args
|
||||
* @return object
|
||||
* @throws ClassNotFoundException no suitable class definition found
|
||||
* @throws InstantiationException error while trying to instantiate object
|
||||
*/
|
||||
public function create(string $classIdentifier, ...$args)
|
||||
{
|
||||
$fqClassName = $this->classLocator->locateClass($classIdentifier);
|
||||
if (is_null($fqClassName)) {
|
||||
throw new ClassNotFoundException('no suitable class definition found for [' . $classIdentifier . ']');
|
||||
}
|
||||
|
||||
try {
|
||||
return (new \ReflectionClass($fqClassName))->newInstance(...$args);
|
||||
} catch (\ReflectionException $e) {
|
||||
throw new InstantiationException(
|
||||
'error while trying to instantiate object of class [' . $fqClassName . ']',
|
||||
0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassLocatorInterface
|
||||
*/
|
||||
public function getClassLocator(): ClassLocatorInterface
|
||||
{
|
||||
return $this->classLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassLocatorInterface $classLocator
|
||||
* @return AbstractFactory
|
||||
*/
|
||||
public function setClassLocator(ClassLocatorInterface $classLocator)
|
||||
{
|
||||
$this->classLocator = $classLocator;
|
||||
return $this;
|
||||
}
|
||||
}
|
55
src/AbstractSingletonFactory.php
Normal file
55
src/AbstractSingletonFactory.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of AbstractSingletonFactory
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Factories;
|
||||
|
||||
|
||||
/**
|
||||
* Class AbstractSingletonFactory
|
||||
*
|
||||
* @package FF\Factories
|
||||
*/
|
||||
abstract class AbstractSingletonFactory extends AbstractFactory
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $instanceCache = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Successfully created instances will be stored within the instance cache using their $classIdentifier as key.
|
||||
* Retrieves the object from the instance cache if already present. If not, attempts to create it.
|
||||
*/
|
||||
public function create(string $classIdentifier, ...$args)
|
||||
{
|
||||
// check instance cache first
|
||||
if (isset($this->instanceCache[$classIdentifier])) {
|
||||
return $this->instanceCache[$classIdentifier];
|
||||
}
|
||||
|
||||
$instance = parent::create($classIdentifier, ...$args);
|
||||
$this->instanceCache[$classIdentifier] = $instance;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all created instances from the cache
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function clearInstanceCache()
|
||||
{
|
||||
$this->instanceCache = [];
|
||||
return $this;
|
||||
}
|
||||
}
|
29
src/ClassLocators/ClassLocatorInterface.php
Normal file
29
src/ClassLocators/ClassLocatorInterface.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of ClassLocatorInterface
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Factories\ClassLocators;
|
||||
|
||||
/**
|
||||
* Interface ClassLocatorInterface
|
||||
*
|
||||
* @package FF\Factories\ClassLocators
|
||||
*/
|
||||
interface ClassLocatorInterface
|
||||
{
|
||||
/**
|
||||
* Locates a full-qualified class name using the given identifier
|
||||
*
|
||||
* Returns null if no suitable class found.
|
||||
*
|
||||
* @param string $classIdentifier
|
||||
* @return string|null
|
||||
*/
|
||||
public function locateClass(string $classIdentifier): ?string;
|
||||
}
|
118
src/ClassLocators/NamespaceClassLocator.php
Normal file
118
src/ClassLocators/NamespaceClassLocator.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of NamespaceClassLocator
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Factories\ClassLocators;
|
||||
|
||||
use FF\Utils\ClassUtils;
|
||||
|
||||
/**
|
||||
* Class NamespaceClassLocator
|
||||
*
|
||||
* Expects a class name relative to any of the registered namespaces as class identifier.
|
||||
*
|
||||
* Example 1:
|
||||
* Registered namespace: 'FF\Forms\Fields'
|
||||
* valid $classIdentifiers:
|
||||
* - 'TextField' => finds FF\Forms\Fields\TextField
|
||||
* - 'RadioField' => finds FF\Forms\Fields\RadioField
|
||||
*
|
||||
* Example 2:
|
||||
* Registered namespace: 'FF\Forms'
|
||||
* valid $classIdentifiers:
|
||||
* - 'Fields\TextField' => finds FF\Forms\Fields\TextField
|
||||
* - 'Fields\RadioField' => finds FF\Forms\Fields\RadioField
|
||||
*
|
||||
* "Going up" in the namespace path by using '..\\' will not work.
|
||||
*
|
||||
* @package FF\Factories\ClassLocators
|
||||
*/
|
||||
class NamespaceClassLocator implements ClassLocatorInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $namespaces = [];
|
||||
|
||||
/**
|
||||
* @param string[] $namespaces
|
||||
*/
|
||||
public function __construct(string ...$namespaces)
|
||||
{
|
||||
$this->setNamespaces($namespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates a full-qualified class name using the given identifier
|
||||
*
|
||||
* Returns null if no suitable class found.
|
||||
*
|
||||
* @param string $classIdentifier
|
||||
* @return string|null
|
||||
*/
|
||||
public function locateClass(string $classIdentifier): ?string
|
||||
{
|
||||
foreach ($this->namespaces as $ns) {
|
||||
$fqClassname = $this->buildFqClassName($ns, $classIdentifier);
|
||||
if (!class_exists($fqClassname)) continue;
|
||||
|
||||
return $fqClassname;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNamespaces(): array
|
||||
{
|
||||
return $this->namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $namespaces
|
||||
* @return $this
|
||||
*/
|
||||
public function setNamespaces(array $namespaces)
|
||||
{
|
||||
$this->normalizeNamespaces($namespaces);
|
||||
$this->namespaces = $namespaces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $namespaces
|
||||
* @return $this
|
||||
*/
|
||||
public function prependNamespaces(string ...$namespaces)
|
||||
{
|
||||
$this->normalizeNamespaces($namespaces);
|
||||
$this->namespaces = array_merge($namespaces, $this->namespaces);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ns
|
||||
* @param string $classIdentifier
|
||||
* @return string
|
||||
*/
|
||||
protected function buildFqClassName(string $ns, string $classIdentifier): string
|
||||
{
|
||||
return $ns . '\\' . $classIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $namespaces
|
||||
*/
|
||||
protected function normalizeNamespaces(array &$namespaces)
|
||||
{
|
||||
array_walk($namespaces, [ClassUtils::class, 'normalizeNamespace']);
|
||||
}
|
||||
}
|
84
src/ClassLocators/NamespacePrefixedClassLocator.php
Normal file
84
src/ClassLocators/NamespacePrefixedClassLocator.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of NamespacePrefixedClassLocator
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Factories\ClassLocators;
|
||||
|
||||
use FF\Utils\ClassUtils;
|
||||
|
||||
/**
|
||||
* Class NamespacePrefixedClassLocator
|
||||
*
|
||||
* Expects a class name prefixed by a specific path relative to any of the registered namespaces as class identifier.
|
||||
*
|
||||
* Example 1:
|
||||
* Registered namespace: 'FF\Runtime'
|
||||
* $prefix: 'Events'
|
||||
* valid $classIdentifiers:
|
||||
* - 'OnError' => finds FF\Runtime\Events\OnError
|
||||
*
|
||||
* Example 2:
|
||||
* Registered namespace: 'FF'
|
||||
* $prefix: 'Events'
|
||||
* valid $classIdentifiers:
|
||||
* - 'Runtime\OnError' => finds FF\Runtime\Events\OnError
|
||||
*
|
||||
* "Going up" in the namespace path by using '..\\' will not work.
|
||||
*
|
||||
* @package FF\Factories\ClassLocators
|
||||
*/
|
||||
class NamespacePrefixedClassLocator extends NamespaceClassLocator
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* @param string $prefix
|
||||
* @param string[] $namespaces
|
||||
*/
|
||||
public function __construct(string $prefix, string ...$namespaces)
|
||||
{
|
||||
parent::__construct(...$namespaces);
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPrefix(): string
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $prefix
|
||||
* @return $this
|
||||
*/
|
||||
public function setPrefix(string $prefix)
|
||||
{
|
||||
$this->prefix = ClassUtils::normalizeNamespace($prefix);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ns
|
||||
* @param string $classIdentifier
|
||||
* @return string
|
||||
*/
|
||||
protected function buildFqClassName(string $ns, string $classIdentifier): string
|
||||
{
|
||||
$classPath = explode('\\', ClassUtils::normalizeNamespace($classIdentifier));
|
||||
$localClassName = array_pop($classPath);
|
||||
$subNs = !empty($classPath) ? implode('\\', $classPath) . '\\' : '';
|
||||
|
||||
return $ns . '\\' . $subNs . $this->prefix . '\\' . $localClassName;
|
||||
}
|
||||
}
|
21
src/Exceptions/ClassNotFoundException.php
Normal file
21
src/Exceptions/ClassNotFoundException.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of ClassNotFoundException
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Factories\Exceptions;
|
||||
|
||||
/**
|
||||
* Class ClassNotFoundException
|
||||
*
|
||||
* @package FF\Factories\Exceptions
|
||||
*/
|
||||
class ClassNotFoundException extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
21
src/Exceptions/InstantiationException.php
Normal file
21
src/Exceptions/InstantiationException.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of InstantiationException
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Factories\Exceptions;
|
||||
|
||||
/**
|
||||
* Class InstantiationException
|
||||
*
|
||||
* @package FF\Factories\Exceptions
|
||||
*/
|
||||
class InstantiationException extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
136
tests/AbstractFactoryTest.php
Normal file
136
tests/AbstractFactoryTest.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of AbstractFactoryTest
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Tests\Factories
|
||||
{
|
||||
use FF\Factories\AbstractFactory;
|
||||
use FF\Factories\ClassLocators\NamespaceClassLocator;
|
||||
use FF\Factories\Exceptions\ClassNotFoundException;
|
||||
use FF\Tests\Factories\Sub\MySubObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test AbstractFactoryTest
|
||||
*
|
||||
* @package FF\Tests
|
||||
*/
|
||||
class AbstractFactoryTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var MyFactory
|
||||
*/
|
||||
protected $uut;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->uut = new MyFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testSetGetClassLocator()
|
||||
{
|
||||
$value = new NamespaceClassLocator();
|
||||
$same = $this->uut->setClassLocator($value);
|
||||
$this->assertSame($this->uut, $same);
|
||||
$this->assertEquals($value, $this->uut->getClassLocator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testCreate()
|
||||
{
|
||||
/** @var MyObject $obj */
|
||||
$obj = $this->uut->create('MyObject', 'foo', true);
|
||||
$this->assertInstanceOf(MyObject::class, $obj);
|
||||
$this->assertEquals('foo', $obj->arg1);
|
||||
$this->assertTrue($obj->arg2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testCreateSubNamespace()
|
||||
{
|
||||
/** @var MySubObject $obj */
|
||||
$obj = $this->uut->create('Sub\\MySubObject', 'foo', true);
|
||||
$this->assertInstanceOf(MySubObject::class, $obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testCreateWithInsufficientArgs()
|
||||
{
|
||||
$this->expectException(\ArgumentCountError::class);
|
||||
|
||||
$this->uut->create('MyObject', 'foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testCreateUnknown()
|
||||
{
|
||||
$this->expectException(ClassNotFoundException::class);
|
||||
|
||||
$this->uut->create('foo');
|
||||
}
|
||||
}
|
||||
|
||||
class MyFactory extends AbstractFactory
|
||||
{
|
||||
/**
|
||||
* Set to protected to prevent client instantiation.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(new NamespaceClassLocator(__NAMESPACE__));
|
||||
}
|
||||
}
|
||||
|
||||
class MyObject
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $arg1;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $arg2;
|
||||
|
||||
/**
|
||||
* @param string $arg1
|
||||
* @param bool $arg2
|
||||
*/
|
||||
public function __construct(string $arg1, bool $arg2)
|
||||
{
|
||||
$this->arg1 = $arg1;
|
||||
$this->arg2 = $arg2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace FF\Tests\Factories\Sub {
|
||||
|
||||
use FF\Tests\Factories\MyObject;
|
||||
|
||||
class MySubObject extends MyObject
|
||||
{
|
||||
|
||||
}
|
||||
}
|
80
tests/AbstractSingletonFactoryTest.php
Normal file
80
tests/AbstractSingletonFactoryTest.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of AbstractSingletonFactoryTest
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Tests\Factories;
|
||||
|
||||
use FF\Factories\AbstractSingletonFactory;
|
||||
use FF\Factories\ClassLocators\NamespaceClassLocator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test AbstractSingletonFactoryTest
|
||||
*
|
||||
* @package FF\Tests
|
||||
*/
|
||||
class AbstractSingletonFactoryTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var MySingletonFactory
|
||||
*/
|
||||
protected $uut;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->uut = new MySingletonFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testCreateSingleton()
|
||||
{
|
||||
/** @var MySingletonObject $obj1 */
|
||||
$obj1 = $this->uut->create('MySingletonObject');
|
||||
/** @var MySingletonObject $obj2 */
|
||||
$obj2 = $this->uut->create('MySingletonObject');
|
||||
$this->assertSame($obj1, $obj2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testClearInstanceCache()
|
||||
{
|
||||
/** @var MySingletonObject $obj1 */
|
||||
$obj1 = $this->uut->create('MySingletonObject');
|
||||
|
||||
$same = $this->uut->clearInstanceCache();
|
||||
$this->assertSame($this->uut, $same);
|
||||
|
||||
/** @var MySingletonObject $obj2 */
|
||||
$obj2 = $this->uut->create('MySingletonObject');
|
||||
$this->assertNotSame($obj1, $obj2);
|
||||
}
|
||||
}
|
||||
|
||||
class MySingletonFactory extends AbstractSingletonFactory
|
||||
{
|
||||
/**
|
||||
* Set to protected to prevent client instantiation.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(new NamespaceClassLocator(__NAMESPACE__));
|
||||
}
|
||||
}
|
||||
|
||||
class MySingletonObject
|
||||
{
|
||||
|
||||
}
|
118
tests/ClassLocators/NamespaceClassLocatorTest.php
Normal file
118
tests/ClassLocators/NamespaceClassLocatorTest.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of NamespaceClassLocatorTest
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Tests\Factories\ClassLocators
|
||||
{
|
||||
use FF\Factories\ClassLocators\NamespaceClassLocator;
|
||||
use FF\Tests\Factories\ClassLocators\Sub\MySubObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test NamespaceClassLocatorTest
|
||||
*
|
||||
* @package FF\Tests
|
||||
*/
|
||||
class NamespaceClassLocatorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var NamespaceClassLocator
|
||||
*/
|
||||
protected $uut;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $namespaceCache = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->uut = new NamespaceClassLocator(__NAMESPACE__);
|
||||
$this->namespaceCache = $this->uut->getNamespaces();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->uut->setNamespaces($this->namespaceCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testSetGetNamespaces()
|
||||
{
|
||||
$value = ['foo', 'bar'];
|
||||
$same = $this->uut->setNamespaces($value);
|
||||
$this->assertSame($this->uut, $same);
|
||||
$this->assertEquals($value, $this->uut->getNamespaces());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testPrependNamespaces()
|
||||
{
|
||||
$same = $this->uut->setNamespaces(['foo', 'bar'])->prependNamespaces('baz');
|
||||
$this->assertSame($this->uut, $same);
|
||||
$this->assertEquals('baz', $this->uut->getNamespaces()[0]);
|
||||
$this->assertEquals(3, count($this->uut->getNamespaces()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testLocateClass()
|
||||
{
|
||||
$result = $this->uut->locateClass('MyObject');
|
||||
$this->assertEquals(MyObject::class, $result);
|
||||
|
||||
$result = $this->uut->locateClass('Sub\MySubObject');
|
||||
$this->assertEquals(MySubObject::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testLocateClassNotFound()
|
||||
{
|
||||
$result = $this->uut->locateClass('Foo');
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testLocateClassNotFoundUpFailure()
|
||||
{
|
||||
$result = $this->uut->locateClass('Sub\\..\\MyObject');
|
||||
$this->assertNull($result);
|
||||
}
|
||||
}
|
||||
|
||||
class MyObject
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace FF\Tests\Factories\ClassLocators\Sub {
|
||||
|
||||
use FF\Tests\Factories\ClassLocators\MyObject;
|
||||
|
||||
class MySubObject extends MyObject
|
||||
{
|
||||
|
||||
}
|
||||
}
|
95
tests/ClassLocators/NamespacePrefixedClassLocatorTest.php
Normal file
95
tests/ClassLocators/NamespacePrefixedClassLocatorTest.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* Definition of NamespacePrefixedClassLocatorTest
|
||||
*
|
||||
* @author Marco Stoll <marco@fast-forward-encoding.de>
|
||||
* @copyright 2019-forever Marco Stoll
|
||||
* @filesource
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FF\Tests\Factories\ClassLocators
|
||||
{
|
||||
use FF\Factories\ClassLocators\NamespacePrefixedClassLocator;
|
||||
use FF\Tests\Factories\ClassLocators\Foo\MyFooObject;
|
||||
use FF\Tests\Factories\ClassLocators\Sub\Foo\MySubFooObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test NamespacePrefixedClassLocatorTest
|
||||
*
|
||||
* @package FF\Tests
|
||||
*/
|
||||
class NamespacePrefixedClassLocatorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var NamespacePrefixedClassLocator
|
||||
*/
|
||||
protected $uut;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->uut = new NamespacePrefixedClassLocator('Foo', __NAMESPACE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testSetGetPrefix()
|
||||
{
|
||||
$value = 'foo';
|
||||
$same = $this->uut->setPrefix($value);
|
||||
$this->assertSame($this->uut, $same);
|
||||
$this->assertEquals($value, $this->uut->getPrefix());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testLocateClass()
|
||||
{
|
||||
$result = $this->uut->locateClass('MyFooObject');
|
||||
$this->assertEquals(MyFooObject::class, $result);
|
||||
|
||||
$result = $this->uut->locateClass('Sub\MySubFooObject');
|
||||
$this->assertEquals(MySubFooObject::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testLocateClassNotFound()
|
||||
{
|
||||
$result = $this->uut->locateClass('Foo');
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the namesake method/feature
|
||||
*/
|
||||
public function testLocateClassNotFoundUpFailure()
|
||||
{
|
||||
$result = $this->uut->locateClass('Sub\\..\\MyFooObject');
|
||||
$this->assertNull($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace FF\Tests\Factories\ClassLocators\Foo {
|
||||
|
||||
class MyFooObject
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace FF\Tests\Factories\ClassLocators\Sub\Foo {
|
||||
|
||||
class MySubFooObject
|
||||
{
|
||||
|
||||
}
|
||||
}
|
8
tests/testsuite.xml
Normal file
8
tests/testsuite.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit>
|
||||
<testsuites>
|
||||
<testsuite name="FastForward">
|
||||
<directory>./</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
Loading…
x
Reference in New Issue
Block a user