I have a class called Type:
class Type { ... }
Which has a property named $value:
class Type {
protected $value;
//More code here...
}
I wish that when I attempt to use a function, when I pass the object, the value of $obj->value will be passed. For example:
$obj = new Type("value");
echo $obj; //Desired output: value
I've tried many things, and searched everywhere, but I can't seem to find this one. Is it even possible?
Thanks in advance. :)
EDIT: Maybe you misunderstood (or narrowed) my question a bit. I want it for all value types, not just the string ones, including int float and boolean
$obj = new Type(true);
echo !$obj; //Desired output: false
$obj2 = new Type(9);
echo ($obj + 1); //Desired output: 10
See: Magic Methods
class Type {
public function __toString() {
return (string)$this->value;
}
}
If your usage of the object won't trigger the magic you can allways use:
$someVar = (string)$obj;
Update:
Beyond strings or arrays (see ArrayAccess interface), it's, at the moment, not possible to have php handle objects like predefined data types.
Related
I'm trying to filter an array of objects implementing a specific interface (which simply defines the isComplete(): bool method) based on the result of that method. array_filter doesn't work because it can't call a method on each object to determine whether to filter it (or can it?). I've tried writing a function that takes the splatted array as an argument by reference, this doesn't work either:
function skipIncomplete(CompletableObjectInterface &...$objects): array {
$skipped = [];
foreach ($objects as $index => $item) {
if (!$item->isComplete()) {
$skipped[] = $item->id ?? $index;
unset($objects[$index]);
}
}
return $skipped;
}
The original elements passed in simply don't end up getting unset.
I'm looking for a way that doesn't include creating an entirely new Collection class to hold my CompletableObjects for complexity reasons. I really want to keep the type hint so no one can pass in a generic array, causing runtime errors when the function tries to call $item->isComplete.
Is there any way I can achieve this in PHP 7.3.15?
Added a filter, please comment as to what is wrong with this type of approach:
<?php
interface CompletableObjectInterface {
public function isComplete() : bool;
}
class Foo implements CompletableObjectInterface
{
public function isComplete() : bool
{
return false;
}
}
class Bar implements CompletableObjectInterface
{
public function isComplete() : bool
{
return true;
}
}
$foo = new Foo;
$bar = new Bar;
$incomplete = array_filter([$foo, $bar], function($obj) { return !$obj->isComplete();});
var_dump($incomplete);
Output:
array(1) {
[0]=>
object(Foo)#1 (0) {
}
}
Looks like you got a bit hung up on a wrong understanding of the ... syntax for a variable number of arguments.
You are passing in one array, and the $objects parameter will therefore contain that array in the first index, i.e. in $objects[0].
So in theory you could just change your line
unset($objects[$index]);
to
unset($objects[0][$index]);
However, I do not really see why the variable number of arguments syntax is used at all, since you apparently are just expecting one array of values (objects in this case) as an argument to the function. Therefore I'd recommend you just remove the ... from the argument list and your solution does what you wanted.
Alternatively you can of course add an outer foreach-loop and iterate over all passed "arrays of objects", if that is an use case for you.
_toString() is called when an object is used as string. How can I do something similar for numerical values, something like __toInt(), or __toArray(). Do such methods exist? Is there a work around? Is it a bad idea to use something like that even if there is a workaround for it?
There is no __toArray magic-method (just check the ones that exist here), but then, there shouldn't be, IMO.
Though people have asked for a magic toArray method, it doesn't look like such a method will be implemented any time soon.
Considering what objects are for, and how we use them, a toInt method wouldn't make much sense, and since all objects can be cast to an array, and can be iterated over, I see very little point in using __toArray anyway.
To "convert" on object to an array, you can use either one of the following methods:
$obj = new stdClass;
$obj->foo = 'bar';
var_dump((array) $obj);
//or
var_dump(json_decode(json_encode($obj), true));
This can be done with both custom objects, as stdClass instances alike.
As far as accessing them as an array, I can't see the point. Why write a slow magic method to be able to do something like:
$bar = 'foo';
$obj[$bar];
if you can do:
$obj->{$bar}
or if you can do:
foreach($obj as $property => $value){}
Or, if you need something a tad more specific, just implement any of the Traversable interfaces.
And for those rare cases, where you want an object to produce an array from specific properties in a very particular way, just write a method for that and call that method explicitly.
class ComplexObject
{
private $secret = null;
private $childObject = null;
public $foo = null;
//some methods, then:
public function toArray()
{//custom array representation of object
$data = array();
foreach($this->childObject as $property => $val)
{
if (!is_object($this->childObject->{$property}))
{
$data[$property] = $val;
}
}
$data['foo'] = $this->foo;
return $data;
}
//and even:
public function toJson()
{
return json_encode($this->toArray());
}
}
Ok, you have to call these methods yourself, explicitly, but that's not that hard, really... is it?
Assume this class code:
class Foo {
function method() {
echo 'works';
}
}
Is there any way to store a reference to the method method of a Foo instance?
I'm just experimenting and fiddling around, my goal is checking whether PHP allows to call $FooInstance->method() without writing $FooInstance-> every time. I know I could write a function wrapper for this, but I'm more interested in getting a reference to the instance method.
For example, this pseudo-code would theoretically store $foo->method in the $method variable:
$foo = new Foo();
$method = $foo->method; //Undefined property: Foo::$method
$method();
Apparently, as method is a method and I'm not calling it with () the interpreter thinks I'm looking for a property thus this doesn't work.
I've read through Returning References but the examples only show how to return references to variables, not methods.
Therefore, I've adapted my code to store an anonymous function in a variable and return it:
class Foo {
function &method() {
$fn = function() {
echo 'works';
};
return $fn;
}
}
$foo = new Foo();
$method = &$foo->method();
$method();
This works, but is rather ugly. Also, there's no neat way to call it a single time, as this seems to require storing the returned function in a variable prior to calling it: $foo->method()(); and ($foo->method())(); are syntax errors.
Also, I've tried returning the anonymous function directly without storing it in a variable, but then I get the following notice:
Notice: Only variable references should be returned by reference
Does this mean that returning/storing a reference to a class instance method is impossible/discouraged or am I overlooking something?
Update: I don't mind adding a getter if necessary, the goal is just getting a reference to the method. I've even tried:
class Foo {
var $fn = function() {
echo 'works';
};
function &method() {
return $this->fn;
}
}
But from the unexpected 'function' (T_FUNCTION) error I'd believe that PHP wisely doesn't allow properties to store functions.
I'm starting to believe that my goal isn't easily achievable without the use of ugly hacks as eval().
It is. You have to use an array, with two values: the class instance (or string of the class name if you are calling a static method) and the method name as a string. This is documented on the Callbacks Man page:
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
Demo (Codepad):
<?php
class Something {
public function abc() {
echo 'called';
}
}
$some = new Something;
$meth = array($some, 'abc');
$meth(); // 'called'
Note this is also works with the built-ins that require callbacks (Codepad):
class Filter {
public function doFilter($value) {
return $value !== 3;
}
}
$filter = new Filter;
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array($filter, 'doFilter'))); // 'array(1,2,4,5)'
And for static methods -- note the 'Filter' instead of an instance of a class as the first element in the array (Codepad):
class Filter {
public static function doFilter($value) {
return $value !== 3;
}
}
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array('Filter', 'doFilter'))); // 'array(1,2,4,5)'
// -------- or -----------
var_dump(array_filter($test, 'Filter::doFilter')); // As of PHP 5.2.3
Yes, you can. PHP has a "callable" pseudo-type, which is, in fact, either just a string or an array. Several functions (usort comes to mind) accept a parameter of the "callback" type: in fact, they just want a function name, or an object-method pair.
That's right, strings are callable:
$fn = "strlen";
$fn("string"); // returns 6
As mentioned, it's possible to use an array as a callback, too. In that case, the first element has to be an object, and the second argument must be a method name:
$obj = new Foo();
$fn = array($obj, "method");
$fn(); // calls $obj->method()
Previously, you had to use call_user_func to call them, but syntax sugar in recent versions make it possible to perform the call straight on variables.
You can read more on the "callable" documentation page.
No, as far as I know it's not possible to store a reference to a method in PHP. Storing object / class name and a method name in an array works, but it's just an array without any special meaning. You can play with the array as you please, for example:
$ref = [new My_Class(), "x"];
// all is fine here ...
$ref();
// but this also valid, now the 'reference' points to My_Other_Class::x()
// do you expect real reference to behave like this?
$ref[0] = new My_Other_Class();
$ref();
// this is also valid syntax, but it throws fatal error
$ref[0] = 1;
$ref();
// let's assume My_Class::y() is a protected method, this won't work outside My_Class
$ref = [new My_Class(), 'y'];
$ref();
this is prone to error as you loose syntax checking due to storing the method name as string.
you can't pass reliably a reference to a private or a protected method this way (unless you call the reference from a context that already has proper access to the method).
Personally I prefer to use lambdas:
$ref = function() use($my_object) { $my_object->x(); }
If you do this from inside $my_object it gets less clunky thanks to access to $this:
$ref = function() { $this->x(); }
this works with protected / private methods
syntax checking works in IDE (less bugs)
unfortunately it's less concise
I came across plenty of examples of method chaining in PHP, but couldn't find anything about this one, so I'm asking for help you guys;)
My problem is - can I in some way find out if the method in chain is the last one? In most cases people are using some sort of final method (execute, send,..) to tell when the chain ends and return the corresponding result. But I wonder if there is some hidden magic method or technique than can check all the methods in chain and detect if there is no next method?
Without final method it works fine for strings (in the very simple example), but not if I want to return array.
Here is my snippet :
class Chain {
private $strArray;
function __call($name, $args) {
$this->strArray[] = $args[0];
return $this;
}
function __toString() {
return implode('-', $this->strArray);
}
}
// example 1
$c = new Chain();
$x = $c->foo('hi')->bar('stack'); // need array('hi', 'stack')
// example 2
$c = new Chain();
$x = $c->foo('hi')->bar('stack')->foobar('overflow'); // need array('hi', 'stack', 'overflow')
// example 3
$c = new Chain();
echo $c->foo('hi')->foobar('overflow'); // prints 'hi-overflow'
// example 4
$c = new Chain();
echo $c->foo('hi')->bar('stack')->foobar('overflow'); // prints 'hi-stack-overflow'
You see, when I want to print the result of chain, I can modify the result in the __toString method, but what if I need an array (example 1, 2)? Is there any way to achieve that without calling some additional "final" method?
Thanks a lot for help and let me know if you need more info.
EDIT: After feedback from #bandi I tried to extend ArrayObject like this.
class Chain extends ArrayObject {
function __call($name, $args) {
$this->append($args[0]);
return $this;
}
function __toString() {
return implode('-', $this->getIterator()->getArrayCopy());
}
}
// returned ref. to object, works fine in loop or accessing offset
$obj = new Chain;
$x = $obj->foo('hi')->bar('stack')->foobar('overflow'); // need array('hi', 'stack', 'overflow')
foreach ($x as $y) {
echo $y, "\n";
}
var_dump($x[0], $x[1], $x[2]);
// returned String
$c = new Chain;
echo $c->foo('hi')->foobar('overflow'); // prints 'hi-overflow'
It does what I wanted, however I don't feel so good about the $this->getIterator()->getArrayCopy() part. Is there some simple way of accessing the array (internally in ["storage":"ArrayObject":private])?
Thanks
Method chaining is using the return value of a function, in this case this is the reference to the object. Predicting the use of a returned value is generally not possible.
You have to tell the called method that you want to do something different. You can use the second argument for this, e.g. you return a different result if the second argument is defined. The other option might be to modify the class so that it behaves like an array except if it is printed.
I'm looking for a functionality, in the example below called "theFunctionILookFor", that will make the code work.
$myClassName = "someName";
$parentOrInterfaceName = "someParent";
if (theFunctionILookFor($myClassName)) {
echo "is parent";
}
Edit: I try to avoid instantiating the class just to perform this check. I would like to be able to pass 2 string parameters to the checking function
Looks like this is my answer: is_subclass_of
It can accept both parameters as strings
http://php.net/manual/en/function.is-subclass-of.php
Try is_subclass_of(). It can accept both parameters as strings. http://php.net/manual/en/function.is-subclass-of.php
Using the Reflection API, you can construct a ReflectionClass object using the name of the class as well as an object.
You might use this?
http://www.php.net/manual/en/function.get-parent-class.php
From the page:
get_parent_class
(PHP 4, PHP 5, PHP 7)
get_parent_class — Retrieves the parent class
name for object or class
Description
string get_parent_class ([ mixed $object ] )
Retrieves the parent
class name for object or class.
This is an old thread, but still for future reference, you can use the functions is_a and is_subclass_of to accomplish this in a more efficient way.
No need to instantiate anything, you can just pass your string like:
if (is_subclass_of($myObject, 'SomeClass')) // is_a(...)
echo "yes, \$myObject is a subclass of SomeClass\n";
else
echo "no, \$myObject is not a subclass of SomeClass\n";
PHP Docs:
http://www.php.net/manual/en/function.is-a.php
http://www.php.net/manual/en/function.is-subclass-of.php
I think that instanceof is a lot faster and more clear to read.
instanceof does not work on strings, because it can be used as a "identifier" for a class:
$temp = 'tester';
$object = new sub();
var_dump($object instanceof $temp); //prints "true" on php 5.3.8
class tester{}
class sub extends \tester{
}
Also, it works on interfaces - you just need a variable to contain the absolute class name (including namespace, if you want to be 100% sure).
Maybe it's old question, but problem still is actual, here, below, the best solution I've found
class ProductAttributeCaster
{
public function cast(mixed $value): mixed
{
if ($value instanceof ($this->castTo())) {
// do stuff
}
return $value;
}
function castTo(): string
{
return ProductAttribute::class;
}
}