I'm writing a wrapper (Adapter pattern) for a number of REST calls. I'm trying to decide how much my response object should be able to do.
class Job
{
private $xml;
public function __construct(SimpleXMLElement $xml)
{
$this->xml = $xml;
}
public function getName()
{
return $this->xml->Name;
}
public function getId()
{
return $this->xml->ID;
}
...
...
...
}
class RESTManager
{
private $client;
public function __construct(Guzzle $client)
{
$this->client = $client
}
public function getJobByName($name)
{
$job = $this->client->get('/query/job/' . $name);
return new Job($job->asXMLObj())
}
}
So I have something similar to the above. Once I have the Job object I will want to get some additional information such as, a list of items in the Job. This requires another server call
Should I ask the Job what the items are
class Job {
private $manager;
...
...
public function addManager(RESTManager $manager)
{
$this->manager = $manager;
}
public function getItems()
{
return $this->manager->getJobItems($this);
}
}
or
ask the RESTManager directly?
class RESTManager {
...
...
public function getJobItems(Job $job)
{
return $this->client->get('/job/' . $job->getId() . '/items');
}
}
Related
I have a class that is using the State pattern. Here's a simple example
/**
* #Enitity
**/
class Door
{
protected $id;
protected $state;
public function __construct($id, DoorState $state)
public function setState(DoorState $state)
{
$this->state = $state;
}
public function close()
{
$this->setState($this->state->close())
}
...
}
interface DoorState
{
public function close;
public function open;
public function lock;
public function unlock;
}
class DoorAction implements DoorState
{
public function close()
{
throw new DoorError();
}
...
}
then several classes that define the appropriate actions in the states
class OpenedDoor extends DoorAction
{
public function close()
{
return new ClosedDoor();
}
}
So I would have some thing like
$door = new Door('1', new OpenedDoor());
DoctrineDoorRepository::save($door);
$door->close();
DoctrineDoorRepository::save($door);
How would I implement the mapping in Doctrine so I can persist it?
I'm hung up on the $state property. I would like to save the whole DoorAction based object but do I have to the map the DoorAction super class or each individual sub class?
I've looked at implementing it using Embeddable or SuperMapping but run into problems with each.
Doctrine2 DBAL has a feature in the documentation that allows ENUM's
https://www.doctrine-project.org/projects/doctrine-orm/en/current/cookbook/mysql-enums.html#mysql-enums
When we take the Solution 2: Defining a Type as a base, one could create an own type, for instance called doorstatetype or similar to represent the open/closed state. For instance like this:
<?php
namespace Acme\Model\Door;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
class DoorStateType extends Type
{
const ENUM_DOORSTATE = 'enumdoorstate';
const STATE_OPEN = 'open';
const STATE_CLOSED = 'closed';
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return "ENUM('" . self::STATE_OPEN . "', '" . self::STATE_CLOSED . "') COMMENT '(DC2Type:" . ENUM_DOORSTATE . ")'";
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return $value;
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if (!in_array($value, array(self::STATE_OPEN, self::STATE_CLOSED))) {
throw new \InvalidArgumentException("Invalid state");
}
return $value;
}
public function getName()
{
return self::ENUM_DOORSTATE;
}
}
And then use it like this:
<?php
namespace Acme\Model\Door;
/** #Entity */
class Door
{
/** #Column(type="enumdoorstate") */
private $state;
public function open()
{
if (!DoorStateType::STATE_OPEN === $this->state) {
throw new \LogicException('Cannot open an already open door');
}
$this->state = DoorStateType::STATE_OPEN;
}
public function close()
{
if (!DoorStateType::STATE_CLOSED === $this->state) {
throw new \LogicException('Cannot close an already closed door');
}
$this->state = DoorStateType::STATE_CLOSED;
}
}
This allows searching for states:
$openDoors = $repository->findBy(array('state' => DoorStateType::STATE_OPEN));
You could basically then have the convertToPHPValue method create objects of the desired states that allow for some logic, like checking if an open door can be locked or similar.
In the case where the state has to be a class that contains logic, you could implement it like this:
First we define a normal state from which we can inherit:
<?php
namespace Acme\Model\Door;
abstract class DoorState
{
// Those methods define default behaviour for when something isn't possible
public function open()
{
throw new \LogicException('Cannot open door');
}
public function close()
{
throw new \LogicException('Cannot close door');
}
abstract public function getStateName();
}
Then the OpenState:
<?php
namespace Acme\Model\Door;
class OpenState extends DoorState
{
const STATE = 'open';
public function close()
{
return new ClosedState();
}
public function getStateName()
{
return self::STATE;
}
// More logic
}
And finally the ClosedState:
<?php
namespace Acme\Model\Door;
class ClosedState extends DoorState
{
const STATE = 'closed';
public function open()
{
return new OpenState();
}
public function getStateName()
{
return self::STATE;
}
// More logic
}
We can then, for persistence, simply use different convert methods:
<?php
namespace Acme\Model\Door;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
class DoorStateType extends Type
{
// SQL declarations etc.
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ($value === OpenState::STATE) {
return new OpenState();
}
if ($value === ClosedState::STATE) {
return new ClosedState();
}
throw new \Exception(sprintf('Unknown state "%s", expected one of "%s"', $value, implode('", "', [OpenState::STATE, ClosedState::STATE])));
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return $value->getStateName();
}
}
What if you map state as a string and then:
public function setState(DoorState $state)
{
$this->state = serialize($state);
}
and:
private function state()
{
return unserialize($this->state);
}
public function close()
{
$this->setState($this->state()->close())
}
It doesn't save my changes after adding a category.
If I add a category, it is seen in the overview, but if I refresh I see the original amount.
I guess there is an error in my Singleton-design but I can't seem to find it.
class ProductService {
private $_database;
public function __construct($databaseType) {
$databaseFactory = new DatabaseFactory();
$this->_database = $databaseFactory->createDatabase($databaseType);
}
public function addCategory($category){
$this->_database->addCategory($category);
}
public function getAllCategories() {
return $this->_database->getAllCategories();
}
}
class DatabaseFactory {
public function __construct() {
}
public function createDatabase($type){
switch ($type) {
case "Memory" :
return MemoryDatabase::getInstance();
}
}
}
class MemoryDatabase {
private $categories;
private function __construct() {
$this->categories = array(
new Category("Cheese"),
);
}
public static function getInstance() {
static $inst = null;
if ($inst === null) {
$inst = new MemoryDatabase();
}
return $inst;
}
private function __clone() {}
private function __wakeup() {}
public function addCategory($category) {
array_push($this->categories, $category);
}
public function getAllCategories() {
return $this->categories;
}
}
Each request you perform in PHP is stateless.
If you want to persist data between requests, you will need to put your data in some form of persistant storage, i.e., sessions, filesystem, database, memory, etc.
Singleton pattern only ensures a single copy of an object is created, for a given request.
I have problem in using ExceptionMatcher...My example spec:
class DescribeBall extends \PHPSpec\Context {
private $_ball = null;
function before() {
$this->_ball = $this->spec(new Ball);
}
function itShouldHaveStatusRolledOnRoll() {
$this->_ball->roll();
$this->_ball->getStatus()->should->be('Rolled');
}
function itShouldThrowException() {
$this->_ball->getException()->should->throwException('Exception','Error');
}
}
My example class
class Ball {
private $status = null;
public function roll() {
$this->status = 'Rolled';
}
public function getStatus() {
return $this->status;
}
public function getException() {
throw new Exception('Error');
}
}
Anyone used this matcher with success?
$this->_ball->getException()->should->throwException('Exception','Error');
Thanks to my colleagues:
"The last time I looked at it, it used closures (unless Marcello changed it meanwhile) it should still work like this":
function itShouldThrowException() {
$ball = $this->_ball;
$this->spec(function() use ($ball) {
$ball->getException();
})->should->throwException('Exception','Error');
}
Bellow is a PHP script.
I tried to implement the Observer pattern (without MVC structure)... only basic.
The error which is encountered has been specified in a comment.
First I tried to add User objects to the UsersLibrary repository. There was a error such as User::update() does not exists or something.
Why is that error encountered? What fix should be applied and how?
interface IObserver {
public function update(IObservable $sender);
}
interface IObservable {
public function addObserver(IObserver $obj);
public function notify();
}
class UsersLibrary implements IObservable {
private $container;
private $contor;
//private $z;
public function __construct() {//IObserver $a) {
$this->container = array();
$this->contor = 0;
echo "<div>[constructing UsersLibrary...]</div>";
$this->addObserver(new Logger());
//$this->z = $a;
}
public function add($obj) {
echo "<div>[adding a new user...]</div>";
$this->container[$this->contor] = $obj;
$this->contor++;
$this->notify();
}
public function get($index) {
return $this->container[$index];
}
public function addObserver(IObserver $obj) {
$this->container[] = $obj;
}
public function notify() {
echo "<div>[notification in progress...]</div>";
foreach($this->container as $temp) {
//echo $temp;
#################################################################
$temp->update(); //--------ERROR
//Fatal Error: Call to a member function update() on a non-object.
#################################################################
}
//$this->container[0]->update();
//$this->z->update($this);
}
}
class User {
private $id;
private $name;
public function __construct($id, $name) {
$this->id = $id;
$this->name = $name;
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
}
class Logger implements IObserver {
public function __construct() {
echo "<div>[constructing Logger...]</div>";
}
public function update(IObservable $sender) {
echo "<div>A new user has been added.</div>";
}
}
$a = new UsersLibrary(); //new Logger());
//$a->add(new User(1, "DemoUser1"));
//$a->add(new User(2, "DemoUser2"));
$a->add("Demo");
echo $a->get(0);
//echo $a->get(0)->getName();
Your User class is not implementing interface IObserver and therefore is not forced to have the method update().
You have to instantiate a new User() in order to add it to the UsersLibrary:
$library = new UsersLibrary();
$user = new User(1, "Demo");
$library->add($user);
Also, you are mixing Users and Loggers into your UsersLibrary container. Maybe think about separating the containers for them?
You are passing a string instead of an object in your $a->add() call. You should either pass in an object, or alter the code in UserLibrary::add() to wrap it's argument in an appropriate object (or do an object lookup of it sees a string, for instance find a user with that name).
$user = new User(1, "Demo");
$a = new UsersLibrary();
$a->add($user);
I need to implement the following pattern in php:
class EventSubscriber
{
private $userCode;
public function __construct(&$userCode) { $this->userCode = &$userCode; }
public function Subscribe($eventHandler) { $userCode[] = $eventHandler; }
}
class Event
{
private $subscriber;
private $userCode = array();
public function __construct()
{
$this->subscriber = new Subscriber($this->userCode)
}
public function Subscriber() { return $this->subscriber; }
public function Fire()
{
foreach ($this->userCode as $eventHandler)
{
/* Here i need to execute $eventHandler */
}
}
}
class Button
{
private $eventClick;
public function __construct() { $this->eventClick = new Event(); }
public function EventClick() { return $this->eventClick->Subscriber(); }
public function Render()
{
if (/* Button was clicked */) $this->eventClick->Fire();
return '<input type="button" />';
}
}
class Page
{
private $button;
// THIS IS PRIVATE CLASS MEMBER !!!
private function ButtonClickedHandler($sender, $eventArgs)
{
echo "button was clicked";
}
public function __construct()
{
$this->button = new Button();
$this->button->EventClick()->Subscribe(array($this, 'ButtonClickedHandler'));
}
...
}
what is the correct way to do so.
P.S.
I was using call_user_func for that purpose and believe it or not it was able to call private class members, but after few weeks of development i've found that it stopped working. Was it a bug in my code or was it some something else that made me think that 'call_user_func' is able call private class functions, I don't know, but now I'm looking for a simple, fast and elegant method of safely calling one's private class member from other class. I'm looking to closures right now, but have problems with '$this' inside closure...
Callbacks in PHP aren't like callbacks in most other languages. Typical languages represent callbacks as pointers, whereas PHP represents them as strings. There's no "magic" between the string or array() syntax and the call. call_user_func(array($obj, 'str')) is syntactically the same as $obj->str(). If str is private, the call will fail.
You should simply make your event handler public. This has valid semantic meaning, i.e., "intended to be called from outside my class."
This implementation choice has other interesting side effects, for example:
class Food {
static function getCallback() {
return 'self::func';
}
static function func() {}
static function go() {
call_user_func(self::getCallback()); // Calls the intended function
}
}
class Barf {
static function go() {
call_user_func(Food::getCallback()); // 'self' is interpreted as 'Barf', so:
} // Error -- no function 'func' in 'Barf'
}
Anyway, if someone's interested, I've found the only possible solution via ReflectionMethod. Using this method with Php 5.3.2 gives performance penalty and is 2.3 times slower than calling class member directly, and only 1.3 times slower than call_user_func method. So in my case it is absolutely acceptable. Here's the code if someone interested:
class EventArgs {
}
class EventEraser {
private $eventIndex;
private $eventErased;
private $eventHandlers;
public function __construct($eventIndex, array &$eventHandlers) {
$this->eventIndex = $eventIndex;
$this->eventHandlers = &$eventHandlers;
}
public function RemoveEventHandler() {
if (!$this->eventErased) {
unset($this->eventHandlers[$this->eventIndex]);
$this->eventErased = true;
}
}
}
class EventSubscriber {
private $eventIndex;
private $eventHandlers;
public function __construct(array &$eventHandlers) {
$this->eventIndex = 0;
$this->eventHandlers = &$eventHandlers;
}
public function AddEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex++] = $eventHandler;
}
public function AddRemovableEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex] = $eventHandler;
$result = new EventEraser($this->eventIndex++, $this->eventHandlers);
return $result;
}
}
class EventHandler {
private $owner;
private $method;
public function __construct($owner, $methodName) {
$this->owner = $owner;
$this->method = new \ReflectionMethod($owner, $methodName);
$this->method->setAccessible(true);
}
public function Invoke($sender, $eventArgs) {
$this->method->invoke($this->owner, $sender, $eventArgs);
}
}
class Event {
private $unlocked = true;
private $eventReceiver;
private $eventHandlers;
private $recursionAllowed = true;
public function __construct() {
$this->eventHandlers = array();
}
public function GetUnlocked() {
return $this->unlocked;
}
public function SetUnlocked($value) {
$this->unlocked = $value;
}
public function FireEventHandlers($sender, $eventArgs) {
if ($this->unlocked) {
//защита от рекурсии
if ($this->recursionAllowed) {
$this->recursionAllowed = false;
foreach ($this->eventHandlers as $eventHandler) {
$eventHandler->Invoke($sender, $eventArgs);
}
$this->recursionAllowed = true;
}
}
}
public function Subscriber() {
if ($this->eventReceiver == null) {
$this->eventReceiver = new EventSubscriber($this->eventHandlers);
}
return $this->eventReceiver;
}
}
As time passes, there are new ways of achieving this.
Currently PSR-14 is drafted to handle this use case.
So you might find any of these interesting:
https://packagist.org/?query=psr-14