In my task would be very nice to write a kind of objects serialization (for XML output). I've already done it, but have no idea, how to avoid recursive links.
The trouble is that some objects must have public(!) properties with links to their parents (it's really nessecary). And when I try to serialize a parent object which agregates some children - children with links to parent do recursion forever.
Is there a solution to handle such recursions as print_r() does without hacks?
I can't use somthing like "if ($prop === 'parent')", because sometimes there's more than 1 link to parents from different contexts.
Write your own serialization function and always pass it a list of already-processed items. Since PHP5 (I assume, you are using php5) always copies references to an object, you can do the following:
public function __sleep() {
return $this->serialize();
}
protected function serialize($processed = array()) {
if (($position = array_search($this, $processed, true)) !== false) {
# This object has already been processed, you can use the
# $position of this object in the $processed array to reference it.
return;
}
$processed[] = $this;
# do your actual serialization here
# ...
}
Related
Is it possible to use XPath syntax directly on PHP objects in order to navigate through the hierarchy of the object?
That is, can I use (2) instead of (1):
$object->subObject1->subObject2
$object['subObject1/subObject2'] (The expression in the brackets is the XPath.)
Additional question:
According to my current understanding, a conversion of an object into an ArrayObject doesn't make sense, because XPath cannot be used with ArrayObjects. Is this correct?
If all you need is basic traversal based on a /-separated path, then you can implement it with a simple loop like this:
public function getDescendant($path) {
// Separate the path into an array of components
$path_parts = explode('/', $path);
// Start by pointing at the current object
$var = $this;
// Loop over the parts of the path specified
foreach($path_parts as $property)
{
// Check that it's a valid access
if ( is_object($var) && isset($var->$property) )
{
// Traverse to the specified property,
// overwriting the same variable
$var = $var->$property;
}
else
{
return null;
}
}
// Our variable has now traversed the specified path
return $var;
}
To set a value is similar, but we need one extra trick: to make it possible to assign a value after the loop has exited, we need to assign the variable by reference each time:
public function setDescendant($path, $value) {
// Separate the path into an array of components
$path_parts = explode('/', $path);
// Start by pointing at the current object
$var =& $this;
// Loop over the parts of the path specified
foreach($path_parts as $property)
{
// Traverse to the specified property,
// overwriting the same variable with a *reference*
$var =& $var->$property;
}
// Our variable has now traversed the specified path,
// and is a reference to the variable we want to overwrite
$var = $value;
}
Adding those to a class called Test, allows us to do something like the following:
$foo = new Test;
$foo->setDescendant('A/B', 42);
$bar = new Test;
$bar->setDescendant('One/Two', $foo);
echo $bar->getDescendant('One/Two/A/B'), ' is the same as ', $bar->One->Two->A->B;
To allow this using array access notation as in your question, you need to make a class that implements the ArrayAccess interface:
The above functions can be used directly as offsetGet and offsetSet
offsetExists would be similar to getDescendant/offsetGet, except returning false instead of null, and true instead of $var.
To implement offsetUnset properly is slightly trickier, as you can't use the assign-by-reference trick to actually delete a property from its parent object. Instead, you need to treat the last part of the specified path specially, e.g. by grabbing it with array_pop($path_parts)
With a bit of care, the 4 methods could probably use a common base.
One other thought is that this might be a good candidate for a Trait, which basically lets you copy-and-paste the functions into unrelated classes. Note that Traits can't implement Interfaces directly, so each class will need both implements ArrayAccess and the use statement for your Trait.
(I may come back and edit in a full example of ArrayAccess methods when I have time.)
With some dependencies it should be (easily) possible supporting the complete set of XPath expressions. The only difficulty is to implement the walk over the object from the fully qualified XPath.
Serialize the object to XML with something like XML_Serializer from PEAR.
Load the created document as DOMDocument, run your arbitrary XPath expression and get the node path ($node->getNodePath()) from the selected elements as shown here
Armed with a node path like /blah/example[2]/x[3] you can now implement a walk on the object recursively using object attribute iteration. This heavily depends on how the serializer from 1. actually works.
Note: I don't know if implementing the ArrayAccess interface is actually necessary, because you can access object attributes like $obj->$key with $key being some string that was sliced from the node path.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Return a loop in function php
To make my question more clear, I'll explain the situation a bit... I'm trying to make a simple yet powerfull PHP-ORM-tool. First versions ( only 25 kB of code ) and tests are quite promising, it has e.g. lazy-loading. Now I'm optimizing the thing by e.g. minimizing the number of queries, ...
For the lazy-loading, I use a Proxy-class. The Child-property of the Parent-class is a Proxy at first. That Proxy contains an empty object...
class Parent {
getChild() { ... }
//other code
}
class Child {
getName() { ... }
//other code
}
class Proxy {
$object = false;
public function _query() { /*some code to get the objects*/ }
__call() {
if(!$objects)
$this->_query();
//perform called function on the $object(s)
}
//other code
}
When we ask the Child from the Parent, we assume it is a Child, but in fact it is a Proxy. As long as we don't do anything with it, we don't have to query the database... Whenever we ask the Child something ( like getName() ), the magic call function comes in action, queries the database an performs the called function on the new object. The principle is easy, but it is a lot more difficult in code... (it also support lists of objects, triggers in loops, has arraysaccess, the querying is quite complex too, ...)
The problem now is the following:
foreach( $parents as $parent ) {
echo $parent->getChild()->getName();
}
Every call in the foreach loop, triggers a query to the database...
I don't want that! Because I already know that I want the children of a list of parents (thats what the human mind says at least...)
Let's assume I have knowledge of all the Proxies of the same type, I would like to do something like this:
class Proxy {
_query() {
## some code to test if the call originates from within a loop ##
//if so: fill all the Proxies of this type with their object(s)
//else fill this Proxy with its object(s)
}
//other code
}
I know I'm simplyfying this a bit, but that's the general idea...
debug_backtrace can give me the method from which a function was called, but I want information on the loop-structures... (if possible even the original list etc...)
Why not just using a parameter like
function _query($loop = false)
when called in a loop you could use _query(true), to tell the function you are in a loop.
Otherwise you could use an internal counter-variable to count the calls for the query. ;)
You can't programmatically, but one hack I can think of is to set a global flag variable before any loop?
You can examine the call stack, but the overhead will be huge.
No, it can't detect that as far as I know but you could always send a parameter when calling the method no?
I am creating a class which I will use to store and load some settings. Inside the class all settings are stored in an array. The settings can be nested, so the settings array is a multidimensional array. I want to store and load the settings using the magic methods __get and __set, so the settings can act as class members. However, since I'm using nested methods, I can't get the __set method to work when I try to access a nested setting.
The class is like this:
class settings
{
private $_settings = array();
//some functions to fill the array
public function __set($name, $value)
{
echo 'inside the __set method';
//do some stuff
}
}
And the code to use this class:
$foo = new settings();
//do some stuff with the class, so the internal settings array is as followed:
//array(
// somename => somevalue
// bar => array (
// baz = someothervalue
// qux = 42
// )
// )
$foo->somename = something; //this works, __set method is called correctly
$foo->bar['baz'] = somethingelse; //Doesn't work, __set method isn't called at all
How can I get this last line to work?
When accessing an array using this method, it actually goes through __get instead. In order to set a parameter on that array that was returned it needs to be returned as a reference: &__get($name)
Unless, what you mean is that you want each item that is returned as an array to act the same way as the parent object, in which case you should take a look at Zend Framework's Zend_Config object source for a good way to do that. (It returns a new instance of itself with the sub-array as the parameter).
This would work:
$settings = new Settings();
$settings->foo = 'foo';
$settings->bar = array('bar');
But, there is no point in using magic methods or the internal array at all. When you are allowing getting and setting of random members anyway, then you can just as well make them all public.
Edit after comments (not answer to question above)
Like I already said in the comments I think your design is flawed. Let's tackle this step by step and see if we can improve it. Here is what you said about the Settings class requirements:
settings can be saved to a file or a database
settings might need to update other parts of the application
settings need to be validated before they are changed
should use $setting->foo[subsetting] over $setting->data[foo[subsetting]]
settings class needs to give access to the settings data for other classes
first time an instance is made, the settings need to be loaded from a file
Now, that is quite a lot of things to do for a single class. Judging by the requirements you are trying to build a self-persisting Singleton Registry, which on a scale of 1 (bad) to 10 (apocalyptic) is a level 11 idea in my book.
According to the Single Responsibility Principle (the S in SOLID) a class should have one and only reason to change. If you look at your requirements you will notice that there is definitely more than one reason to change it. And if you look at GRASP you will notice that your class takes on more roles than it should.
In detail:
settings can be saved to a file or a database
That is at least two responsibilites: db access and file access. Some people might want to further distinguish between reading from file and saving to file. Let's ignore the DB part for now and just focus on file access and the simplest thing that could possibly work for now.
You already said that your settings array is just a dumb key/value store, which is pretty much what arrays in PHP are. Also, in PHP you can include arrays from a file when they are written like this:
<?php // settings.php
return array(
'foo' => 'bar'
);
So, technically you dont need to do anything but
$settings = include 'settings.php';
echo $settings['foo']; // prints 'bar';
to load and use your Settings array from a file. This is so simple that it's barely worth writing an object for it, especially since you will only load those settings once in your bootstrap and distribute them to the classes that need them from there.
Saving an array as an includable file isnt difficult either thanks to var_export and file_put_contents. We can easily create a Service class for that, for example
class ArrayToFileService
{
public function export($filePath, array $data)
{
file_put_contents($filePath, $this->getIncludableArrayString($data));
}
protected function getIncludableArrayString($data)
{
return sprintf('<?php return %s;', var_export($data, true));
}
}
Note that I deliberatly did not make the methods static despite the class having no members of it's own to operate on. Usign the class statically will add coupling between the class and any consumer of that class and that is undesirable and unneccessary.
All you have to do now to save your settings is
$arrayToFileService = new ArrayToFileService;
$arrayToFileService->export('settings.php', $settings);
In fact, this is completely generic, so you can reuse it for any arrays you want to persist this way.
settings might need to update other parts of the application
I am not sure why you would need this. Given that our settings array can hold arbitrary data you cannot know in advance which parts of the application might need updating. Also, knowing how to update other parts of the application isnt the responsiblity of a data container. What we need is a mechanism that tells the various parts of the application when the array got updated. Of course, we cannot do that with a plain old array because its not an object. Fortunately, PHP allows us to access an object like an array by implementing ArrayAccess:
class HashMap implements ArrayAccess
{
protected $data;
public function __construct(array $initialData = array())
{
$this->data = $initialData;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
$this->data[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->data[$offset]);
}
public function getArrayCopy()
{
return $this->data;
}
}
The methods starting with offset* are required by the interface. The method getArrayCopy is there so we can use it with our ArrayToFileService. We could also add the IteratorAggregate interface to have the object behave even more like an array but since that isnt a requirement right now, we dont need it. Now to allow for arbitrary updating, we add a Subject/Observer pattern by implementing SplSubject:
class ObservableHashMap implements ArrayAccess, SplSubject
…
protected $observers;
public function __construct(array $initialData = array())
{
$this->data = $initialData;
$this->observers = new SplObjectStorage;
}
public function attach(SplObserver $observer)
{
$this->observers->attach($observer);
}
public function detach(SplObserver $observer)
{
$this->observers->detach($observer);
}
public function notify()
{
foreach ($this->observers as $observers) {
$observers->update($this);
}
}
}
This allows us to register arbitrary objects implementing the SplObserver interface with the ObservableHashMap (renamed from HashMap) class and notify them about changes. It would be somewhat prettier to have the Observable part as a standalone class to be able to reuse it for other classes as well. For this, we could make the Observable part into a Decorator or a Trait. We could also decouple Subject and Observers further by adding an EventDispatcher to mediate between the two, but for now this should suffice.
Now to notify an observer, we have to modify all methods of the class that should trigger a notification, for instance
public function offsetSet($offset, $value)
{
$this->data[$offset] = $value;
$this->notify();
}
Whenever you call offsetSet() or use [] to modify a value in the HashMap, any registered observers will be notified and passed the entire HashMap instance. They can then inspect that instance to see whether something important changed and react as needed, e.g. let's assume SomeComponent
class SomeComponent implements SplObserver
{
public function update(SplSubject $subject)
{
echo 'something changed';
}
}
And then you just do
$data = include 'settings.php';
$settings = new ObservableHashMap($data);
$settings->attach(new SomeComponent);
$settings['foo'] = 'foobarbaz'; // will print 'something changed'
This way, your settings class needs no knowledge about what needs to happen when a value changes. You can keep it all where it belongs: in the observers.
settings need to be validated before they are changed
That one is easy. You dont do it inside the hashmap/settings object at all. Given that the HashMap is just a dumb container holding arbitrary data that is supposed to be used by other classes, you put the validation into those classes that use the data. Problem solved.
should use $setting->foo[subsetting] over $setting->data[foo[subsetting]]
Well, yeah. As you probably have guessed already, the above implementation doesnt use this notation. It uses $settings['foo'] = 'bar' and you cannot use $settings['foo']['bar'] with ArrayAccess (at least to my knowledge). So that is somewhat of a limitation.
settings class needs to give access to the settings data for other classes
This and the next requirement smell like Singleton to me. If so, think again. All you ever need is to instantiate the settings class once in your bootstrap. You are creating all the other classes that are required to fulfill the request there, so you can inject all the settings values right there. There is no need for the Settings class to be globally accessible. Create, inject, discard.
first time an instance is made, the settings need to be loaded from a file
See above.
The part $foo->bar is actually calling __get, this function should (in your case) return an array.
returning the right array in the __get would then be your solution.
As has been stated, this is because it is the array stored in $foo->bar that is being modified rather than the class member. The only way to invoke __set behaviour on an 'array' would be to create a class implementing the ArrayAccess interface and the offsetSet method, however this would defeat the purpose of keeping the settings in the same object.
A reasonably neat and common work around is to use dot delimited paths:
class Settings {
protected $__settings = array();
// Saves a lot of code duplication in get/set methods.
protected function get_or_set($key, $value = null) {
$ref =& $this->__settings;
$parts = explode('.', $key);
// Find the last array section
while(count($parts) > 1) {
$part = array_shift($parts);
if(!isset($ref[$part]))
$ref[$part] = array();
$ref =& $ref[$part];
}
// Perform the appropriate action.
$part = array_shift($parts);
if($value)
$ref[$part] = $value;
return $ref[$part];
}
public function get($key) { return $this->get_or_set($key); }
public function set($key, $value) { return $this->get_or_set($key, $value); }
public function dump() { print_r($this->__settings); }
}
$foo = new Settings();
$foo->set('somename', 'something');
$foo->set('bar.baz', 'somethingelse');
$foo->dump();
/*Array
(
[somename] => something
[bar] => Array
(
[baz] => somethingelse
)
)*/
This also makes it clearer you are not manipulating instance variables, as well as allowing arbitrary keys without fear of conflicts with instance variables. Further processing for specific keys can be achieved by simply adding key comparisons to get/set e.g.
public function set(/* ... */) {
/* ... */
if(strpos($key, 'display.theme') == 0)
/* update the theme */
/* ... */
}
I'm trying to get my head around using Iterators effectively in PHP 5, and without a lot of decent examples on the net, it's proving to be a little difficult.
I'm trying to loop over a directory, and read all the (php) files within to search for defined classes. What I then want to do is have an associative array returned with the class names as keys, the the file paths as the values.
By using a RecursiveDirectoryIterator(), I can recurse through directories.
By passing this into a RecursiveIteratorIterator, I can retrieve the contents of the directory as a single dimensional iterator.
By then using a filter on this, I can filter out all the directories, and non-php files which will just leave me the files I want to consider.
What I now want to do is be able to pass this iterator into another iterator (not sure which would be suitable), such that when it loops over each entry, it could retrieve an array which it needs to combine into a master array.
It's a little complicated to explain, so here's a code example:
// $php_files now represents an array of SplFileInfo objects representing files under $dir that match our criteria
$php_files = new PhpFileFilter(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)));
class ClassDetector extends FilterIterator {
public function accept() {
$file = $this->current(); // get the current item, which will be an SplFileInfo object
// Match all the classes contained within this file
if (preg_match($regex, $file->getContents(), $match)) {
// Return an assoc array of all the classes matched, the class name as key and the filepath as value
return array(
'class1' => $file->getFilename(),
'class2' => $file->getFilename(),
'class3' => $file->getFilename(),
);
}
}
}
foreach (new ClassDetector($php_files) as $class => $file) {
print "{$class} => {$file}\n";
}
// Expected output:
// class1 => /foo.php
// class2 => /foo.php
// class3 => /foo.php
// class4 => /bar.php
// class5 => /bar.php
// ... etc ...
As you can see from this example, I'm kind of hijacking the accept() method for FilterIterator, which is completely incorrect usage I know - but I use it only as an example to demonstrate how I just want the one function to be called, and for it to return an array which is merged into a master array.
At the moment I'm thinking I'm going to have to use one of the RecursionIterators, since this appears to be what they do, but I'm not fond of the idea of using two different methods (hasChildren() and getChildren()) to achieve the goal.
In short, I'm trying to identify which Iterator I can use (or extend) to get it to pass over a single-dimensional array(?) of objects, and get it to combine the resulting array into a master one and return that.
I realise that there are several other ways around this, ala something like:
$master = array();
foreach($php_files as $file) {
if (preg_match($regex, $file->getContents(), $match)) {
// create $match_results
$master = array_merge($master, $match_results);
}
}
but this defeats the purpose of using Iterators, and it's not very elegant either as a solution.
Anyway, I hope I've explained that well enough. Thanks for reading this far, and for your answers in advance :)
Right, I managed to get my head around it eventually. I had to use a Recursive iterator because the input iterator is essentially generating child results, and I extended IteratorIterator which already had the functionality to loop over an Iterator.
Anyways, here's a code example, just in case this helps anyone else. This assumes you've passed in an array of SplFileInfo objects (which are the result of a DirectoryIterator anyway).
class
ClassMatcher
extends
IteratorIterator
implements
RecursiveIterator
{
protected $matches;
public function hasChildren() {
return preg_match_all(
'#class (\w+)\b#ism',
file_get_contents($this->current()->getPathname()),
$this->matches
);
}
public function getChildren() {
$classes = $this->matches[1];
return new RecursiveArrayIterator(
array_combine(
$classes, // class name as key
array_fill(0, count($classes), $this->current()->getPathname()) // file path as value
)
);
}
}
I once did something similar. The source is right here and I believe is easily understandable. If you have any problem with it please let me know.
The main idea is to extend SplFileInfo and then use RecursiveIteratorIterator::setInfoClass($className); in order to obtain information about the source code. A Filter for parsing only PHP files could be nice though I decided back then to filter them by extension in the main loop.
I have a very strange problem, when I try to var_dump (or print_r) a Doctrine Object, my Apache responses with an empty blank page (200 OK header). I can var_dump a normal php var like:
$dummy = array("a" => 1, "b" =>2);
And it works fine. But I can't with any object from any Doctrine class, (like a result from $connection->query(), or an instance of a class from my object model with Doctrine).
Anybody knows why this happens?
Lazy load proxies always contain an instance of Doctrine’s EntityManager and all its dependencies.
Therefore a var_dump will possibly dump a very large recursive structure which is impossible to render and read. You have to use \Doctrine\Common\Util\Debug::dump() to restrict the dumping to a human readable level. Note that the default depth for this function is set to 2 (it's the second parameter)
Use the toArray method of the Doctrine_Record class
var_dump($doctrine_record->toArray());
will only display the DB fields and avoid dumping the complete Doctrine internals (which contains self reference/recursion btw)
I've had that sometimes when trying to print_r() a self-referencing object - it gets into a loop and runs out of memory. Possibly that's what's happening to you.
Try increasing the memory limit (ini_set('memory_limit', '256M');) and see if that fixes it.
Edit: I don't think there's an actual fix for this - it's PHP's internal var_dump / print_r that don't limit depth on recursion (or don't do it properly, at least). If you install the XDebug extension, this can replace the built-in var_dump with a version that handles recursion much better.
You can use toArray if you are sure the object is an instance of Doctrine_Collection.
Xdebug does not help with doctrine records.
The way I suggest is implementing a custom recursive function to print object, that use Doctrine_Record::toArray() when neeeded
function var_dump_improved()
{
foreach (func_get_args() as $arg) {
if ($args instanceof Doctrine_Collection) {
print_r($arg);
} else if ( $arg instanceof Traversable || is_array($arg) ) {
// do a foreach and recall var_dump_improved on subelements
} else if (...) {
// other types
}
}
}
Some recursive function to debug with max nesting levels are here
http://php.net/manual/en/function.var-dump.php
Look at the comments, look for "recursion"