mirror of
https://github.com/DesignPatternsPHP/DesignPatternsPHP.git
synced 2025-08-09 00:16:32 +02:00
start a restructure
This commit is contained in:
25
Structural/Adapter/Book.php
Normal file
25
Structural/Adapter/Book.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Adapter;
|
||||
|
||||
/**
|
||||
* Book is a concrete and standard paper book
|
||||
*/
|
||||
class Book implements PaperBookInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function turnPage()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
43
Structural/Adapter/EBookAdapter.php
Normal file
43
Structural/Adapter/EBookAdapter.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Adapter;
|
||||
|
||||
/**
|
||||
* EBookAdapter is an adapter to fit an e-book like a paper book
|
||||
*
|
||||
* This is the adapter here. Notice it implements PaperBookInterface,
|
||||
* therefore you don't have to change the code of the client which using paper book.
|
||||
*/
|
||||
class EBookAdapter implements PaperBookInterface
|
||||
{
|
||||
/**
|
||||
* @var EBookInterface
|
||||
*/
|
||||
protected $eBook;
|
||||
|
||||
/**
|
||||
* Notice the constructor, it "wraps" an electronic book
|
||||
*
|
||||
* @param EBookInterface $ebook
|
||||
*/
|
||||
public function __construct(EBookInterface $ebook)
|
||||
{
|
||||
$this->eBook = $ebook;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class makes the proper translation from one interface to another
|
||||
*/
|
||||
public function open()
|
||||
{
|
||||
$this->eBook->pressStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* turns pages
|
||||
*/
|
||||
public function turnPage()
|
||||
{
|
||||
$this->eBook->pressNext();
|
||||
}
|
||||
}
|
23
Structural/Adapter/EBookInterface.php
Normal file
23
Structural/Adapter/EBookInterface.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Adapter;
|
||||
|
||||
/**
|
||||
* EBookInterface is a contract for an electronic book
|
||||
*/
|
||||
interface EBookInterface
|
||||
{
|
||||
/**
|
||||
* go to next page
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function pressNext();
|
||||
|
||||
/**
|
||||
* start the book
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function pressStart();
|
||||
}
|
25
Structural/Adapter/Kindle.php
Normal file
25
Structural/Adapter/Kindle.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Adapter;
|
||||
|
||||
/**
|
||||
* Kindle is a concrete electronic book
|
||||
*/
|
||||
class Kindle implements EBookInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function pressNext()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function pressStart()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
23
Structural/Adapter/PaperBookInterface.php
Normal file
23
Structural/Adapter/PaperBookInterface.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Adapter;
|
||||
|
||||
/**
|
||||
* PaperBookInterface is a contract for a book
|
||||
*/
|
||||
interface PaperBookInterface
|
||||
{
|
||||
/**
|
||||
* method to turn pages
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function turnPage();
|
||||
|
||||
/**
|
||||
* method to open the book
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function open();
|
||||
}
|
10
Structural/Adapter/README.md
Normal file
10
Structural/Adapter/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Adapter / Wrapper
|
||||
|
||||
## Purpose
|
||||
|
||||
To translate one interface for a class into a compatible interface. An adapter allows classes to work together that normally could not because of incompatible interfaces by providing it's interface to clients while using the original interface.
|
||||
|
||||
## Examples
|
||||
|
||||
* DB Client libraries adapter
|
||||
* using multiple different webservices and adapters normalize data so that the outcome is the same for all
|
41
Structural/Adapter/Test/AdapterTest.php
Normal file
41
Structural/Adapter/Test/AdapterTest.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Tests\Adapter;
|
||||
|
||||
use DesignPatterns\Adapter\EBookAdapter;
|
||||
use DesignPatterns\Adapter\Kindle;
|
||||
use DesignPatterns\Adapter\PaperBookInterface;
|
||||
use DesignPatterns\Adapter\Book;
|
||||
|
||||
/**
|
||||
* AdapterTest shows the use of an adapted e-book that behave like a book
|
||||
* You don't have to change the code of your client
|
||||
*/
|
||||
class AdapterTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getBook()
|
||||
{
|
||||
return array(
|
||||
array(new Book()),
|
||||
// we build a "wrapped" electronic book in the adapter
|
||||
array(new EBookAdapter(new Kindle()))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This client only knows paper book but surprise, surprise, the second book
|
||||
* is in fact an electronic book, but both work the same way
|
||||
*
|
||||
* @param PaperBookInterface $book
|
||||
*
|
||||
* @dataProvider getBook
|
||||
*/
|
||||
public function test_I_am_an_old_Client(PaperBookInterface $book)
|
||||
{
|
||||
$book->open();
|
||||
$book->turnPage();
|
||||
}
|
||||
}
|
44
Structural/Composite/Form.php
Normal file
44
Structural/Composite/Form.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Composite;
|
||||
|
||||
/**
|
||||
* The composite node MUST extend the component contract. This is mandatory for building
|
||||
* a tree of components.
|
||||
*/
|
||||
class Form extends FormElement
|
||||
{
|
||||
/**
|
||||
* @var array|FormElement[]
|
||||
*/
|
||||
protected $elements;
|
||||
|
||||
/**
|
||||
* runs through all elements and calls render() on them, then returns the complete representation
|
||||
* of the form
|
||||
*
|
||||
* from the outside, one will not see this and the form will act like a single object instance
|
||||
*
|
||||
* @param int $indent
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render($indent = 0)
|
||||
{
|
||||
$formCode = '';
|
||||
|
||||
foreach ($this->elements as $element) {
|
||||
$formCode .= $element->render($indent + 1) . PHP_EOL;
|
||||
}
|
||||
|
||||
return $formCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormElement $element
|
||||
*/
|
||||
public function addElement(FormElement $element)
|
||||
{
|
||||
$this->elements[] = $element;
|
||||
}
|
||||
}
|
18
Structural/Composite/FormElement.php
Normal file
18
Structural/Composite/FormElement.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Composite;
|
||||
|
||||
/**
|
||||
* Class FormElement
|
||||
*/
|
||||
abstract class FormElement
|
||||
{
|
||||
/**
|
||||
* renders the elements' code
|
||||
*
|
||||
* @param int $indent
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function render($indent = 0);
|
||||
}
|
21
Structural/Composite/InputElement.php
Normal file
21
Structural/Composite/InputElement.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Composite;
|
||||
|
||||
/**
|
||||
* Class InputElement
|
||||
*/
|
||||
class InputElement extends FormElement
|
||||
{
|
||||
/**
|
||||
* renders the input element HTML
|
||||
*
|
||||
* @param int $indent
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function render($indent = 0)
|
||||
{
|
||||
return str_repeat(' ', $indent) . '<input type="text" />';
|
||||
}
|
||||
}
|
12
Structural/Composite/README.md
Normal file
12
Structural/Composite/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Composite
|
||||
|
||||
# Purpose
|
||||
|
||||
To treat a group of objects the same way as a single instance of the object.
|
||||
|
||||
# Examples
|
||||
|
||||
* a form class instance handles all its form elements like a single instance of the form, when `render()` is called, it
|
||||
subsequently runs trough all its child elements and calls `render()` on them
|
||||
* `Zend_Config`: a tree of configuration options, each one is a `Zend_Config` object itself
|
||||
|
34
Structural/Composite/Test/FormTest.php
Normal file
34
Structural/Composite/Test/FormTest.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Test\Composite;
|
||||
|
||||
use DesignPatterns\Composite;
|
||||
|
||||
/**
|
||||
* FormTest tests the composite pattern on Form
|
||||
*/
|
||||
class FormTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testRender()
|
||||
{
|
||||
$form = new Composite\Form();
|
||||
$form->addElement(new Composite\TextElement());
|
||||
$form->addElement(new Composite\InputElement());
|
||||
$embed = new Composite\Form();
|
||||
$embed->addElement(new Composite\TextElement());
|
||||
$embed->addElement(new Composite\InputElement());
|
||||
$form->addElement($embed); // here we have a embedded form (like SF2 does)
|
||||
|
||||
$this->assertRegExp('#^\s{4}#m', $form->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* The all point of this pattern, a Composite must inherit from the node
|
||||
* if you want to builld trees
|
||||
*/
|
||||
public function testFormImplementsFormEelement()
|
||||
{
|
||||
$this->assertTrue(is_subclass_of('DesignPatterns\Composite\Form', 'DesignPatterns\Composite\FormElement'));
|
||||
}
|
||||
}
|
21
Structural/Composite/TextElement.php
Normal file
21
Structural/Composite/TextElement.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Composite;
|
||||
|
||||
/**
|
||||
* Class TextElement
|
||||
*/
|
||||
class TextElement extends FormElement
|
||||
{
|
||||
/**
|
||||
* renders the text element
|
||||
*
|
||||
* @param int $indent
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function render($indent = 0)
|
||||
{
|
||||
return str_repeat(' ', $indent) . 'this is a text element';
|
||||
}
|
||||
}
|
18
Structural/DataMapper/README.md
Normal file
18
Structural/DataMapper/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Data Mapper
|
||||
|
||||
## Purpose
|
||||
|
||||
A Data Mapper, is a Data Access Layer that performs bidirectional transfer of
|
||||
data between a persistent data store (often a relational database) and an in
|
||||
memory data representation (the domain layer). The goal of the pattern is to
|
||||
keep the in memory representation and the persistent data store independent of
|
||||
each other and the data mapper itself. The layer is composed of one or more
|
||||
mappers (or Data Access Objects), performing the data transfer. Mapper
|
||||
implementations vary in scope. Generic mappers will handle many different domain
|
||||
entity types, dedicated mappers will handle one or a few.
|
||||
|
||||
The key point of this pattern is, unlike Active Record pattern, the data model follows Single Responsibility Principle.
|
||||
|
||||
## Examples
|
||||
|
||||
* DB Object Relational Mapper (ORM) : Doctrine2 uses DAO named as "EntityRepository"
|
105
Structural/DataMapper/Test/UserMapperTest.php
Normal file
105
Structural/DataMapper/Test/UserMapperTest.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Test\DataMapper;
|
||||
|
||||
use DesignPatterns\DataMapper\UserMapper;
|
||||
use DesignPatterns\DataMapper\User;
|
||||
|
||||
/**
|
||||
* UserMapperTest tests the datamapper pattern
|
||||
*/
|
||||
class UserMapperTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var UserMapper
|
||||
*/
|
||||
protected $mapper;
|
||||
|
||||
/**
|
||||
* @var DBAL
|
||||
*/
|
||||
protected $dbal;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->dbal = $this->getMockBuilder('DesignPatterns\DataMapper\DBAL')
|
||||
->disableAutoload()
|
||||
->setMethods(array('insert', 'update', 'find', 'findAll'))
|
||||
->getMock();
|
||||
|
||||
$this->mapper = new UserMapper($this->dbal);
|
||||
}
|
||||
|
||||
public function getNewUser()
|
||||
{
|
||||
return array(array(new User(null, 'Odysseus', 'Odysseus@ithaca.gr')));
|
||||
}
|
||||
|
||||
public function getExistingUser()
|
||||
{
|
||||
return array(array(new User(1, 'Odysseus', 'Odysseus@ithaca.gr')));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getNewUser
|
||||
*/
|
||||
public function testPersistNew(User $user)
|
||||
{
|
||||
$this->dbal->expects($this->once())
|
||||
->method('insert');
|
||||
$this->mapper->save($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getExistingUser
|
||||
*/
|
||||
public function testPersistExisting(User $user)
|
||||
{
|
||||
$this->dbal->expects($this->once())
|
||||
->method('update');
|
||||
$this->mapper->save($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getExistingUser
|
||||
*/
|
||||
public function testRestoreOne(User $existing)
|
||||
{
|
||||
$rows = new \ArrayIterator(array(array('userid' => 1, 'username' => 'Odysseus', 'email' => 'Odysseus@ithaca.gr')));
|
||||
$this->dbal->expects($this->once())
|
||||
->method('find')
|
||||
->with(1)
|
||||
->will($this->returnValue($rows));
|
||||
|
||||
$user = $this->mapper->findById(1);
|
||||
$this->assertEquals($existing, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getExistingUser
|
||||
*/
|
||||
public function testRestoreMulti(User $existing)
|
||||
{
|
||||
$rows = array(array('userid' => 1, 'username' => 'Odysseus', 'email' => 'Odysseus@ithaca.gr'));
|
||||
$this->dbal->expects($this->once())
|
||||
->method('findAll')
|
||||
->will($this->returnValue($rows));
|
||||
|
||||
$user = $this->mapper->findAll();
|
||||
$this->assertEquals(array($existing), $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
* @expectedExceptionMessage User #404 not found
|
||||
*/
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->dbal->expects($this->once())
|
||||
->method('find')
|
||||
->with(404)
|
||||
->will($this->returnValue(array()));
|
||||
|
||||
$user = $this->mapper->findById(404);
|
||||
}
|
||||
}
|
89
Structural/DataMapper/User.php
Normal file
89
Structural/DataMapper/User.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\DataMapper;
|
||||
|
||||
/**
|
||||
* DataMapper pattern
|
||||
*
|
||||
* This is our representation of a DataBase record in the memory (Entity)
|
||||
*
|
||||
* Validation would also go in this object
|
||||
*
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $userId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $email;
|
||||
|
||||
/**
|
||||
* @param null $id
|
||||
* @param null $username
|
||||
* @param null $email
|
||||
*/
|
||||
public function __construct($id = null, $username = null, $email = null)
|
||||
{
|
||||
$this->userId = $id;
|
||||
$this->username = $username;
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getUserId()
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
*/
|
||||
public function setUserID($userId)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*/
|
||||
public function setEmail($email)
|
||||
{
|
||||
$this->email = $email;
|
||||
}
|
||||
}
|
107
Structural/DataMapper/UserMapper.php
Normal file
107
Structural/DataMapper/UserMapper.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\DataMapper;
|
||||
|
||||
/**
|
||||
* class UserMapper
|
||||
*/
|
||||
class UserMapper
|
||||
{
|
||||
/**
|
||||
* @var DBAL
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @param DBAL $dbLayer
|
||||
*/
|
||||
public function __construct(DBAL $dbLayer)
|
||||
{
|
||||
$this->adapter = $dbLayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* saves a user object from memory to Database
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function save(User $user)
|
||||
{
|
||||
/* $data keys should correspond to valid Table columns on the Database */
|
||||
$data = array(
|
||||
'userid' => $user->getUserId(),
|
||||
'username' => $user->getUsername(),
|
||||
'email' => $user->getEmail(),
|
||||
);
|
||||
|
||||
/* if no ID specified create new user else update the one in the Database */
|
||||
if (null === ($id = $user->getUserId())) {
|
||||
unset($data['userid']);
|
||||
$this->adapter->insert($data);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$this->adapter->update($data, array('userid = ?' => $id));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* finds a user from Database based on ID and returns a User object located
|
||||
* in memory
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @return User
|
||||
*/
|
||||
public function findById($id)
|
||||
{
|
||||
$result = $this->adapter->find($id);
|
||||
|
||||
if (0 == count($result)) {
|
||||
throw new \InvalidArgumentException("User #$id not found");
|
||||
}
|
||||
$row = $result->current();
|
||||
|
||||
return $this->mapObject($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches an array from Database and returns an array of User objects
|
||||
* located in memory
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findAll()
|
||||
{
|
||||
$resultSet = $this->adapter->findAll();
|
||||
$entries = array();
|
||||
|
||||
foreach ($resultSet as $row) {
|
||||
$entries[] = $this->mapObject($row);
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a table row to an object
|
||||
*
|
||||
* @param array $row
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
protected function mapObject(array $row)
|
||||
{
|
||||
$entry = new User();
|
||||
$entry->setUserID($row['userid']);
|
||||
$entry->setUsername($row['username']);
|
||||
$entry->setEmail($row['email']);
|
||||
|
||||
return $entry;
|
||||
}
|
||||
}
|
31
Structural/Decorator/Decorator.php
Normal file
31
Structural/Decorator/Decorator.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Decorator;
|
||||
|
||||
/**
|
||||
* the Decorator MUST implement the RendererInterface contract, this is the key-feature
|
||||
* of this design pattern. If not, this is no longer a Decorator but just a dumb
|
||||
* wrapper.
|
||||
*/
|
||||
|
||||
/**
|
||||
* class Decorator
|
||||
*/
|
||||
abstract class Decorator implements RendererInterface
|
||||
{
|
||||
/**
|
||||
* @var RendererInterface
|
||||
*/
|
||||
protected $wrapped;
|
||||
|
||||
/**
|
||||
* You must type-hint the wrapped component :
|
||||
* It ensures you can call renderData() in the subclasses !
|
||||
*
|
||||
* @param RendererInterface $wrappable
|
||||
*/
|
||||
public function __construct(RendererInterface $wrappable)
|
||||
{
|
||||
$this->wrapped = $wrappable;
|
||||
}
|
||||
}
|
10
Structural/Decorator/README.md
Normal file
10
Structural/Decorator/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Decorator
|
||||
|
||||
## Purpose
|
||||
|
||||
To dynamically add new functionality to class instances.
|
||||
|
||||
## Examples
|
||||
|
||||
* Zend Framework: decorators for `Zend_Form_Element` instances
|
||||
* Web Service Layer: Decorators JSON and XML for a REST service (in this case, only one of these should be allowed of course)
|
21
Structural/Decorator/RenderInJson.php
Normal file
21
Structural/Decorator/RenderInJson.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Decorator;
|
||||
|
||||
/**
|
||||
* Class RenderInJson
|
||||
*/
|
||||
class RenderInJson extends Decorator
|
||||
{
|
||||
/**
|
||||
* render data as JSON
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function renderData()
|
||||
{
|
||||
$output = $this->wrapped->renderData();
|
||||
|
||||
return json_encode($output);
|
||||
}
|
||||
}
|
29
Structural/Decorator/RenderInXml.php
Normal file
29
Structural/Decorator/RenderInXml.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Decorator;
|
||||
|
||||
/**
|
||||
* Class RenderInXml
|
||||
*/
|
||||
class RenderInXml extends Decorator
|
||||
{
|
||||
/**
|
||||
* render data as XML
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function renderData()
|
||||
{
|
||||
$output = $this->wrapped->renderData();
|
||||
|
||||
// do some fancy conversion to xml from array ...
|
||||
|
||||
$doc = new \DOMDocument();
|
||||
|
||||
foreach ($output as $key => $val) {
|
||||
$doc->appendChild($doc->createElement($key, $val));
|
||||
}
|
||||
|
||||
return $doc->saveXML();
|
||||
}
|
||||
}
|
16
Structural/Decorator/RendererInterface.php
Normal file
16
Structural/Decorator/RendererInterface.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Decorator;
|
||||
|
||||
/**
|
||||
* Class RendererInterface
|
||||
*/
|
||||
interface RendererInterface
|
||||
{
|
||||
/**
|
||||
* render data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function renderData();
|
||||
}
|
63
Structural/Decorator/Test/DecoratorTest.php
Normal file
63
Structural/Decorator/Test/DecoratorTest.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Tests\Decorator;
|
||||
|
||||
use DesignPatterns\Decorator;
|
||||
|
||||
/**
|
||||
* DecoratorTest tests the decorator pattern
|
||||
*/
|
||||
class DecoratorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
protected $service;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->service = new Decorator\Webservice(array('foo' => 'bar'));
|
||||
}
|
||||
|
||||
public function testJsonDecorator()
|
||||
{
|
||||
// Wrap service with a JSON decorator for renderers
|
||||
$service = new Decorator\RenderInJson($this->service);
|
||||
// Our Renderer will now output JSON instead of an array
|
||||
$this->assertEquals('{"foo":"bar"}', $service->renderData());
|
||||
}
|
||||
|
||||
public function testXmlDecorator()
|
||||
{
|
||||
// Wrap service with a JSON decorator for renderers
|
||||
$service = new Decorator\RenderInXml($this->service);
|
||||
// Our Renderer will now output XML instead of an array
|
||||
$this->assertXmlStringEqualsXmlString('<?xml version="1.0"?><foo>bar</foo>', $service->renderData());
|
||||
}
|
||||
|
||||
/**
|
||||
* The first key-point of this pattern :
|
||||
*/
|
||||
public function testDecoratorMustImplementsRenderer()
|
||||
{
|
||||
$this->assertTrue(is_subclass_of('DesignPatterns\Decorator\Decorator', 'DesignPatterns\Decorator\RendererInterface'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Second key-point of this pattern : the decorator is type-hinted
|
||||
*
|
||||
* @expectedException \PHPUnit_Framework_Error
|
||||
*/
|
||||
public function testDecoratorTypeHinted()
|
||||
{
|
||||
$this->getMockForAbstractClass('DesignPatterns\Decorator\Decorator', array(new \stdClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* The decorator implements and wraps the same interface
|
||||
*/
|
||||
public function testDecoratorOnlyAcceptRenderer()
|
||||
{
|
||||
$mock = $this->getMock('DesignPatterns\Decorator\RendererInterface');
|
||||
$dec = $this->getMockForAbstractClass('DesignPatterns\Decorator\Decorator', array($mock));
|
||||
$this->assertNotNull($dec);
|
||||
}
|
||||
}
|
30
Structural/Decorator/Webservice.php
Normal file
30
Structural/Decorator/Webservice.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Decorator;
|
||||
|
||||
/**
|
||||
* Class Webservice
|
||||
*/
|
||||
class Webservice implements RendererInterface
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function renderData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
19
Structural/DependencyInjection/AbstractConfig.php
Normal file
19
Structural/DependencyInjection/AbstractConfig.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\DependencyInjection;
|
||||
|
||||
/**
|
||||
* class AbstractConfig
|
||||
*/
|
||||
abstract class AbstractConfig
|
||||
{
|
||||
/**
|
||||
* @var Storage of data
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
public function __construct($storage)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
}
|
||||
}
|
36
Structural/DependencyInjection/ArrayConfig.php
Normal file
36
Structural/DependencyInjection/ArrayConfig.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\DependencyInjection;
|
||||
|
||||
/**
|
||||
* class ArrayConfig
|
||||
*
|
||||
* uses array as data source
|
||||
*/
|
||||
class ArrayConfig extends AbstractConfig implements Parameters
|
||||
{
|
||||
/**
|
||||
* Get parameter
|
||||
*
|
||||
* @param string|int $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null) {
|
||||
if (isset($this->storage[$key])) {
|
||||
return $this->storage[$key];
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameter
|
||||
*
|
||||
* @param string|int $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->storage[$key] = $value;
|
||||
}
|
||||
}
|
49
Structural/DependencyInjection/Connection.php
Normal file
49
Structural/DependencyInjection/Connection.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\DependencyInjection;
|
||||
|
||||
/**
|
||||
* Class Connection
|
||||
*/
|
||||
class Connection
|
||||
{
|
||||
/**
|
||||
* @var Configuration
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @var Currently connected host
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* @param Parameters $config
|
||||
*/
|
||||
public function __construct(Parameters $config)
|
||||
{
|
||||
$this->configuration = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* connection using the injected config
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
$host = $this->configuration->get('host');
|
||||
// connection to host, authentication etc...
|
||||
|
||||
//if connected
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get currently connected host
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
}
|
26
Structural/DependencyInjection/Parameters.php
Normal file
26
Structural/DependencyInjection/Parameters.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\DependencyInjection;
|
||||
|
||||
/**
|
||||
* Parameters interface
|
||||
*/
|
||||
interface Parameters
|
||||
{
|
||||
/**
|
||||
* Get parameter
|
||||
*
|
||||
* @param string|int $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* Set parameter
|
||||
*
|
||||
* @param string|int $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set($key, $value);
|
||||
}
|
16
Structural/DependencyInjection/README.md
Normal file
16
Structural/DependencyInjection/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Dependency Injection
|
||||
|
||||
## Purpose
|
||||
|
||||
To implement a loosely coupled architecture in order to get better testable, maintainable and extendable code.
|
||||
|
||||
## Usage
|
||||
|
||||
Configuration gets injected and `Connection` will get all that it needs from Configuration Without DI, the configuration would be created directly in Connection, which is not very good for testing and extending `Connection`.
|
||||
|
||||
Notice we are following Inversion of control principle in `Connection` by asking `$config` to implement `Parameters` interface. This decouples our components. We don't care where the source of information comes from, we only care that config has certain methods to retrieve that information. Read more about Inversion of control [here](http://en.wikipedia.org/wiki/Inversion_of_control).
|
||||
|
||||
## Examples
|
||||
|
||||
* the Doctrine2 ORM uses dependency injection e.g. for Configuration that is injected into a Connection object. for testing purposes, one can easily create a mock object of the configuration and inject that into the connection object
|
||||
* Symfony and Zend Framework 2 already have containers for DI that create objects via a configuration array and inject them where needed (i.e. in Controllers)
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Tests\DependencyInjection;
|
||||
|
||||
use DesignPatterns\DependencyInjection\Parameters;
|
||||
use DesignPatterns\DependencyInjection\AbstractConfig;
|
||||
use DesignPatterns\DependencyInjection\ArrayConfig;
|
||||
use DesignPatterns\DependencyInjection\Connection;
|
||||
|
||||
class DependencyInjectionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected $config;
|
||||
protected $source;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->source = include 'config.php';
|
||||
$this->config = new ArrayConfig($this->source);
|
||||
}
|
||||
|
||||
public function testDependencyInjection()
|
||||
{
|
||||
$connection = new Connection($this->config);
|
||||
$connection->connect();
|
||||
$this->assertEquals($this->source['host'], $connection->getHost());
|
||||
}
|
||||
}
|
3
Structural/DependencyInjection/Test/config.php
Normal file
3
Structural/DependencyInjection/Test/config.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
return array('host' => 'github.com');
|
31
Structural/Facade/BiosInterface.php
Normal file
31
Structural/Facade/BiosInterface.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Facade;
|
||||
|
||||
/**
|
||||
* Class BiosInterface
|
||||
*/
|
||||
interface BiosInterface
|
||||
{
|
||||
/**
|
||||
* execute the BIOS
|
||||
*/
|
||||
public function execute();
|
||||
|
||||
/**
|
||||
* wait for halt
|
||||
*/
|
||||
public function waitForKeyPress();
|
||||
|
||||
/**
|
||||
* launches the OS
|
||||
*
|
||||
* @param OsInterface $os
|
||||
*/
|
||||
public function launch(OsInterface $os);
|
||||
|
||||
/**
|
||||
* power down BIOS
|
||||
*/
|
||||
public function powerDown();
|
||||
}
|
52
Structural/Facade/Facade.php
Normal file
52
Structural/Facade/Facade.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Facade;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
class Facade
|
||||
{
|
||||
/**
|
||||
* @var OsInterface
|
||||
*/
|
||||
protected $os;
|
||||
|
||||
/**
|
||||
* @var BiosInterface
|
||||
*/
|
||||
protected $bios;
|
||||
|
||||
/**
|
||||
* This is the perfect time to use a dependency injection container
|
||||
* to create an instance of this class
|
||||
*
|
||||
* @param BiosInterface $bios
|
||||
* @param OsInterface $os
|
||||
*/
|
||||
public function __construct(BiosInterface $bios, OsInterface $os)
|
||||
{
|
||||
$this->bios = $bios;
|
||||
$this->os = $os;
|
||||
}
|
||||
|
||||
/**
|
||||
* turn on the system
|
||||
*/
|
||||
public function turnOn()
|
||||
{
|
||||
$this->bios->execute();
|
||||
$this->bios->waitForKeyPress();
|
||||
$this->bios->launch($this->os);
|
||||
}
|
||||
|
||||
/**
|
||||
* turn off the system
|
||||
*/
|
||||
public function turnOff()
|
||||
{
|
||||
$this->os->halt();
|
||||
$this->bios->powerDown();
|
||||
}
|
||||
}
|
14
Structural/Facade/OsInterface.php
Normal file
14
Structural/Facade/OsInterface.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Facade;
|
||||
|
||||
/**
|
||||
* Class OsInterface
|
||||
*/
|
||||
interface OsInterface
|
||||
{
|
||||
/**
|
||||
* halt the OS
|
||||
*/
|
||||
public function halt();
|
||||
}
|
17
Structural/Facade/README.md
Normal file
17
Structural/Facade/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Facade
|
||||
|
||||
## Purpose
|
||||
|
||||
The primary goal of a Facade Pattern is not to avoid you to read the manual of a complex API. It's only a side-effect.
|
||||
The first goal is to reduce coupling and follow the Law of Demeter.
|
||||
|
||||
A Facade is meant to decouple a client and a sub-system by embedding many (but sometimes just one) interface, and of course to reduce complexity.
|
||||
|
||||
* A facade does not forbid you the access to the sub-system
|
||||
* You can (you should) have multiple facades for one sub-system
|
||||
|
||||
That's why a good facade has no `new` in it. If there are multiple creations for each method, it is not a Facade, it's a Builder or a
|
||||
[Abstract|Static|Simple] Factory [Method].
|
||||
|
||||
The best facade has no `new` and a constructor with interface-type-hinted parameters.
|
||||
If you need creation of new instances, use a Factory as argument.
|
45
Structural/Facade/Test/FacadeTest.php
Normal file
45
Structural/Facade/Test/FacadeTest.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Tests\Facade;
|
||||
|
||||
use DesignPatterns\Facade\Facade as Computer;
|
||||
|
||||
/**
|
||||
* FacadeTest shows example of facades
|
||||
*/
|
||||
class FacadeTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function getComputer()
|
||||
{
|
||||
$bios = $this->getMockBuilder('DesignPatterns\Facade\BiosInterface')
|
||||
->setMethods(array('launch', 'execute', 'waitForKeyPress'))
|
||||
->disableAutoload()
|
||||
->getMock();
|
||||
$operatingSys = $this->getMockBuilder('DesignPatterns\Facade\OsInterface')
|
||||
->setMethods(array('getName'))
|
||||
->disableAutoload()
|
||||
->getMock();
|
||||
$bios->expects($this->once())
|
||||
->method('launch')
|
||||
->with($operatingSys);
|
||||
$operatingSys
|
||||
->expects($this->once())
|
||||
->method('getName')
|
||||
->will($this->returnValue('Linux'));
|
||||
|
||||
$facade = new Computer($bios, $operatingSys);
|
||||
return array(array($facade, $operatingSys));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getComputer
|
||||
*/
|
||||
public function testComputerOn(Computer $facade, $os)
|
||||
{
|
||||
// interface is simpler :
|
||||
$facade->turnOn();
|
||||
// but I can access to lower component
|
||||
$this->assertEquals('Linux', $os->getName());
|
||||
}
|
||||
}
|
11
Structural/FluentInterface/README.md
Normal file
11
Structural/FluentInterface/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Fluent Interface
|
||||
|
||||
## Purpose
|
||||
|
||||
To write code that is easy readable just like sentences in a natural language (like English).
|
||||
|
||||
## Examples
|
||||
|
||||
* Doctrine2's QueryBuilder works something like that example class below
|
||||
* PHPUnit uses fluent interfaces to build mock objects
|
||||
* Yii Framework: CDbCommand and CActiveRecord use this pattern, too
|
80
Structural/FluentInterface/SQL.php
Normal file
80
Structural/FluentInterface/SQL.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\FluentInterface;
|
||||
|
||||
/**
|
||||
* class SQL
|
||||
*/
|
||||
class SQL
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $from = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $where = array();
|
||||
|
||||
/**
|
||||
* adds select fields
|
||||
*
|
||||
* @param array $fields
|
||||
*
|
||||
* @return SQL
|
||||
*/
|
||||
public function select(array $fields = array())
|
||||
{
|
||||
$this->fields = $fields;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a FROM clause
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $alias
|
||||
*
|
||||
* @return SQL
|
||||
*/
|
||||
public function from($table, $alias)
|
||||
{
|
||||
$this->from[] = $table . ' AS ' . $alias;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a WHERE condition
|
||||
*
|
||||
* @param string $condition
|
||||
*
|
||||
* @return SQL
|
||||
*/
|
||||
public function where($condition)
|
||||
{
|
||||
$this->where[] = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query, just an example of building a query,
|
||||
* no check on consistency
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return 'SELECT ' . implode(',', $this->fields)
|
||||
. ' FROM ' . implode(',', $this->from)
|
||||
. ' WHERE ' . implode(' AND ', $this->where);
|
||||
}
|
||||
}
|
23
Structural/FluentInterface/Test/FluentInterfaceTest.php
Normal file
23
Structural/FluentInterface/Test/FluentInterfaceTest.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Tests\FluentInterface;
|
||||
|
||||
use DesignPatterns\FluentInterface\SQL;
|
||||
|
||||
/**
|
||||
* FluentInterfaceTest tests the fluent interface SQL
|
||||
*/
|
||||
class FluentInterfaceTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testBuildSQL()
|
||||
{
|
||||
$instance = new SQL();
|
||||
$query = $instance->select(array('foo', 'bar'))
|
||||
->from('foobar', 'f')
|
||||
->where('f.bar = ?')
|
||||
->getQuery();
|
||||
|
||||
$this->assertEquals('SELECT foo,bar FROM foobar AS f WHERE f.bar = ?', $query);
|
||||
}
|
||||
}
|
9
Structural/Proxy/README.md
Normal file
9
Structural/Proxy/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Proxy
|
||||
|
||||
## Purpose
|
||||
|
||||
To interface to anything that is expensive or impossible to duplicate.
|
||||
|
||||
## Examples
|
||||
|
||||
* Doctrine2 uses proxies to implement framework magic (e.g. lazy initialization) in them, while the user still works with his own entity classes and will never use nor touch the proxies
|
51
Structural/Proxy/Record.php
Normal file
51
Structural/Proxy/Record.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Proxy;
|
||||
|
||||
/**
|
||||
* class Record
|
||||
*/
|
||||
class Record
|
||||
{
|
||||
/**
|
||||
* @var array|null
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @param null $data
|
||||
*/
|
||||
public function __construct($data = null)
|
||||
{
|
||||
$this->data = (array) $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* magic setter
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->data[(string) $name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* magic getter
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (array_key_exists($name, $this->data)) {
|
||||
return $this->data[(string) $name];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
50
Structural/Proxy/RecordProxy.php
Normal file
50
Structural/Proxy/RecordProxy.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Proxy;
|
||||
|
||||
/**
|
||||
* Class RecordProxy
|
||||
*/
|
||||
class RecordProxy extends Record
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isDirty = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isInitialized = false;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct($data)
|
||||
{
|
||||
parent::__construct($data);
|
||||
|
||||
// when the record has data, mark it as initialized
|
||||
// since Record will hold our business logic, we don't want to
|
||||
// implement this behaviour there, but instead in a new proxy class
|
||||
// that extends the Record class
|
||||
if (null !== $data) {
|
||||
$this->isInitialized = true;
|
||||
$this->isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* magic setter
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->isDirty = true;
|
||||
parent::__set($name, $value);
|
||||
}
|
||||
}
|
11
Structural/Registry/README.md
Normal file
11
Structural/Registry/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Registry
|
||||
|
||||
## Purpose
|
||||
|
||||
To implement a central storage for objects often used throughout the application, is typically implemented using
|
||||
an abstract class with only static methods (or using the Singleton pattern)
|
||||
|
||||
## Examples
|
||||
|
||||
* Zend Framework: `Zend_Registry` holds the application's logger object, front controller etc.
|
||||
* Yii Framework: `CWebApplication` holds all the application components, such as `CWebUser`, `CUrlManager`, etc.
|
45
Structural/Registry/Registry.php
Normal file
45
Structural/Registry/Registry.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Registry;
|
||||
|
||||
/**
|
||||
* class Registry
|
||||
*/
|
||||
abstract class Registry
|
||||
{
|
||||
const LOGGER = 'logger';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $storedValues = array();
|
||||
|
||||
/**
|
||||
* sets a value
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function set($key, $value)
|
||||
{
|
||||
self::$storedValues[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a value from the registry
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @static
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($key)
|
||||
{
|
||||
return self::$storedValues[$key];
|
||||
}
|
||||
|
||||
// typically there would be methods to check if a key has already been registered and so on ...
|
||||
}
|
9
Structural/Registry/index.php
Normal file
9
Structural/Registry/index.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
use DesignPatterns\Registry\Registry;
|
||||
|
||||
// while bootstrapping the application
|
||||
Registry::set(Registry::LOGGER, new \StdClass());
|
||||
|
||||
// throughout the application
|
||||
Registry::get(Registry::LOGGER)->log('foo');
|
Reference in New Issue
Block a user