Implemented the Factory and the FactoryTest.

The Factory class is the central point for class communication in FuzeWorks. When someone needs to load, for instance, the layout class, one has to do the following:

$factory = Factory::getInstance();
$layout = $factory->layout;

The Factory class allows the user to replace dependencies on the fly. It is possible for a class to replace a dependency, like Logger, on the fly by calling the $factory->newInstance('Logger'); or the $factory->setInstance('Logger', $object); This allows for creative ways to do dependency injection, or keep classes
separated.

It is also possible to load a cloned instance of the Factory class, so that all properties are independant as well,
all to suit your very needs.
This commit is contained in:
Abel Hoogeveen 2016-05-27 16:33:12 +02:00
parent 5aa165d4a4
commit bc6136525b
9 changed files with 377 additions and 24 deletions

View File

@ -21,7 +21,7 @@ build:composer:
test:5.4:
image: php:5.4
script:
- vendor/bin/phpunit -c phpunit.xml --coverage-text
- vendor/bin/phpunit -c tests/phpunit.xml --coverage-text
cache:
key: "$CI_BUILD_REF/$CI_BUILD_REF_NAME"
paths:
@ -31,7 +31,7 @@ test:5.4:
test:5.5:
image: php:5.5
script:
- vendor/bin/phpunit -c phpunit.xml --coverage-text
- vendor/bin/phpunit -c tests/phpunit.xml --coverage-text
cache:
key: "$CI_BUILD_REF/$CI_BUILD_REF_NAME"
paths:
@ -42,7 +42,7 @@ test:5.6:
stage: test
image: php:5.6
script:
- vendor/bin/phpunit -c phpunit.xml --coverage-text
- vendor/bin/phpunit -c tests/phpunit.xml --coverage-text
cache:
key: "$CI_BUILD_REF/$CI_BUILD_REF_NAME"
paths:
@ -52,7 +52,7 @@ test:7.0:
stage: test
image: php:7.0
script:
- vendor/bin/phpunit -c phpunit.xml --coverage-text
- vendor/bin/phpunit -c tests/phpunit.xml --coverage-text
cache:
key: "$CI_BUILD_REF/$CI_BUILD_REF_NAME"
paths:
@ -64,7 +64,7 @@ release:
only:
- master
script:
- vendor/bin/phpunit -c phpunit.xml --coverage-text
- vendor/bin/phpunit -c tests/phpunit.xml --coverage-text
- vendor/bin/apigen generate -s . -d 'build/phpdoc' --todo --title 'FuzeWorks Build' --exclude 'tests/*','build/*','vendor/*','CI/*'
artifacts:
name: "${CI_BUILD_NAME}_${CI_BUILD_REF_NAME}"

View File

@ -33,17 +33,52 @@
namespace FuzeWorks;
/**
*
* Factory Class.
*
* The Factory class is the central point for class communication in FuzeWorks.
* When someone needs to load, for instance, the layout class, one has to do the following:
* $factory = Factory::getInstance();
* $layout = $factory->layout;
*
* The Factory class allows the user to replace dependencies on the fly. It is possible for a class
* to replace a dependency, like Logger, on the fly by calling the $factory->newInstance('Logger'); or the
* $factory->setInstance('Logger', $object); This allows for creative ways to do dependency injection, or keep classes
* separated.
*
* It is also possible to load a cloned instance of the Factory class, so that all properties are independant as well,
* all to suit your very needs.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
*/
class Factory
{
/**
* The Factory instance that is shared by default when calling Factory::getInstance();
*
* @var FuzeWorks\Factory Default shared instance
*/
private static $sharedFactoryInstance;
private $instances = array();
/**
* Whether to clone all Factory instances upon calling Factory::getInstance()
*
* @var bool Clone all Factory instances.
*/
protected static $cloneInstances = false;
/**
* Array of all the classes loaded by this specific instance of the Factory
*
* @var array Array of all loaded classes in THIS Factory
*/
protected $instances = array();
/**
* Factory instance constructor. Should only really be called once
* @return void
*/
public function __construct()
{
self::$sharedFactoryInstance = $this;
@ -65,16 +100,15 @@ class Factory
$this->instances['Output'] = new Output();
}
public function newInstance($className, $namespace = 'FuzeWorks\\')
{
$instanceName = ucfirst($className);
$className = $namespace.$instanceName;
$this->instances[$instanceName] = new $className();
}
public static function getInstance($cloneInstance = true)
/**
* Get a new instance of the Factory class.
*
* @param bool $cloneInstance Whether to get a cloned instance (true) or exactly the same instance (false)
* @return FuzeWorks\Factory Factory Instance
*/
public static function getInstance($cloneInstance = false)
{
if ($cloneInstance === true)
if ($cloneInstance === true || self::$cloneInstances === true)
{
return clone self::$sharedFactoryInstance;
}
@ -82,86 +116,257 @@ class Factory
return self::$sharedFactoryInstance;
}
/**
* Enable cloning all Factory instances upon calling Factory::getInstance()
*
* @return void
*/
public static function enableCloneInstances()
{
self::$cloneInstances = true;
}
/**
* Disable cloning all Factory instances upon calling Factory::getInstance()
*
* @return void
*/
public static function disableCloneInstances()
{
self::$cloneInstances = false;
}
/**
* Create a new instance of one of the loaded classes.
* It reloads the class. It does NOT clone it.
*
* @param string $className The name of the loaded class, WITHOUT the namespace
* @param string $namespace Optional namespace. Defaults to 'FuzeWorks\'
* @return FuzeWorks\Factory Factory Instance
*/
public function newInstance($className, $namespace = 'FuzeWorks\\')
{
// Determine the class to load
$instanceName = ucfirst($className);
$className = $namespace.$instanceName;
// Remove the current instance
unset($this->instances[$instanceName]);
// And set the new one
$this->instances[$instanceName] = new $className();
// Return itself
return $this;
}
/**
* Clone an instance of one of the loaded classes.
* It clones the class. It does NOT re-create it.
*
* @param string $className The name of the loaded class, WITHOUT the namespace
* @return FuzeWorks\Factory Factory Instance
*/
public function cloneInstance($className)
{
// Determine the class to load
$instanceName = ucfirst($className);
// Clone the instance
$this->instances[$instanceName] = clone $this->instances[$instanceName];
// Return itself
return $this;
}
/**
* Set an instance of one of the loaded classes with your own $object.
* Replace the existing class with one of your own.
*
* @param string $className The name of the loaded class, WITHOUT the namespace
* @param mixed $object Object to replace the class with
* @return FuzeWorks\Factory Factory Instance
*/
public function setInstance($className, $object)
{
// Determine the instance name
$instanceName = ucfirst($className);
// Unset and set
unset($this->instances[$instanceName]);
$this->instances[$instanceName] = $object;
// Return itself
return $this;
}
/**
* Remove an instance of one of the loaded classes.
*
* @param string $className The name of the loaded class, WITHOUT the namespace
* @return FuzeWorks\Factory Factory Instance
*/
public function removeInstance($className)
{
// Determine the instance name
$instanceName = ucfirst($className);
// Unset
unset($this->instances[$instanceName]);
// Return itself
return $this;
}
/**
* Get one of the loaded classes. Overloading method.
*
* @param string $objectName Name of the class to get
* @return mixed The class requested
*/
public function __get($objectName)
{
if (isset($this->instances[ucfirst($objectName)]))
{
return $this->instances[ucfirst($objectName)];
}
return null;
}
/* --------------------------------- Compatibility classes --------------------------------- */
/**
* @deprecated
*/
public function getConfig()
{
return $this->instances['Config'];
}
/**
* @deprecated
*/
public function getCore()
{
return $this->instances['Core'];
}
/**
* @deprecated
*/
public function getDatabase()
{
return $this->instances['Database'];
}
/**
* @deprecated
*/
public function getEvents()
{
return $this->instances['Events'];
}
/**
* @deprecated
*/
public function getHelpers()
{
return $this->instances['Helpers'];
}
/**
* @deprecated
*/
public function getInput()
{
return $this->instances['Input'];
}
/**
* @deprecated
*/
public function getLanguage()
{
return $this->instances['Language'];
}
/**
* @deprecated
*/
public function getLayout()
{
return $this->instances['Layout'];
}
/**
* @deprecated
*/
public function getLibraries()
{
return $this->instances['Config'];
}
/**
* @deprecated
*/
public function getLogger()
{
return $this->instances['Logger'];
}
/**
* @deprecated
*/
public function getModels()
{
return $this->instances['Models'];
}
/**
* @deprecated
*/
public function getModules()
{
return $this->instances['Modules'];
}
/**
* @deprecated
*/
public function getOutput()
{
return $this->instances['Output'];
}
/**
* @deprecated
*/
public function getRouter()
{
return $this->instances['Router'];
}
/**
* @deprecated
*/
public function getSecurity()
{
return $this->instances['Security'];
}
/**
* @deprecated
*/
public function getUri()
{
return $this->instances['URI'];
}
/**
* @deprecated
*/
public function getUtf8()
{
return $this->instances['Utf8'];

149
tests/core_factoryTest.php Normal file
View File

@ -0,0 +1,149 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks MVC PHP FrameWork
*
* Copyright (C) 2015 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://fuzeworks.techfuze.net
* @since Version 0.0.1
*
* @version Version 0.0.1
*/
use FuzeWorks\Factory;
/**
* Class FactoryTest.
*
* Will test the FuzeWorks Factory.
*/
class factoryTest extends CoreTestAbstract
{
public function testCanLoadFactory()
{
$this->assertInstanceOf('FuzeWorks\Factory', Factory::getInstance());
}
/**
* @depends testCanLoadFactory
*/
public function testLoadSameInstance()
{
$this->assertTrue(Factory::getInstance() === Factory::getInstance());
}
/**
* @depends testCanLoadFactory
*/
public function testLoadDifferentInstance()
{
// First a situation where one is the shared instance and one is a cloned instance
$this->assertFalse(Factory::getInstance() === Factory::getInstance(true));
// And a situation where both are cloned instances
$this->assertFalse(Factory::getInstance(true) === Factory::getInstance(true));
}
/**
* @depends testCanLoadFactory
*/
public function testObjectsSameInstance()
{
// Create mock
$mock = $this->getMock('MockInstance');
// Test not set
$this->assertNull(Factory::getInstance()->mock);
// Same instance factories
$factory1 = Factory::getInstance()->setInstance('Mock', $mock);
$factory2 = Factory::getInstance()->setInstance('Mock', $mock);
// Return the mocks
$this->assertTrue($factory1->mock === $factory2->mock);
// Different instance factories
$factory3 = Factory::getInstance(true)->setInstance('Mock', $mock);
$factory4 = Factory::getInstance(true)->setInstance('Mock', $mock);
// Return the mocks
$this->assertTrue($factory3->mock === $factory4->mock);
}
/**
* @depends testObjectsSameInstance
*/
public function testObjectsDifferentInstance()
{
// Create mock
$mock = $this->getMock('MockInstance');
// Test not set
$this->assertNull(Factory::getInstance()->mock);
// Same instance factories
$factory1 = Factory::getInstance()->setInstance('Mock', $mock);
$factory2 = Factory::getInstance()->setInstance('Mock', $mock);
// Clone the instance in factory2
$factory2->cloneInstance('Mock');
// Should be true, since both Factories use the same Mock instance
$this->assertTrue($factory1->mock === $factory2->mock);
// Different instance factories
$factory3 = Factory::getInstance(true)->setInstance('Mock', $mock);
$factory4 = Factory::getInstance(true)->setInstance('Mock', $mock);
// Clone the instance in factory4
$factory4->cloneInstance('Mock');
// Should be false, since both Factories use a different Mock instance
$this->assertFalse($factory3->mock === $factory4->mock);
}
public function testGlobalCloneInstance()
{
// First test without global cloning
$this->assertTrue(Factory::getInstance() === Factory::getInstance());
// Now enable global cloning
Factory::enableCloneInstances();
// Now test without global cloning
$this->assertFalse(Factory::getInstance() === Factory::getInstance());
// Disable global cloning
Factory::disableCloneInstances();
// And test again without global cloning
$this->assertTrue(Factory::getInstance() === Factory::getInstance());
}
public function tearDown()
{
Factory::getInstance()->removeInstance('Mock');
Factory::disableCloneInstances();
}
}

View File

@ -1,5 +1,5 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
bootstrap="tests/autoload.php"
bootstrap="autoload.php"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
@ -11,15 +11,14 @@
<testsuites>
<testsuite name="Core Functionality">
<directory>tests</directory>
<directory>tests/helpers</directory>
<directory>.</directory>
</testsuite>
</testsuites>
<logging>
<log type="json" target="build/phpunit/logfile.json"/>
<log type="junit" target="build/phpunit/logfile.xml" logIncompleteSkipped="false"/>
<log type="testdox-html" target="build/phpunit/testdox.html"/>
<log type="testdox-text" target="build/phpunit/testdox.txt"/>
<log type="json" target="../build/phpunit/logfile.json"/>
<log type="junit" target="../build/phpunit/logfile.xml" logIncompleteSkipped="false"/>
<log type="testdox-html" target="../build/phpunit/testdox.html"/>
<log type="testdox-text" target="../build/phpunit/testdox.txt"/>
</logging>
</phpunit>