PHP array with only objects of a specific class - php

I have a class called Rule and I'm about to create a RuleContainer class that's actually an array of Rule objects.
I wonder if there is an alternative of creating a new class. Is there any (modern) way to approach this problem? That is, something like using SPL to define an array that only allows adding objects of a specific class.
If not, which interface should I implement in my RuleContainer class?

The most suitable class for your task would be SplObjectStorage, but it doesn't allow for class typehint.
I think, you could do as follow:
class RuleContainer extends SplObjectStorage
{
function attach(Rule $rule)
{
parent::attach($rule);
}
function detach(Rule $rule)
{
parent::detach($rule);
}
}
and so on. You can read for SplObjectStorage interface on php.net and decide, what will you use and what needs overriding.

In your case, I would implement the Iterator interface in the RuleContainer, as I've done several times when I needed a sort of Collection<T> as we know it from other (typed) languages. And in the add(Rule $item) or addItem(Rule $item) method I'd make sure with the type definition of the argument (or using instanceof) that the item to be added is of type Rule.

Depending on the usage patterns for your container class, you need to implement one or more of these interfaces:
Iterator - to use it as foreach($container as $key => $value);
Countable - for count($container);
ArrayAccess - for $container[$key] (set it, get it, check if it isset(), unset() it);

Usage of PHP array-routines interfaces
You may achieve your goal with, for example, ArrayAccess implementation. Together with Iterator it will look like:
class ArrayStorage implements Iterator, ArrayAccess
{
private $holder = [];
private $instanceName;
public function __construct($instanceName)
{
if (!class_exists($instanceName)) {
throw new \Exception('Class '.$instanceName.' was not found');
}
$this->instanceName = $instanceName;
}
public function rewind()
{
reset($this->holder);
}
public function current()
{
return current($this->holder);
}
public function key()
{
return key($this->holder);
}
public function next()
{
next($this->holder);
}
public function valid()
{
return false !== $this->current();
}
public function offsetSet($offset, $value)
{
if (!($value instanceof $this->instanceName)) {
throw new \Exception('Storage allows only '.$this->instanceName.' instances');
}
if (is_null($offset)) {
$this->holder[] = $value;
} else {
$this->holder[$offset] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->holder[$offset]);
}
public function offsetUnset($offset)
{
unset($this->holder[$offset]);
}
public function offsetGet($offset)
{
return isset($this->holder[$offset]) ? $this->holder[$offset] : null;
}
}
Procs
So - yes, you are doing instanceof check explicitly, but end user of your class doesn't know about that. It will only be possible to operate on valid instances in context of this storage (you can check this fiddle for usage sample). Concept is like:
$storage = new ArrayStorage('Foo'); //define what we will accept
$storage[] = new Foo; //fine, [] array-writing
$storage['baz'] = new Foo; //fine, key set
foreach ($storage as $key => $value) {
echo($key. ' => '.PHP_EOL.var_export($value, 1).PHP_EOL);
}
//invalid, will not pass. Either throw exception or just ignore:
$storage['bee'] = new Bar;
End fail-check behavior is up to you, but, my opinion, throwing exception is the best choice here as they are catchable, thus, end user may decide what to do in this case. Further option may be to add Countable to the storage, but it won't change generic idea.
And cons
Downside - no, you will not be able to "typehint" it somehow. While it is useful, in doc blocks you still will need to show what kind of entity are you accepting. In terms of general language features, there is arrayof RFC, by Joe Watkins, which was proposed for PHP version 5.6, but, unfortunately, failed. May be it will be reconsidered in next versions releases.

You can make RuleContainer yourself (as you say) and do all sorts of cleverness to manually enforce it but you live in the real world I live in the real world and you simply don't need a container object for this, just use an array.
If your problem is simply one of enforcement of the subject object type a lá List<className>() you can't do this in PHP and to be honest it's of debatable use in the languages where it is found (I know I will get down voted for saying this, but I will still be right) //excepting it helps further clarify the purpose of the list// In all honesty my 20+ years of programming across almost all the languages there is (except machine code perl and fortran), I can tell you such constructs and simply not worth the human overhead and themselves can include indirect unintended burdens way over their actual worth:
An easy compromise is: no laziness, start naming the array more than something like tmpList and if you are absolultey determined implment a simple test to http://php.net/manual/en/function.get-class.php at the start of the forloops you surely eventually use

Related

PHP interfaces. Different return type

Should interfaces in PHP return the same types ?
My friend told me that it should return the same types.(for good practice)
I know that PHP is dynamic type language and it isn't possible.
For example:
interface idReturn
{
//Return INT
public function getById($id);
}
interface arrayReturn
{
//Return array
public function getByData($data);
}
Is it good practice or not?
It's not an interface thing. To avoid type checking in the consumers, a method should return one type only. It makes for easier to understand code due to the reduced number of execution pathes through the code.
For instance, this adds unnecessary complexity:
function get_users()
{
// some code returning array when users have been found
// or null when no users have been found
}
$users = get_users();
if (!is_null($users)) {
foreach ($users as $user) {
// do something with $user
}
}
If get_users() would simply return an empty array when no users have been found, you can simplify the consuming code to just read
foreach (get_users() as $user) {
// do something with $user
}
Also, by virtue of Liskov's Substitution Principle, any classes that are subtypes of a supertype need to be usable interchangeably in a consumer using the supertype. If the consumer expects an integer, you may not return a string since that might break the consumer. The same holds true for classes implementing an interface and consumers of that interface, e.g.
interface Contract
{
/** #return array */
public function fn();
}
Now assume A implements Contract and returns array, while B implements Contract and returns array or null. My consumer expecting an array will now break when B returns null:
function fn(Contract $instance)
{
// will break when B returns null
foreach ($instance->fn() as $foo) {
// do something with $foo
}
}
Since PHP cannot currently enforce a return type, it's up to the developer to indicate it in the DocBlock and to make sure the implementing or subtyped classes adhere to that return type.
With that said, if the contract defines multiple possible return types, the consumer has to make sure it can handle them. However, that again means undesirable type checking.
He probably meant this:
interface I {
public function f();
}
class A implements I {
public function f() {
return 1; // int
}
}
class B implements I {
public function f() {
return 'a'; // string
}
}
Whether or not you want this depends entirely on the contract you define for I::f. Contracts should be put in separate documentation because PHP lacks support for formally specifying them (this is not PHP-specific and it is the case in most languages, notable exceptions being D and Eiffel).

PHP callback creation internals & performance for lazy initialization

First, take a look at this PHP 5.5.8 code which implements lazy initialization of class properties with using a Trait:
trait Lazy
{
private $__lazilyLoaded = [];
protected function lazy($property, $initializer)
{
echo "Initializer in lazy() parameters has HASH = "
. spl_object_hash($initializer) . "\n";
if (!property_exists($this, $property)
|| !array_key_exists($property, $this->__lazilyLoaded))
{
echo "Initialization of property " . $property . "\n";
$this->__lazilyLoaded[$property] = true;
$this->$property = $initializer();
}
return $this->$property;
}
}
class Test
{
use Lazy;
private $x = 'uninitialized';
public function x()
{
return $this->lazy('x', function(){
return 'abc';
});
}
}
echo "<pre>";
$t = new Test;
echo $t->x() . "\n";
echo $t->x() . "\n";
echo "</pre>";
The output is as follow:
uninitialized
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
Initialization of property x
abc
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
abc
Here are my questions and things I'd like to discuss and improve, but I don't know how.
Based on the HASH values reported, it may appear that the initializer function is created only once.
But actually uniqueness is not guaranteed between objects that did not reside in memory simultaneously. So the question remains unanswered - whether the initializer gets created only once, and it matters for performance I think, but I'm not sure.
The way it's implemented now is not very safe in that if I refactor the code and change property $x to something else, I might forget to change the 'x' value as a first parameter to lazy() method. I'd be happy to use & $this->x instead as a first parameter, but then inside lazy() function I don't have a key to use for $__lazilyLoaded array to keep track of what has been initialized and what has not. How could I solve this problem? Using hash as a key isn't safe, nor it can be generated for callbacks like array($object, 'methodName')
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to access the raw $this->x property as it can be still uninitialized. So I wonder is there a better way - maybe I should save all the values in some Trait's field?
The global aim is to make it:
a) Fast - acceptable enough for small and medium software applications
b) Concise in syntax - as much as possible, to be used widely in the methods of the classes which utilize the Lazy trait.
c) Modular - it would be nice if objects still held their own properties; I don't like the idea of one super-global storage of lazily-initialized values.
Thank you for your help, ideas and hints!
So the question remains unanswered - whether the
initializer gets created only once, and it matters for performance I
think, but I'm not sure.
Well, closure instance is created only once. But anyway, performance will depend not on closure instance creation time (since it is insignificant), but closure execution time.
I'd be happy to use & $this->x instead as a first parameter, but then
inside lazy() function I don't have a key to use for $__lazilyLoaded
array to keep track of what has been initialized and what has not. How
could I solve this problem? Using hash as a key isn't safe, nor it can
be generated for callbacks like array($object, 'methodName')
I can propose the following solution:
<?php
trait Lazy
{
private $_lazyProperties = [];
private function getPropertyValue($propertyName) {
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
if(!isset($this->_propertyLoaders[$propertyName])) {
throw new Exception("Property $propertyName does not have loader!");
}
$propertyValue = $this->_propertyLoaders[$propertyName]();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
public function __call($methodName, $arguments) {
if(strpos($methodName, 'get') !== 0) {
throw new Exception("Method $methodName is not implemented!");
}
$propertyName = substr($methodName, 3);
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
$propertyInializerName = 'lazy' . $propertyName;
$propertyValue = $this->$propertyInializerName();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
}
/**
* #method getX()
**/
class Test
{
use Lazy;
protected function lazyX() {
echo("Initalizer called.\r\n");
return "X THE METHOD";
}
}
echo "<pre>";
$t = new Test;
echo $t->getX() . "\n";
echo $t->getX() . "\n";
echo "</pre>";
Result:
c:\Temp>php test.php
<pre>X THE METHOD
X THE METHOD
</pre>
c:\Temp>php test.php
<pre>Initalizer called.
X THE METHOD
X THE METHOD
</pre>
c:\Temp>
You cannot always be protected from forgetting something, but it is easier to remember when all things are close to each other. So, I propose to implement lazy loaders as methods on corresponding classes with specific names. To provide autocomplete #method annotation can be used. In a good IDE refactoring method name in annotation will allow to rename method across all project. Lazy loading function will be declared in the same class so renaming it also is not a problem.
By declaring a function with a name, starting with "lazy", in my example you both declare a corresponding accessor function, with name starting with "get" and it's lazy loader.
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to
access the raw $this->x property as it can be still uninitialized. So
I wonder is there a better way - maybe I should save all the values in
some Trait's field?
Trait fields are available in the class, that uses specific trait. Even private fields. Remember, this is composition, not inheritance. I think it's better to create private trait array field and store your lazy properties there. No need to create a new field for every property.
But I cannot say I like the whole scheme. Can you explain the use of it for you? May be we can come with better solution.

How to solve the missing object properties in PHP?

This is a bit philosophical but I think many people encountered this problem. The goal is to access various (dynamically declared) properties in PHP and get rid of notices when they are not set.
Why not to __get?
That's good option if you can declare your own class, but not in case of stdClass, SimpleXML or similar. Extending them is not and option since you usually do not instantiate these classes directly, they are returned as a result of JSON/XML parsing.
Example:
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
We have simple stdClass object. The problems is obvious:
$b = $data->birthday;
The property is not defined and therefore a notice is raised:
PHP Notice: Undefined property: stdClass::$birthday
This can happen very often if you consider that you get that object from parsing some JSON. The naive solution is obvious:
$b = isset($data->birthday) ? $data->birthday : null;
However, one gets tired very soon when wrapping every accessor into this. Especially when chaining the objects, such as $data->people[0]->birthday->year. Check whether people is set. Check if the first element is set. Check if birthday is set. Check if year is set. I feel a bit overchecked...
Question:
Finally, my question is here.
What is the best approach to this issue? Silencing notices does not seem to be the best idea. And checking every property is difficult. I have seen some solutions such as Symfony property access but I think it is still too much boilerplate. Is there any simpler way? Either third party library, PHP setting, C extension, I don't care as far as it works... And what are the possible pitfalls?
If I understand correctly, you want to deal with 3rd party Objects, where you have no control, but your logic requires certain properties that may not be present on the Object. That means, the data you accepting are invalid (or should be declared invalid) for your logic. Then the burden of checking the validity goes into your validator. Which I hope you already have following best practices to deal with 3rd party data. :)
You can use your own validator or one by frameworks. A common way is to write a set of Rules that your data needs to obey in order to be valid.
Now inside your validator, whenever a rule is not obeyed, you throw an Exception describing the error and attaching Exception properties that carry the information you want to use. Later when you call your validator somewhere in your logic, you place it inside try {...} block and you catch() your Exceptions and deal with them, that is, write your special logic reserved for those exceptions. As general practice, if your logic becomes too large in a block, you want to "outsource" it as function.
Quoting the great book by Robert Martin "Clean Code", highly recommended for any developer:
The first rule of function is that they should be small. The second is that they should be smaller than that.
I understand your frustration dealing with eternal issets and see as cause of the problem here that each time you need to write a handler dealing with that technical issue of this or that property not present. That technical issue is of very low level in your abstraction hierarchy, and in order to handle it properly, you have to go all the way up your abstraction chain to reach a higher step that has a meaning for your logic. It is always hard to jump between different levels of abstraction, especially far apart. It is also what makes your code hard to maintain and is recommended to avoid.
Ideally your whole architecture is designed as a tree where Controllers sitting at its nodes only know about the edges going down from them.
For instance, coming back to your example, the question is -
Q - What is the meaning for your app of the situation that $data->birthday is missing?
The meaning will depend on what the current function throwing the Exception wants to achieve. That is a convenient place to handle your Exception.
Hope it helps :)
One solution (I don't know if it's the better solution, but one possible solution) is to create a function like this:
function from_obj(&$type,$default = "") {
return isset($type)? $type : $default;
}
then
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
$name = from_obj( $object->name , "unknown");
$job = from_obj( $object->job , "unknown");
$skill = from_obj( $object->skills[0] , "unknown");
$skills = from_obj( $object->skills , Array());
echo "Your name is $name. You are a $job and your main skill is $skill";
if(count($skills) > 0 ) {
echo "\n\nYour skills: " . implode(",",$skills);
}
I think it's convienent because you have at the top of your script what you want and what it should be (array, string, etc)
EDIT:
Another solution. You could create a Bridge class that extends ArrayObject:
class ObjectBridge extends ArrayObject{
private $obj;
public function __construct(&$obj) {
$this->obj = $obj;
}
public function __get($a) {
if(isset($this->obj->$a)) {
return $this->obj->$a;
}else {
// return an empty object in order to prevent errors with chain call
$tmp = new stdClass();
return new ObjectBridge($tmp);
}
}
public function __set($key,$value) {
$this->obj->$key = $value;
}
public function __call($method,$args) {
call_user_func_array(Array($this->obj,$method),$args);
}
public function __toString() {
return "";
}
}
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
$bridge = new ObjectBridge($object);
echo "My name is {$bridge->name}, I have " . count($bridge->skills). " skills and {$bridge->donald->duck->is->paperinik}<br/>";
// output: My name is Pavel, I have 0 skills and
// (no notice, no warning)
// we can set a property
$bridge->skills = Array('php','javascript');
// output: My name is Pavel, my main skill is php
echo "My name is {$bridge->name}, my main skill is {$bridge->skills[0]}<br/>";
// available also on original object
echo $object->skills[0]; // output: php
Personally I would prefer the first solution. It's more clear and more safe.
Data formats which have optional fields are quite difficult to deal with. They're problematic in particular if you have third parties accessing or providing the data, since there rarely is enough documentation to comprehensively cover all causes for the fields to appear or disappear. And of course, the permutations tend to be harder to test, because coders won't instinctively realize that the fields may be there.
That's a long way of saying that if you can avoid having optional fields in your data, the best approach to dealing with missing object properties in PHP is to not have any missing object properties...
If the data you're dealing with is not up to you, then I'd look into forcing default values on all fields, perhaps via a helper function or some sort of crazy variation of the prototype pattern. You could build a data template, which contains default values for all fields of the data, and merge that with the real data.
However, if you do that, are you failing, unless? (Which is another programming philosophy to take into heart.) I suppose one could make the case that providing safe default parameters satisfies data validation for any missing fields. But particularly when dealing with third party data, you should exercise high level of paranoia against any field you're plastering with default values. It's too easy to just set it to null and -- in the process -- fail to understand why it was missing in the first place.
You should also ask what are you trying to achieve? Clarity? Safety? Stability? Minimal code duplication? These are all valid goals. Being tired? Less so. It suggests a lack disciprine, and a good programmer is always disciprined. Of course, I'll accept that people are less likely to do something, if they view it as a chore.
My point is, the answer to your question may differ depending on why it's being asked. Zero effort solution is probably not going to be available, so if you're only exchanging one menial programming task to another one, are you solving anything?
If you are looking for a systematic solution that will guarantee that the data is always in the format you have specified, leading to reduced number of logical tests in the code that processes that data, then perhaps what I suggested above will be of help. But it will not come without a cost and effort.
in PHP version 8
you can use Nullsafe operator as follow:
$res = $data?->people[0]?->birthday?->year;
The best answers have been given, but here is a lazy one:
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
if(
//...check mandatory properties: !isset($object->...)&&
){
//error
}
error_reporting(E_ALL^E_NOTICE);//Yes you're right, not the best idea...
$b = $object->birthday?:'0000-00-00';//thanks Elvis (php>5.3)
//Notice that if your default value is "null", you can just do $b = $object->birthday;
//assign other vars here
error_reporting(E_ALL);
//Your code
Use a Proxy object - it will add just one tiny class and one line per object instantiation to use it.
class ProxyObj {
protected $obj;
public function __construct( $obj ) {
$this->_obj = $obj;
}
public function __get($key) {
if (isset($this->_obj->$key)) {
return $this->_obj->$key;
}
return null;
}
public function __set($key, $value) {
$this->_obj->$key = $value;
}
}
$proxy = new ProxyObj(json_decode($data));
$b = $proxy->birthday;
You can decode the JSON object to an array:
$data = '{"name": "Pavel", "job": "programmer"}';
$jsonarray = json_decode($data, true);
$b = $jsonarray["birthday"]; // NULL
function check($temp=null) {
if(isset($temp))
return $temp;
else
return null;
}
$b = check($data->birthday);
I've hit this problem, mainly from getting json data from a nosql backed api that by design has inconsistent structures, eg if a user has an address you'll get $user->address otherwise the address key just isn't there. Rather than put tons of issets in my templates I wrote this class...
class GracefulData
{
private $_path;
public function __construct($d=null,$p='')
{
$this->_path=$p;
if($d){
foreach(get_object_vars($d) as $property => $value) {
if(is_object($d->$property)){
$this->$property = new GracefulData($d->$property,$this->_path . '->' . $property);
}else{
$this->$property = $value;
}
}
}
}
public function __get($property) {
return new GracefulData(null,$this->_path . '->' . $property);
}
public function __toString() {
Log::info('GracefulData: Invalid property accessed' . $this->_path);
return '';
}
}
and then instantiate it like so
$user = new GracefulData($response->body);
It will gracefully handle nested calls to existing and non existing properties. What it can't handle though is if you access a child of an existing non-object property eg
$user->firstName->something
Lots of good answers here, I consider #Luca 's answer as one of the best - I extended his a little so that I could pass in either an array or object and have it create an easy to use object. Here's mine:
<?php
namespace App\Libraries;
use ArrayObject;
use stdClass;
class SoftObject extends ArrayObject{
private $obj;
public function __construct($data) {
if(is_object($data)){
$this->obj = $data;
}elseif(is_array($data)){
// turn it into a multidimensional object
$this->obj = json_decode(json_encode($data), false);
}
}
public function __get($a) {
if(isset($this->obj->$a)) {
return $this->obj->$a;
}else {
// return an empty object in order to prevent errors with chain call
$tmp = new stdClass();
return new SoftObject($tmp);
}
}
public function __set($key, $value) {
$this->obj->$key = $value;
}
public function __call($method, $args) {
call_user_func_array(Array($this->obj,$method),$args);
}
public function __toString() {
return "";
}
}
// attributions: https://stackoverflow.com/questions/18361594/how-to-solve-the-missing-object-properties-in-php | Luca Rainone
I have written a helper function for multilevel chaining, for example, let's say you want to do something like $obj1->obj2->obj3->obj4, and my helper will return empty string if one of the tiers is not defined or null
class MyUtils
{
// for $obj1->obj2->obj3: MyUtils::nested($obj1, 'obj2', 'obj3')
// returns '' if some of tiers is null
public static function nested($obj1, ...$tiers)
{
if (!isset($obj1)) return '';
$a = $obj1;
for($i = 0; $i < count($tiers); $i++){
if (isset($a->{$tiers[$i]})) {
$a = $a->{$tiers[$i]};
} else {
return '';
}
}
return $a;
}
}

PHP Lazy loading objects and dependency injection

I have recently started reading about dependency injection and it has made me rethink some of my designs.
The problem i have is something like this:
Let's say i have two classes: Car and Passenger;
For those two classes i have some data mappers to work with the database: CarDataMapper and PassengerDataMapper
I want to be able to do something like this in code:
$car = CarDataMapper->getCarById(23); // returns the car object
foreach($car->getPassengers() as $passenger){ // returns all passengers of that car
$passenger->doSomething();
}
Before I knew anything about DI, I would build my classes like this:
class Car {
private $_id;
private $_passengers = null;
public function getPassengers(){
if($this->_passengers === null){
$passengerDataMapper = new PassengerDataMapper;
$passengers = $passengerDataMapper->getPassengersByCarId($this->getId());
$this->setPassengers($passengers);
}
return $this->_passengers;
}
}
I would also have similar code in the Passenger->getCar() method to fetch the car the passenger is in.
I now understand that this creates dependencies (well, I understood it before too, but I wasn't aware that this is "wrong") between the Car and the Passenger objects and the data mapper objects.
While trying to think of the solution for this two options came to mind, but I don't really like any of them:
1: Doing something like this:
$car = $carDataMapper->getCarById(23);
$passengers = $passengerDataMapper->getPassengersByCarId($car->getId());
$car->setPassengers($passengers);
foreach($car->getPassengers() as $passenger){
$passenger->doSomething();
}
But what if passengers have objects that they need injected, and what if the nesting goes to ten or twenty levels... I would wind up instantiating nearly every object in the start of my application, which would in turn query the entire database during the process.
If i have to send the passenger to another object which has to do something with the objects that the passenger holds, I do not want to immediately instantiate these objects too.
2: Injecting the data mappers into the car and passenger objects and having something like this:
class Car {
private $_id;
private $_passengers = null;
private $_dataMapper = null;
public function __construct($dataMapper){
$this->setDataMapper($dataMapper);
}
public function getPassengers(){
if($this->_passengers === null && $this->_dataMapper instanceof PassengerDataMapper){
$passengers = $this->_dataMapper->getPassengersByCarId($this->getId());
$this->setPassengers($passengers);
}
return $this->_passengers;
}
}
I dont like this any better, because it's not like the Car is really unaware of the data mapper, and without the data mapper, the Car could behave unpredictably (not returning passengers, when it actually has them)
So my first question is:
Am I taking a completely wrong approach here, because, the more I look at it, the more it looks like I'm building an ORM, instead of a business layer?
The second question is:
is there a way of actually decoupling the objects and the data mappers in a way that would allow me to use the objects as described in the very first code block?
Third question:
I've seen some answers for other languages (some version of C, I think) resolving this issue with something like this described here:
What is the proper way to inject a data access dependency for lazy loading?
As I haven't had time to play with other languages, this makes no sense to me, so I'd be grateful if someone would explain the examples in the link in PHP-ish.
I have also looked at some DI frameworks, and read about DI Containers and Inversion of Control, but from what I understood they are used to define and inject dependencies for 'non dynamic' classes, where for instance, the Car would depend on the Engine, but it would not need the engine to be loaded dynamically from the db, it would simply be instantiated and injected into the Car.
Sorry for the lengthy post and thanks in advance.
Maybe off-topic, but I think that it will help you a bit:
I think that you try to achieve the perfect solution. But no matter what you come up with, in a couple of years, you will be more experienced and you'll definitely be able to improve your design.
Over the past years with my colleagues we had developed many ORMs / Business Models, but for almost every new project we were starting from scratch, since everyone was more experienced, everyone had learned from the previous mistakes and everyone had come across with new patterns and ideas. All that added an extra month or so in development, which increased the cost of the final product.
No matter how good the tools are, the key problem is that the final product must be as good as possible, at the minimum cost. The client won't care and won't pay for things that can't see or understand.
Unless, of course, you code for research or for fun.
TL;DR: Your future self will always outsmart your current self, so do not overthink about it. Just pick carefully a working solution, master it and stick with it until it won't solve your problems :D
To answer your questions:
Your code is perfectly fine, but the more you will try to make it "clever" or "abstract" or "dependency-free", the more you will lean towards an ORM.
What you want in the first code block is pretty feasible. Take a look at how the Doctrine ORM works, or this very simple ORM approach I did a few months ago for a weekend project:
https://github.com/aletzo/dweet/blob/master/app/models
I was going to say "I know this is an old question but..." then I realized you posted it 9 hours ago, which is cool, because I just came to a satisfactory 'resolution' for myself. I thought of the implementation and then I realized it is what people were calling 'dependency injection'.
Here is an example:
class Ticket {
private $__replies;
private $__replyFetcher;
private $__replyCallback;
private $__replyArgs;
public function setReplyFetcher(&$instance, $callback, array $args) {
if (!is_object($instance))
throw new Exception ('blah');
if (!is_string($callback))
throw new Exception ('blah');
if (!is_array($args) || empty($args))
throw new Exception ('blah');
$this->__replyFetcher = $instance;
$this->__replyCallback = $callback;
$this->__replyArgs = $args;
return $this;
}
public function getReplies () {
if (!is_object($this->__replyFetcher)) throw new Exception ('Fetcher not set');
return call_user_func_array(array($this->__replyFetcher,$this->__replyCallback),$this->__replyArgs);
}
}
Then, in your service layer (where you 'coordinate' actions between multiple mappers and models) you can call the 'setReplyFetcher' method on all of the ticket objects before you return them to whatever is invoking the service layer -- OR -- you could do something very similar with each mapper, by giving the mapper a private 'fetcherInstance' and 'callback' property for each mapper the object is going to need, and then set THAT up in the service layer, then the mapper will take care of preparing the objects. I am still weighing the differences between the two approaches.
Example of coordinating in the service layer:
class Some_Service_Class {
private $__mapper;
private $__otherMapper;
public function __construct() {
$this->__mapper = new Some_Mapper();
$this->__otherMapper = new Some_Other_Mapper();
}
public function getObjects() {
$objects = $this->__mapper->fetchObjects();
foreach ($objects as &$object) {
$object->setDependentObjectFetcher($this->__otherMapper,'fetchDependents',array($object->getId()));
}
return $objects;
}
}
Either way you go, the object classes are independent of mapper classes, and mapper classes are independent of each other.
EDIT: Here is an example of the other way to do it:
class Some_Service {
private $__mapper;
private $__otherMapper;
public function __construct(){
$this->__mapper = new Some_Mapper();
$this->__otherMapper = new Some_Other_Mapper();
$this->__mapper->setDependentFetcher($this->__otherMapper,'someCallback');
}
public function fetchObjects () {
return $this->__mapper->fetchObjects();
}
}
class Some_Mapper {
private $__dependentMapper;
private $__dependentCallback;
public function __construct ( $mapper, $callback ) {
if (!is_object($mapper) || !is_string($callback)) throw new Exception ('message');
$this->__dependentMapper = $mapper;
$this->__dependentCallback = $callback;
return $this;
}
public function fetchObjects() {
//Some database logic here, returns $results
$args[0] = &$this->__dependentMapper;
$args[1] = &$this->__dependentCallback;
foreach ($results as $result) {
// Do your mapping logic here, assigning values to properties of $object
$args[2] = $object->getId();
$objects[] = call_user_func_array(array($object,'setDependentFetcher'),$args)
}
}
}
As you can see, the mapper requires the other resources to be available to even be instantiated. As you can also see, with this method you are kind of limited to calling mapper functions with object ids as parameters. I'm sure with some sitting down and thinking there is an elegant solution to incorporate other parameters, say fetching 'open' tickets versus 'closed' tickets belonging to a department object.
Here is another approach I thought of. You can create a 'DAOInjection' object that acts as a container for the specific DAO, callback, and args needed to return the desired objects. The classes then only need to know about this DAOInjection class, so they are still decoupled from all of your DAOs/mappers/services/etc.
class DAOInjection {
private $_DAO;
private $_callback;
private $_args;
public function __construct($DAO, $callback, array $args){
if (!is_object($DAO)) throw new Exception;
if (!is_string($callback)) throw new Exception;
$this->_DAO = $DAO;
$this->_callback = $callback;
$this->_args = $args;
}
public function execute( $objectInstance ) {
if (!is_object($objectInstance)) throw new Exception;
$args = $this->_prepareArgs($objectInstance);
return call_user_func_array(array($this->_DAO,$this->_callback),$args);
}
private function _prepareArgs($instance) {
$args = $this->_args;
for($i=0; $i < count($args); $i++){
if ($args[$i] instanceof InjectionHelper) {
$helper = $args[$i];
$args[$i] = $helper->prepareArg($instance);
}
}
return $args;
}
}
You can also pass an 'InjectionHelper' as an argument. The InjectionHelper acts as another callback container -- this way, if you need to pass any information about the lazy-loading object to its injected DAO, you won't have to hard-code it into the object. Plus, if you need to 'pipe' methods together -- say you need to pass $this->getDepartment()->getManager()->getId() to the injected DAO for whatever reason -- you can. Simply pass it like getDepartment|getManager|getId to the InjectionHelper's constructor.
class InjectionHelper {
private $_callback;
public function __construct( $callback ) {
if (!is_string($callback)) throw new Exception;
$this->_callback = $callback;
}
public function prepareArg( $instance ) {
if (!is_object($instance)) throw new Exception;
$callback = explode("|",$this->_callback);
$firstCallback = $callback[0];
$result = $instance->$firstCallback();
array_shift($callback);
if (!empty($callback) && is_object($result)) {
for ($i=0; $i<count($callback); $i++) {
$result = $result->$callback[$i];
if (!is_object($result)) break;
}
}
return $result;
}
}
To implement this functionality in the object, you would require the injections at construction to ensure that the object has or can get all of the information it needs. Each method that uses an injection simply calls the execute() method of the respective DAOInjection.
class Some_Object {
private $_childInjection;
private $_parentInjection;
public function __construct(DAOInjection $childInj, DAOInjection $parInj) {
$this->_childInjection = $childInj;
$this->_parentInjection = $parInj;
}
public function getChildObjects() {
if ($this->_children == null)
$this->_children = $this->_childInjection->execute($this);
return $this->_children;
}
public function getParentObjects() {
if ($this->_parent == null)
$this->_parent = $this->_parentInjection->execute($this);
return $this->_parent;
}
}
I would then, in the constructor of my service class, instantiate the mappers relevant to that service using the relevant DAOInjection classes as arguments for the mappers' constructors. The mappers would then take care of making sure each object has its injections, because the mapper's job is to return complete objects and handle the saving/deleting of objects, while the service's job is to coordinate the relationships between various mappers, objects, and so on.
Ultimately you can use it to inject callbacks to services OR mappers, so say you want your 'Ticket' object to retrieve a parent user, which happens to be outside the realm of the 'Ticket Service' -- the ticket service can just inject a callback to the 'User Service', and it won't have to know a thing about how the DAL works for other objects.
Hope this helps!

Debug a DOMDocument Object in PHP

I'm trying to debug a large and complex DOMDocument object in php. Ideally it'd be nice if I could get DOMDocument to output in a array-like format.
DoMDocument:
$dom = new DOMDocument();
$dom->loadHTML("<html><body><p>Hello World</p></body></html>");
var_dump($dom); //or something equivalent
This outputs
DOMDocument Object ( )
whereas I'd like it to output
DOMDocument:
html
=>body
==>p
===>Hello World
Or something like that. Why is there no handy debug or output for this?!?
This answer is a little late probably, but I liked your question!
PHP has nothing build-in directly to solve your problem, so there is not XML dump or something.
However, PHP has the RecursiveTreeIterator­Docs that comes pretty close to your output:
\-<html>
\-<body>
\-<p>
\-Hello World
(it will look better if your X(HT)ML structure looks more complicated.)
It's used quite simple (as most iterators) with a foreach:
$tree = new RecursiveTreeIterator($iterator);
foreach($tree as $key => $value)
{
echo $value . "\n";
}
(You can wrap this inside a function, so you only need to call the function)
Even this looks simple, there's one caveat: it needs a RecursiveIterator over the DOMDocument tree. As PHP can not guess what you need, it needs to be wrapped into code. As written, I found the question interesting (and obviously you have not asked for XML output), so I wrote some little code that offers the recursive iterator needed. So here we go.
First of all you might not be familiar with iterators in PHP. That's no deal to make use of the code I'll show as I'll do it backwards, however, whenever you consider to run some code on your own, consider whether or not you can make use of the iterator capabilities PHP has to offer. I write that because it helps to solve common problems and to make components that are not really related with each other to work with each other. For example, the RecursiveTreeIterator­Docs is built-in, and it will work with anything you feed it with (and you can even configure it). However it needs a RecursiveIterator to operate upon.
So let's give it a RecursiveIterator that offers <tag> for DOMNodes that are tags (elements) and just the text if they are textnodes:
class DOMRecursiveDecoratorStringAsCurrent extends RecursiveIteratorDecoratorStub
{
public function current()
{
$node = parent::current();
$nodeType = $node->nodeType;
switch($nodeType)
{
case XML_ELEMENT_NODE:
return "<$node->tagName>";
case XML_TEXT_NODE:
return $node->nodeValue;
default:
return sprintf('(%d) %s', $nodeType, $node->nodeValue);
}
}
}
This DOMRecursiveDecoratorStringAsCurrent class (the name is exemplary only) makes use of some abstract code in RecursiveIteratorDecoratorStub. The important part however is the ::current function which just returns the tagName of a DOMNode in bracketsWikipedia (<>) and the text of textnodes as-is. That's what your output needs, so that's everything needed to code.
Actually this does not work until you have the abstract code as well, but to visualize the code how it's used (the most interesting part), let's view it:
$iterator = new DOMRecursiveDecoratorStringAsCurrent($iterator);
$tree = new RecursiveTreeIterator($iterator);
foreach($tree as $key => $value)
{
echo $value . "\n";
}
As it's done backwards, for the moment we have the output specified based on which DOMNode is to be displayed by the RecursiveTreeIterator. Fine so far, easy to get. But the missing meat it is inside the abstract code and how to create a RecursiveIterator over all nodes inside a DOMElement. Just preview the whole code how it is invoked (as written before, you can put this into a function to make it easily accessible within your code for debugging purposes. Probably a function called xmltree_dump):
$dom = new DOMDocument();
$dom->loadHTML("<html><body><p>Hello World</p></body></html>");
$iterator = new DOMRecursiveIterator($dom->documentElement);
$iterator = new DOMRecursiveDecoratorStringAsCurrent($iterator);
$tree = new RecursiveTreeIterator($iterator);
foreach($tree as $key => $value)
{
echo $value . "\n";
}
So what do we got here in addition to the code already covered? First there is a DOMRecursiveIterator - and that's it. The rest of the code is standard DOMDocument code.
So let's write about DOMRecursiveIterator. It's the needed RecursiveIterator that's finally needed within the RecursiveTreeIterator. It get's decorated so that the dump of the tree actually prints tagnames in brackets and text as-is.
Probably it's worth to share the code of it now:
class DOMRecursiveIterator extends DOMIterator implements RecursiveIterator
{
public function hasChildren()
{
return $this->current()->hasChildNodes();
}
public function getChildren()
{
$children = $this->current()->childNodes;
return new self($children);
}
}
It's a pretty short class with only two functions. I'm cheating here as this class also extends from another class. But as written, this is backwards, so this class actually takes care of the recursion: hasChildren and getChildren. Obviously even those two functions don't have much code, they are just mapping the "question" (hasChildren? getChildren?) onto a standard DOMNode. If a node has children, well, say yes or just return them (and this is an iterator, return them in form of an iterator, hence the new self()).
So as this is pretty short, after choking it, just continue with the parent class DOMIterator (the implements RecursiveIterator­Docs is just to make it working):
class DOMIterator extends IteratorDecoratorStub
{
public function __construct($nodeOrNodes)
{
if ($nodeOrNodes instanceof DOMNode)
{
$nodeOrNodes = array($nodeOrNodes);
}
elseif ($nodeOrNodes instanceof DOMNodeList)
{
$nodeOrNodes = new IteratorIterator($nodeOrNodes);
}
if (is_array($nodeOrNodes))
{
$nodeOrNodes = new ArrayIterator($nodeOrNodes);
}
if (! $nodeOrNodes instanceof Iterator)
{
throw new InvalidArgumentException('Not an array, DOMNode or DOMNodeList given.');
}
parent::__construct($nodeOrNodes);
}
}
This is the base iterator for DOMPHP, it just takes a DOMNode or a DOMNodeList to iterate over. This sounds a bit superfluous maybe, as DOM supports this sort-of with DOMNodeList already, but it does not support a RecursiveIterator and we already know that we need one for RecursiveTreeIterator for the output. So in it's constructor an Iterator is created and passed on to the parent class, which again is abstract code. Sure I'll reveal this code in just a minute. As this is backwards, let's review what's been done so far:
RecursiveTreeIterator for the tree-like output.
DOMRecursiveDecoratorStringAsCurrent for the visualization of a DOMNode in the tree
DOMRecursiveIterator and DOMIterator to iterate recursively over all nodes in a DOMDocument.
This in terms of definition as all that's needed, however the code that I called abstract is still missing. It's just some sort of simple proxy code, it delegates the same method down to another object. A related pattern is called Decorator. However, this is just the code, first the Iterator and then it's RecursiveIterator friend:
abstract class IteratorDecoratorStub implements OuterIterator
{
private $iterator;
public function __construct(Iterator $iterator)
{
$this->iterator = $iterator;
}
public function getInnerIterator()
{
return $this->iterator;
}
public function rewind()
{
$this->iterator->rewind();
}
public function valid()
{
return $this->iterator->valid();
}
public function current()
{
return $this->iterator->current();
}
public function key()
{
return $this->iterator->key();
}
public function next()
{
$this->iterator->next();
}
}
abstract class RecursiveIteratorDecoratorStub extends IteratorDecoratorStub implements RecursiveIterator
{
public function __construct(RecursiveIterator $iterator)
{
parent::__construct($iterator);
}
public function hasChildren()
{
return $this->getInnerIterator()->hasChildren();
}
public function getChildren()
{
return new static($this->getInnerIterator()->getChildren());
}
}
That's nothing very magically, it's just well delegating the method calls to it's inherited object $iterator. It looks like repeating and well iterators are about repetition. I put this into abstract classes so I only need to write this very simple code once. So at least I myself don't need to repeat myself.
These two abstract classes are used by other classes which have been already discussed earlier. Because they are so simple, I left it until here.
Well, much to read until here but the good part is, that's it.
In short: PHP does not have this build in, but you can write this on your own quite simple and re-useable. As written earlier, it's a good idea to wrap this into a function called xmltree_dump so it can be easily called for debugging purposes:
function xmltree_dump(DOMNode $node)
{
$iterator = new DOMRecursiveIterator($node);
$decorated = new DOMRecursiveDecoratorStringAsCurrent($iterator);
$tree = new RecursiveTreeIterator($decorated);
foreach($tree as $key => $value)
{
echo $value . "\n";
}
}
Usage:
$dom = new DOMDocument();
$dom->loadHTML("<html><body><p>Hello World</p></body></html>");
xmltree_dump($dom->documentElement);
the only thing needed is to have all the class definitions used included / required. You can put them in one file and use require_once or integrate them with an autoloader that you're probably using. Full code at once.
If you need to edit the way of output, you can edit DOMRecursiveDecoratorStringAsCurrent or change the configuration of RecursiveTreeIterator­ inside xmltree_dump. Hope this is helpful (even quite lengthy, backwards is pretty in-direct).
http://usphp.com/manual/en/function.dom-domdocument-savexml.php
$dom->formatOutput = true;
echo $dom->saveXML();
for a dom node, just use the following:
print_r(simplexml_import_dom($entry)->asXML());
Though I haven't tried it myself, check out Zend_Dom, part of the Zend Framework. Documentation and examples for most of the Zend Framework components is really thorough.
I just used DOMDocument::save. It's lame that it has to write to a file, but whatever.
You can cheat and use JSON to inspect the structure by converting it to an array.
print_r(json_decode(json_encode($node), true));

Categories