I have implemented Chain of Responsibility pattern in PHP with multiple handlers. As pattern suggests, I can chain handlers as following:
$handler = new Handler();
$handler->chainNext(new HandlerOne)->chainNext(new HandlerTwo)->chainNext(new HandlerThree);
$result = $handler->handle();
I would like to do this in a more programmatic way, but I'm failing to achieve the same. When storing handlers in an array, only the first and the last handler get handled (executed).
$handler = new Handler();
foreach ($handlers as $element) {
$handler->chainNext(new $element);
}
$result = $handler->handle();
Do you have any suggestions how to this programmaticaly? The main reason to do this: I will never know how many handlers will need to be executed (1..n). I need this to be dynamic, as they will be read from database.
You need the $element on the next loop. Like this:
$handler = new Handler();
$last = $handler;
foreach ($handlers as $element) {
$last->chainNext($element);
$last = $element;
}
$result = $handler->handle();
Code assumes, that $handlers already contains object instances (new $element is wrong then..)
Related
I am accidentally sort of making my own framework. (Before you start pls see PS at end!)
So for example I have:
class MessageSchedule
{
use Utility;
protected $messageScheduleID;
protected $messageScheduleName;
...
protected $minDaysPerWeek = 7;
protected $maxDaysPerWeek = 3;
protected $currentTimeZone = "Pacific/Honolulu";
}
class MessageSendList
{
use Utility;
protected $messageSendListID = NULL;
protected $messageScheduleID = NULL;
protected $messageScheduleName = NULL;
...
protected $currentTimeZone = NULL;
}
All tables in the database have mirrored classes with EXACTLY the same names for variables and attributes. Now in my Utility traits I have functions to do CRUD and to read an instantiated object into a script as a key/value array which I can then extract.
Using the calculated attribute names of any class I can do a quick (and very dirty) population of an an object with a foreach loop implementing sort of...
$thisThing = new Thing;
$thisThing -> setThisValue = $thisValue;
... etc
I can do it that way BUT it would be less error prone if I could just sort of "clone if attributes there" function in PHP. Sort of:
$thisFoo = new Foo;
$thisBar = new Bar;
$thisOne = $thisFoo->doCreat(12); //Instantiates Foo with values from FooID=12
$thisBar = partialClone($thisOne); // If PHP had a partial clone! That is what I am looking for.
In my case it would copy over the values of $messageScheduleID $messageScheduleName and $timeZone into a new instance of MessageSendList.
Hope that is vaguely comprehensible.
Thanks. Steve
... Later: This is what I am currently using and trying to replace.
$thisSendList = new MessageSendList();
$vars = $thisSendList->classAttributes();//Generic find attribute name
foreach ($vars as $var)
{
if (isset($$var))
{
$thisSendList -> doSet($var, $$var); // Generic set attributes
}
}
$thisSendList -> doCreate();
PS OK I KNOW that I SHOULD be using Laravel or some such but the learning curve there is pretty steep. I already have to use jQuery, PHP, SQL, HTML, CSS, phpStorm (almost a programming language in itself) etc. etc.
I am a pretty good (very slow) programmer and I can code anything and once I have done it I understand it. One day I will probably move to Laravel but, for the moment, it is a step too far for me ... and I know someome will still say "Why not use Yi" or whatever. I am 64, no memory (far too much cannabis) so...
You can use ReflectionClass to get array of public properties, than use array_intersect of those arrays to get list of properties that you should copy.
function getPropertyList($object)
{
$reflection = new ReflectionClass($object);
$properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
$result = [];
foreach ($properties as $property) {
$result[] = $property->getName();
}
return $result;
}
$thisFoo = new Foo;
$thisBar = new Bar;
$thisOne = $thisFoo->doCreat(12); //Instantiates Foo with values from FooID=12
$propsFoo = getPropertyList($thisOne);
$propsBar = getPropertyList($thisBar);
$common = array_intersect($propsFoo, $propsBar);
foreach ($common as $property) {
$thisBar->$property = $thisOne->$property;
}
You can turn this functionality into trait that you would use in classes where you need it.
Resources:
ReflectionClass::getProperties()
array_intersect()
I am trying to make a file watcher where, when you add, update or delete a file, you can see the files updates in a database. I'm using the framework Symfony4 and a bundle from it called ResourceWatcher from YoSymfony. This bundle uses the Finder bundle from Symfony to find files in the directories specified and then, the watcher compares the cache and the new file to see if there are any changes. When I use a method with the watcher which returns a path array, when I try to see the array, it returns null. How am I suppose to use these methods and their returns?
I put the var_dump everywhere to see that the problem comes from the findChanges()->getUpdatedFiles() and getNewFiles();
//OLD CODE
$finder = new Finder();
$finder->files()
->name('*.csv')
->in('%kernel.root_dir%/../src/data/');
//watcher
$hashContent = new Crc32ContentHash();
$resourceCache = new ResourceCachePhpFile('cache-key.php');
$watcher = new ResourceWatcher($resourceCache, $finder, $hashContent);
$watcher->initialize();
if($watcher->findChanges()->hasChanges()){
if($watcher->findChanges()->getNewFiles() === null){
$paths = $watcher->findChanges()->getUpdatedFiles();
}
else{
$paths = $watcher->findChanges()->getNewFiles();
}
$propertyAccessor = PropertyAccess::createPropertyAccessor();
var_dump($propertyAccessor->getValue($paths, '[first_name]'));
die();
}
I'd like to be able to see the paths, convert them into string and use that into my other method to make the data appear in my database.
In my var_dump, I get NULL in terminal.
EDIT:[first_name] is in my csv-file, you can dump $paths directly.
//NEW CODE
$finder = new Finder();
$finder->files()
->name('*.csv')
->in('%kernel.root_dir%/../src/data/');
//watcher
$hashContent = new Crc32ContentHash();
$resourceCache = new ResourceCachePhpFile('cache-key.php');
$watcher = new ResourceWatcher($resourceCache, $finder, $hashContent);
$watcher->initialize();
$changes = $watcher->findChanges();
if(!empty($changes->getUpdatedFiles())){
$updatedFilesPath = $changes->getUpdatedFiles();
$pathString = implode($updatedFilesPath);
$reader = Reader::createFromPath($pathString);
}
elseif(!empty($changes->getNewFiles())){
$newFilesPath = $changes->getNewFiles();
$pathString = implode($newFilesPath);
$reader = Reader::createFromPath($pathString);
}
else{
return;
}
$results = $reader->fetchAssoc();
So it looks like that as soon as you use the method findChanges()->hasChanges(), it tells the watcher that there is some changes but then it resets and there's no changes anymore in the watcher so it's pointless to use
$paths = $watcher->findChanges()->getUpdatedFiles();
since it will always return nothing because of the reset. I had to make a variable with the changes inside so that I could re-use the changes further down.
Details in code...
I am trying to learn to the Dependency Inversion Principle. Currently my code is like this
class Example {
public function __construct( $input, $output ) {
$input_handler = new InputHandler( $input );
$output_handler = new OutputHandler( $output );
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}
$input = new Input();
$output = new Output();
$example = new Example( $input, $output)
However, it seems using basic dependency injection, it should be more like this?
class Example {
public function __construct( $input_handler, $output_handler ) {
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}
$input = new Input();
$output = new Output();
$input_handler = new InputHandler( $input );
$output_handler = new OutputHandler( $output);
$example = new Example( $input_handler, $output_handler)
Is this is correct?
I want to let the programmer choose the type of input / output to use when running the program. So with dependency injection (as far as I understand) it would look like this;
$input = new ConsoleInput();
$output = new FileOutput();
$input_handler = new ConsoleInputHandler( $input );
$output_handler = new FileOutputHandler( $output);
$example = new Example( $input_handler, $output_handler);
$example->doStuffToOutput();
However, I would prefer to make the programmers life a little easier by only needing to pass in the type of input and output, and not need to worry about the classes handling them;
$input = new ConsoleInput();
$output = new FileOutput();
$example = new Example( $input, $output );
$example->doStuffToOutput();
or even
$example = new Example( new ConsoleInput(), new FileOutput() );
$example->doStuffToOutput();
How can I achieve this using DIP and not end up with my initial code block? Is this a good thing to do?
While I was reading your question I felt you have two main goals. Firstly to improve the readability of your code ('..ease the programmer's life') and secondly to decouple "Example" class from the I/O handlers. For my point of view, DI is just a principle to follow in order to reach your goals.
Before I'm attaching any code, I want to emphasize that sometimes it is better to actually couple your code. Code must be coupled somehow. Do not use DI everywhere just because it has been said. Simplicity, as being described with the KISS and YAGNI principles, is always the winner.
So the big question here is whether your second goal (decoupling with DI) is the smart thing to do. Is there a real reason for the InputHandler / OutputHandler in the "Exmaple" class to be changed? If "No" is your answer, I would recommend you to keep it inside this class intact. And "maybe in the distant future it will be profitable" doesn't really count.
However, if your handlers should be unique for each type (file, console etc.), and your decoupling would help you and other programmers to extend the platform, you can take advantage of the Factory pattern. You have several ways to implement this pattern (static / abstract / simple / method factories). The main goal is to lessen the learning curve from the client, and make the "Example" class decoupled, so that adding more types or handlers would not affect this class.
class HandlerFactory {
protected static function createInputHandler(Input $input)
{
switch ($input)
{
case is_a($input, 'FileInput'):
return new FileInputHandler($input);
case is_a($input, 'ConsoleInput'):
return new ConsoleInputHandler($input);
}
throw new \Exception('Missing Input handler');
}
protected static function createOutputHandler(Output $output)
{
switch ($output)
{
case is_a($output, 'FileOutput'):
return new FileOutputHandler($output);
case is_a($output, 'ConsoleOutput'):
return new ConsoleOutputHandler($output);
}
throw new \Exception('Missing Output handler');
}
public static function createHandler($io)
{
switch ($io)
{
case is_a($io, 'Input'):
return self::createInputHandler($io);
case is_a($io, 'Output'):
return self::createOutputHandler($io);
}
throw new \Exception('Missing I/O handler');
}
}
Now your first code in your question is still relevant with a minor twist:
class Example {
public function __construct($input, $output) {
$input_handler = HandlerFactory::createHandler($input);
$output_handler = HandlerFactory::createHandler($output);
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}
$input = new Input();
$output = new Output();
$example = new Example($input, $output);
Use an Abstract Factory class to handle the instantiation of objects needed to handle i/o. You could either inject the factory into the example class, or let the factory instantiate the objects needed and then inject those into the example class. Then you can do:
$IOFactory = new IOFactory();
$example = new Example($IOFactory::makeInputHandler($inputType), $IOFactory::makeOutputHandler($outputType));
$example->doStuffToOutput();
IOFactory takes care of instantiating input and output objects base in their specific types, and then instantiates the handlers and inject them with the input and output object. Afterwards returns the handler objects to be injected in the example object.
In your case you can choose one of many creational design patterns available. My suggestion is to go with either Factory pattern or object pool pattern.
In case of factory method pattern, you can have a class with the responsibility of creating object:
class ObjectFactory {
public InputHandler createInputHandlerObject(inputobj){
if( inputobj instanceOf ConsoleInput ) {
return new ConsoleInputHandler();
} else if( inputobj instanceOf FileInput ) {
}
}
// similarly create a method for creating OutputHandler object.
//create the appropriate object by using instanceOf operator.
Since I'm familiar with Java i have given example in Java . you can change the syntax and use accordingly. This is not the only way to implement Factory Pattern.
If you want to remove the burden of creating the object at runtime you can use Object pool pattern. Hybrid of prototype pattern also becomes handy in your csse.
Quick one; I doubt it's possible, but is there any way to take advantage of the array($key => $value); syntax of PHP with regard to SplObjectStorage objects?
What I mean is, is there any such way to achieve:
$store = // ?
new KeyObject() => new ValueObject(),
new KeyObject() => new ValueObject(),
// ...
In the context initializing an object store? As of the moment I'm simply using: (and will probably continue, considering the sheer unlikeliness of this being a possibility)
$store = new SplObjectStorage();
$store[new KeyObject()] = new ValueObject();
$store[new KeyObject()] = new ValueObject();
// ...
Would be nice, highly doubting it, but maybe someone knows better.
While it would be a more concise syntax, unfortunately it's not possible. The best you can do is either:
$store[new KeyObject()] = new ValueObject();
or
$store->append( new KeyObject(), new ValueObject());
When adding object to an SplObjectStorage.
Why not do something like that:
$store = new SplObjectStorage();
$data = array(
array(new KeyObject, new ValueObject),
array(new KeyObject, new ValueObject),
array(new KeyObject, new ValueObject),
);
foreach($data as $item) {
list($key, $value) = $item;
$store->attach($key, $value);
}
It's not beautiful but it's at least concise.
I'm working on a new class to wrap XML handling. I want my class to use simplexml if it's installed, and the built in XML functions if it's not. Can anyone give me some suggestions on a skeleton class to do this? It seems "wrong" to litter each method with a bunch of if statements, and that also seems like it would make it nearly impossible to correctly test.
Any upfront suggestions would be great!
EDIT: I'm talking about these built-in xml functions.
Which built-in xml functions are you referring to? SimpleXml is a standard extension, which uses libxml underneath - just as the dom extension does. So if the dom extension is installed, chances are that so is SimpleXml.
I've made a class which wraps SimpleXml functionality... take what you may from it...
bXml.class.inc
There is one weird thing... it's that SimpleXml doesn't allow its constructor to be overloaded, so you can't do things at initiation ... like override the input value (i.e. so you can accept XML as in input). I got around that limitation by using an ArrayObject class to wrap the new SimpleXml class.
I use something like this for doing xml translations and content:
Assuming xml structure something like this (important to use a regular structure, means you can pull off some nice agile tricks!):
<word name="nameofitem">
<en>value</en>
<pt>valor</pt>
<de>value_de</de>
</word>
and then a class to handle the xml:
class translations
{
public $xml = null;
private $file = null;
private $dom = null;
function __construct($file="translations") {
// get xml
$this->file = $file;
$this->haschanges = false;
$this->xml = file_get_contents($_SERVER['DOCUMENT_ROOT']."/xml/".$file.".xml");
$this->dom = new DOMdocument();
$this->dom->loadXML($this->xml);
}
function updateNode($toupdate, $newvalue, $lang="pt",$rootnode="word"){
$this->haschanges = true;
$nodes = $this->dom->getElementsByTagName($rootnode);
foreach ($nodes as $key => $value) {
if ($value->getAttribute("name")==$toupdate) {
$nodes->item($key)->getElementsByTagName($lang)->item(0)->nodeValue = htmlspecialchars($newvalue,ENT_QUOTES,'UTF-8');
}
}
}
function saveUpdated(){
$toSave = $this->dom->saveXML();
if ($this->haschanges === true) {
file_put_contents($_SERVER['DOCUMENT_ROOT']."/xml/".$this->file.".xml", $toSave);
return true;
}
else {
return false;
}
}
}
I took out a few of the methods I have, for brevity, but I extend this with things to handle file and image uploads etc too.
Once you have all this you can do:
$xml = new translations();
// loop through all the language posts
foreach ($_POST["xml"]["en"] as $key => $value) {
$xml->updateNode($key, stripslashes($value), "en");
}
Or something ;) hope this gives you some ideas!