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.
Related
In PHP it is possible to get a full class name via class name resolution like this:
Example:
namespace Name\Space;
class ClassName {}
echo ClassName::class;
Output: Name\Space\ClassName
This is better than using the string Name\Space\ClassName directly in the code because code introspection especially in IDEs can find an error directly.
I wonder if there is something similar for methods of a class - this would be specifically useful for callback functions.
This is how you can basically can pass a callback:
$a = function($callback,$arg) { return $callback($arg); }
$a('getInfo',5);
Instead of passing a string here (which might change), I would prefer to do something like this:
$a(MyClass::class::getInfo,5);
With I "go to declaration" click in the IDE I could go directly to getInfo plus I see errors in case with method does not exist anymore. Is there a way to achieve what I want to do here?
In fact, you work with callable type. And PHP allows setting method/function name only as a string. But if you use classes and objects you will have a different way to set callback. For example:
$a = function($callback, $arg) {
return call_user_func($callback, $arg));
}
// call a static method of the class with
// using fullname space and method name as a string
$a('Name\Space\MyClass::getInfo',5);
// call a static method of the class
// with using ::class
$a([MyClass::class, 'getInfo'], 5);
// call a method of an object
$myObject = new MyClass();
$a([$myOject, 'getInfo'], 5);
Three possibilities.
(1)
echo `__CLASS__`;
...returns namespace\classname as a string.
(2)
If you're trying to get the namespace\classname from another class, i.e., not the one where you're currently executing code, then I would suggest setting a public property inside each class such as:
public static $classname = __CLASS__;
which you could then access from anywhere as:
ClassName::$classname
Put it in each of your classes. Always use the same property name.
(3)
Have you considered the PHP function debug_backtrace() which returns a call stack with the most recent call at index = 0;
So, if:
$caller = debug_backtrace();
Then, $caller[0]['class'] contains the fully qualified class name, including any namespace, at the point where you called debug_backtrace().
I'm guessing that #2 is the solution that will work for you.
Just thought of a 4th possibility that doesn't depend on you adding any code to each class. Might add some overhead though, but so does my 3rd solution above.
(4)
$declared_classes = get_declared_classes();
This lists all of the classes currently declared within the PHP scope as fully qualified namespace\classname. You could search the returned array for partial string matches within the array and return the whole namespace\classname string.
One thing to keep in mind. You might have duplicates if different namespaces have same-named classes.
I've added this as a comment somewhere else but figured it might warrant an actual answer to this question. If you use:
$callback = [MyClass::class, 'myMethod'];
Then at least one IDE (PhpStorm) will recognize this as the callable that it is, allow you to navigate to it, mention it in "show usages" and automatically change it when it is renamed through a refactor. I use this in my code if, for instance, I reference a method in a test:
$this->mock(MyClass::class, function(MockInterface $mock) {
$mock->shouldReceive([MyClass:class, 'myMethod'][1])->andReturn(10);
});
Not the cleanest syntax, but it's workable.
This may be a basic question, but it has kept me wondering for quite some time now.
Should I declare all private/local variables being private? Or is this only necessary for "important" variables?
For instance, I have the (temporary) result of a calculation. Should I pre-declare this variable?
Hope someone can point this out.
Since you're talking about private, protected and public I take it you're talking about properties, instead of variables.
In that case: yes, you should declare them beforehand.
Because of how PHP objects are designed, an array (properties_table) is created on compile time. This array ensures that accessing a given property is as fast as possible. However, if you add properties as you go along, PHP needs to keep track of this, too. For that reason, an object has a simple properties table, too.
Whereas the first (properties_table) is an array of pointers, the latter is a simple key => value table.
So what? Well, because the properties_table contains only pointers (which are of a fixed size), they're stored in a simple array, and the pointers are fetched using their respective offsets. The offsets are stored in yet another HashTable, which is the ce->properties_info pointer.
As bwoebi pointed out to me in the comments: getting the offset (HashTable lookup) is a worst-case linear operation (O(n)) and predefined property lookups are constant-time complex operations (O(1)). Dynamic properties, on the other hand need another HashTable lookup, a worst-case linear operation (O(n)). Which means that, accessing a dynamic property takes in average about twice as long. Authors of the Wikipedia can explain Time-Complexity far better than I can, though.
At first, access modifiers might seem irrelevant. As you go along, you'll soon find that sometimes, you just don't want to take the chance that some property of some object gets modified by some bit of code. That's when you see the value of private.
If an object contains another object, that holds all sorts of settings that your code will rely upon, for example, you'll probably use a getter method to access those settings from the outside, but you'll leave that actual property tucked away nicely using private.
If, further down the line, you're going to add data models and a service layer to your project, there's a good change you'll write an (abstract) parent class, if only for type-hinting.
If those service instances contain something like a config property, you'll probably define that getter in the parent class (to only define it once). private means that only the current class has access to a property, but since you're not going to have an instance of the parent to work with, but an instance of the child, you'll see why protected is invaluable when dealing with larger projects, too.
As far as temporary variables are concerned, be it in methods, functions or anywhere else, you don't have to predeclare them, except for, in certain cases arrays:
public function foo()
{
$temp = $this->getSomeValue();
return $temp ? $temp +1 : null;
}
Is perfectly valid, and wouldn't work any better if you were to write
public function foo()
{
$temp;// or $temp = null;
$temp = $this->getSomeValue();
return $temp ? $temp +1 : null;
}
However, it's not uncommon to see simething like this:
public function bar($length = 1)
{
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
This code relies on PHP being kind enough to create an array, and assign it to $return when the $return[] = rand(); statement is reached. PHP will do so, but setting your ini to E_STRICT | E_ALL will reveal that it doesn't do so without complaining about it. When passing 0 to the method, the array won't be created, and PHP will also complain when it reaches the return $return; statement: undeclared variable. Not only is it messy, it's also slowing you down! You're better off declaring $return as an array at the top of the scope:
public function bar($length = 1)
{
$return = array();//that's it
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
To be on the safe side, I'd also check the argument type:
/**
* construct an array with random values
* #param int $length = 1
* #return array
**/
public function bar($length = 1)
{
$length = (int) ((int) $length > 0 ? $length : 1);//make length > 0
$return = array();
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
In most if not all cases: yes.
If the variables are class properties they absolutely should be declared before use.
If the variable is local to a function, declare it in that function before you use it. Function variables are confined to the function's scope (local variables). They don't have to be declared before use but it's good practice to do so, and it gets rid of a warning message if you do. If they are not used anywhere else, they should not be properties though,
If you are using it in the context of the whole class, then yes, you should define your variable as a member of the class.
However, if you are talking about a local variable within the context of a single function and the variable does not need to be used elsewhere (or is not returned), then no.
Essentially you need to determine the importance and scope of your variable before deciding whether to make it a class property or not.
For example:
<?php
class Test {
private $test; // Private property, for use in the class only
public $public_test; // Public Property, for use both internally and external to the class as a whole
public function testing() {
$local = 5; // Local variable, not needed outside of this function ever
$this->test = rand(1, 5);
$calc = $local * $this->test; // Local variable, not needed outside of this function ever
$this->public_test = $calc / 2; // The only thing that the whole class, or public use cares about, is the result of the calculation divided by 2
}
}
It's generally a good rule of thumb for variables to define and initialize them before use. That includes not only definition and initial value but also validation and filtering of input values so that all pre-conditions a chunk of code is based on are established before the concrete processing of the data those variables contain.
Same naturally applies to object members (properties) as those are the variables of the whole object. So they should be defined in the class already (by default their value is NULL in PHP). Dynamic values / filtering can be done in the constructor and/or setter methods.
The rule for visibility is similar to any rule in code: as little as necessary (the easy rule that is so hard to achieve). So keep things local, then private - depending if it's a function variable or an object property.
And perhaps keep in the back of your mind that in PHP you can access private properties from within the same class - not only the same object. This can be useful to know because it allows you to keep things private a little bit longer.
For instance, I have the (temporary) result of a calculation. Should I pre-declare this variable?
This is normally a local variable in a function or method. It's defined when it receives the return value of the calculation method. So there is no need to pre-declare it (per-se).
...
function hasCalculation() {
$temp = $this->calculate();
return (bool) $temp;
}
...
If the calculation is/was expensive it may make sense to store (cache) the value. That works easily when you encapsulate that, for example within an object. In that case you'll use a private property to store that value once calculated.
Take these rule with a grain of salt, they are for general orientation, you can easily modify from that, so this is open to extend, so a good way to keep things flexible.
Really simple question but rather than asking for an academic explanation I want to keep it as pragmatic as possible: when will PHP create a copy of my huge class I'm passing into a function and when will it simply create a pointer to the relevant data? And if it creates pointers all the time, what's the use of specifying & (aside from closures, obviously)? I guess I don't know enough about the guts of the interpreter.
In PHP 5, all objects are passed by their handle. Whether you pass by reference or not is irrelevant in terms of performance. (In fact, passing by reference is warned to be slower in the manual.) The object you are working on inside the function is the same object as pointed to outside the function.
When you pass an object (handle) by reference, then you can alter what the outer variable points to. This is almost always unnecessary.
The & operator denotes a variable as being passed by reference.
$x = 'Hello World';
echo($x);
function asdf(&$var){
$var = 'Test';
}
asdf($x);
echo($x);
Same goes for assignment and pretty much any other statement. If it isn't passed or assigned by reference, assume it is passed or assigned by value instead.
Why bother with &, even though you can do so as please. This is how I do:
Assume I have a class 'Book' with some public methods and properties like title, author, year
then to make an object of it simply:
$book = new Book()
// then I can use all public methods and properties
$book->title;
$book->author;
$book->year;
If I like to then I can make a subclass say
class Novel extends Books{
function buildIt(Book $bk){
$bk->title;
// so on
}
}
In the function buildIt, I purposedly have an class object of Book 'parameter' in which
I can pass the whole object of class 'Book'.
I hope this help.
You can find a lot of uses of passing a variable by reference in the PHP manual. One of the best examples is preg_match.
preg_match will return the number of occurrences a pattern has been matched in the input string. It will then populate, if provided, a referenced $matches array containing the matches.
It can be seen as a way to return more than one value, although you ought to be careful with that. Per example:
class Server {
protected $_clientId = 0;
protected $_clients = array();
/**
* Get a pending connection.
*
* #param &$connection_id int The connection identifier.
* #return resource The socket resource.
*/
public function getNextClient(&$connection_id) {
$clientSocket = socket_accept($this->_server);
$connection_id = $this->_clientId++;
$this->_clients[$connection_id] = $clientSocket;
return $clientSocket;
}
}
$server = new Server;
$socket1 = $server->getNextClient($id);
echo $id; // 0
$socket2 = $server->getNextClient($id);
echo $id; // 1
Important note. Objects are passed-by-reference by default. They will not be cloned. Even without specifying the & in the function argument, modifying the passed object will result in the original object being modified as well. The only way to prevent this is to clone the object in the function/method.
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
# ...
}
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.