I have this class:
class ResultSet
implements ArrayAccess, Countable, Iterator {
/// Rest of implementation ...
}
I'm running into a problem using usort() and passing my object as the first parameter. usort() expects an array instead of an object, but given my implementation of the ArrayAccess interface I don't know what else it could be needing.
The exact error returned by php is:
Warning: usort() expects parameter 1 to be array, object given.
How would usort know how you've implemented ArrayAccess? There is no defined place where the values are kept -- that flexibility is the whole point of the interface.
If you are storing the elements in an array that is a private member of the object, you could proxy the usort operation. For example:
public function usort($callback) {
usort($this->container, $callback);
}
If memory serves, there's a big warning sign on the ArrayAccess page, or in one of the comments on it (probably the latter, in fact). It basically says something in the order of: the interface is somewhat useless, because PHP's array functions don't recognize any of its members as arrays.
Related
We have objects that implement __toString():
class Foo {
public function __toString()
{
return 'bar';
}
}
Then we have functions that return either string or object that implement __toString(), like the example above.
While using return type string for strings works of course:
function getString():string {
return 'Works!';
}
Returning object of type Foo does not work, because it's not a string:
function getString(Foo $foo):string {
return $foo; // Fatal TypeError!
}
Is there any PHP interface that we can use to type hint both string and objects that implement that unknown interface, e.g. Printable or Stringable (lol)?
The goal is to being able to return either string or objects that implement a specific interface (which would enforce the implementation of __toString).
The simple answer is "no" - there is no built-in "stringable" hint, although it has occasionally been suggested in the past.
As of PHP 8.0, there will be support for "union types", so string|Stringable would represent "either a string or an instanceof Stringable". However, there's no such interface as Stringable, so this can't be used to detect implementations of __toString.
Part of the reason for this, and the design question you need to ask, is what contract are you actually representing here? From the point of view of the calling code, there is no guarantee that the return value can actually be used as a string directly - there are many operations which will behave differently for a string and an object, or which will accept a string but not cast an object automatically. So the only thing that's actually guaranteed by such a hint is that (string)$foo will give a string.
So you have a few options:
As Stefan says, always return an object, implementing some interface Stringable. If the function wants to return a string, it would first wrap it in a simple object.
Instead of a generic Stringable, come up with a more meaningful interface to return; maybe what you actually return is something Loggable, for instance. This way, wrapping the string can give you more concrete benefits: you can add other methods to the interface, and the calling code doesn't need to do an "object or string" check before calling those methods.
If the calling code is only ever going to use the return value as a string, don't return the object at all. Mark the function as returning string, and cast any objects to string before returning them.
If the calling code is expected to inspect the return value and do different things with different kinds of objects, don't limit the return type at all. In PHP 8, you might want to add a union type, but only if there's a short list of possibilities; a return type that's too broad is barely useful as a contract anyway.
As of PHP 8, you will have Union Types and the new interface Stringable. You can utilize these two concepts in combination to get the result you want.
function getString(Stringable|string $foo) : string {
// Fatal TypeError if $foo is not a string or a class that implements Stringable
return $foo instanceof Stringable ? $foo->__toString() : $foo;
}
In PHP 8, any class that has a __toString() method will automatically implement the Stringable interface under the hood.
You could box your string in a wrapper class that just takes the string in the c-tor and has a toString() method that returns it.
This wrapper class would implement your Stringable interface, as do your other classes.
Then you can provide a return-type hinted method that returns objects of type Stringable.
This method can decide whether it returns one of your objects or just a wrapper for a string, as all of them implement Stringable.
However, this is to be balanced against just returning mixed and checking the returned value type in your calling method.
Further, if your objects implement the magic method __toString, then all your calling methods could always apply a cast (string) on the returned value.
If the returned value is a string, then it just keeps being a string.
If it is one of your objects that implements the magic method, then you'll get the string.
Yet you won't have the return-type hint in that case.
Strings and objects implementing __toString are not interchangeable, for instance $s[1] will fail on an object (unless it implements some other magic methods). An object might be a valid substitute in contexts where an argument is coerced to a string anyway, but that doesn't apply to all possible operations you can do on a string. So, no, there's no native interface for that, and you shouldn't intermix these types for this reason either.
I need to get the public properties of an object; is there a preferred method for doing this? I'm wary of using ReflectionObject#getProperties() because of the Reflection API overhead, but between casting the object to an array and using get_object_vars(), is there an established standard or clear performance gain for one or the other?
To be clear, I realize that casting the object to an array will give me all the object's properties, but as protected properties will be prepended with * and private properties will be prepended with the class name, it would still be effective for a quick in_array($property, $properties); call.
As you want to get the public properties of the object, you should definitely use get_object_vars, which is a function specifically designed for that task, instead of using some obscure (array) cast with unclear behavior.
It depends, they don't do the same thing.
get_object_vars() will return only the variable that are visible from the calling scope (e.g. it may or may not return protected or private variable).
Casting to array returns all properties, including private ones.
I've seen alot of coders implement SimpleXML_load_string() instead of the SimpleXMLElement() class. Are there any benefits to using the former over the latter? I've read the PHP manual on simplexml. I couldn't manage to get a grasp on what it is.
Any help and guidance (perhaps through examples) would be much appreciated. Thanks in advance.
simplexml_load_string() (as the name suggest) load xml from a string and returns an object of SimepleXMLElement. There is no difference between this and using just the usual constructor of the class.
Update:
SimpleXML::__construct() has an additional parameter (the third one) bool $data_is_url = false. If this is true the behavior is the same as simplexml_load_file() (in conjunction with the common stream wrappers). Thus both simplexml_load_*()-functions cover the same functionality as SimpleXML::__construct().
Additional the functions simplexml_load_*() has a second parameter string $class_name = "SimpleXMLElement" to specify the class of the object to get returned. Thats not a specific feature of the functions, because you can "use" something very similar with usual instanciation too
class MyXML extends SimpleXMLElement {}
$a = new MyXML($xml);
$b = simplexml_load_string($xml, 'MyXML');
A difference between the OOP- and the procedural approach is, that the functions return false on error, but the constructor throws an Exception.
It's mostly a convenience wrapper. With constructing the base element yourself, you need at least two lines of code to accomplish anything. With simplexml_load_string() a single expression might suffice:
print simplexml_load_string($xml)->title;
Is shorter than:
$e = new SimpleXMLElement($xml);
print $e->title;
And then of course, there is also the slight variation in the function signature.
Update: And exactly the same length as of
print(new SimpleXMLElement($xml))->title;
simplexml_load_string can return a different object:
class_name
You may use this optional parameter so that
simplexml_load_string() will return an
object of the specified class. That
class should extend the
SimpleXMLElement class.
This question already has answers here:
How to use class methods as callbacks
(5 answers)
Closed 10 months ago.
I'm trying to understand some code I found the open source oauth-php library. The relevant code snippet is:
protected function sql_printf ( $args )
{
$sql = array_shift($args);
if (count($args) == 1 && is_array($args[0]))
{
$args = $args[0];
}
$args = array_map(array($this, 'sql_escape_string'), $args);
return vsprintf($sql, $args);
}
Where $args is an array of arguments that contain variables intended for use in a formatted printing operation. I looked at the docs for array_map:
http://php.net/manual/en/function.array-map.php
and the user comments and I did not see any use case where the first parameter in a call to array_map() was an array itself. In all the use cases I saw, the first parameter was either NULL or a (callback) function. It seems pretty obvious to me that the code takes the $args array and then builds a new array with the arguments sanitized by $this->sql_escape_string().
But the statement "array($this, 'sql_escape_string')" is throwing me since I would have expected simply '$this->sql_escape_string', or is that not a valid syntax? If so, how does wrapping $this and 'sql_escape_string' in an array create a valid callback function for array_map() to use?
It is actually passing the sql_escape_string method from the class itself as a callback. It is a way of clarifying ambiguous method calls. For example:
array_map('sql_escape_string', $args);
of course applies sql_escape_string() to each value in $args, whereas:
array_map(array($someClass, 'sql_escape_string'), $args);
applies the sql_escape_string() method from $someClass to each value in $args.
The first parameter is a callback. It can be either a string or an array.
since I would have expected simply '$this->sql_escape_string'
You would if it were just one scalar value. But you have an array and you need to apply that escape function to each item of the $args array. So you need to implement foreach and apply that function or use one-liner with array_map.
But the statement "array($this, 'sql_escape_string')" is throwing me since I would have expected simply '$this->sql_escape_string', or is that not a valid syntax?
It's valid, but doesn't refer to what you think it refers to. Consider free functions, constants, class names and variables: each exists in different environments (or "namespaces" if you prefer, but that's easily confused with PHP namespaces). The different environment for variables is made explicit by the use of "$" as a sigil: the variable $foo versus the function foo(), constant foo and class Foo. This is also why constants and variables are case-sensitive, but functions and class names aren't: the different environments allow for different name resolution rules.
Similarly, object methods and properties exist in different environments. As a consequence, $this->sql_escape_string refers to a property, not a method. To confuse matters, that property could contain a callable, though such a callable couldn't be invoked directly:
class Foo {
function frob() {return 23.0 / 42;}
}
$foo = new Foo;
$foo->frob = function () {return 0 / 0;};
$foo->frob(); # calls method, not closure function
$frob = $foo->frob;
$frob(); # oops: division by zero
As with constants and functions, properties and methods are distinguished by the absence or presence of an argument list.
If so, how does wrapping $this and 'sql_escape_string' in an array create a valid callback function for array_map() to use?
PHP's syntax for callable references goes beyond strings.
Free functions (functions not associated with a class or object; contrast with "bound functions") can unambiguously be referred to by their names. Static methods are bound to a class, but can be referred to with a string if it includes the class name (the syntax is "Class::method"). A string cannot contain enough information for an object method, however, since the method must be bound to a particular object, and PHP doesn't have a way to refer to an object using a string. The solution PHP's developers settled on was to use array syntax (as shown in the question sample code). They also included support for array syntax for static methods (array('Class', 'method')).
Besides callable references, callables can be closures. These offer an alternative way of passing object methods, but are more verbose and complex.
$self = $this; # workaround: $this not accessible in closures before 5.4
$args = array_map(
function ($value) use($self) {
return $self->sql_escape_string($value);
}, $args);
Closures aren't so useful when a callable reference will do, but are more powerful overall.
Would it be possible to write a class that is virtually indistinguishable from an actual PHP array by implementing all the necessary SPL interfaces? Are they missing anything that would be critical?
I'd like to build a more advanced Array object, but I want to make sure I wouldn't break an existing app that uses arrays everywhere if I substituted them with a custom Array class.
The only problems i can think of are the gettype() and the is_array() functions.
Check your code for
gettype($FakeArray) == 'array'
is_array($FakeArray)
Because although you can use the object just like an array, it will still be identified as an object.
In addition to the points made above, you would not be able to make user-space array type hints work with instances of your class. For example:
<?php
function f(array $a) { /*...*/ }
$ao = new ArrayObject();
f($ao); //error
?>
Output:
Catchable fatal error: Argument 1 passed to f() must be an array, object given
Other differences include the '+' operator for arrays (merging) and the failure of the entire array_* functions, including the commonly used array_merge and array_shift.