Is it possible to overload operators in PHP? - php

Specifically, I would like to create an Array class and would like to overload the [] operator.

If you are using PHP5 (and you should be), take a look at the SPL ArrayObject classes. The documentation isn't too good, but I think if you extend ArrayObject, you'd have your "fake" array.
EDIT: Here's my quick example; I'm afraid I don't have a valuable use case though:
class a extends ArrayObject {
public function offsetSet($i, $v) {
echo 'appending ' . $v;
parent::offsetSet($i, $v);
}
}
$a = new a;
$a[] = 1;

Actually, the optimal solution is to implement the four methods of the ArrayAccess interface:
http://php.net/manual/en/class.arrayaccess.php
If you would also like to use your object in the context of 'foreach', you'd have to implement the 'Iterator' interface:
http://www.php.net/manual/en/class.iterator.php

PHP's concept of overloading and operators (see Overloading, and Array Operators) is not like C++'s concept. I don't believe it is possible to overload operators such as +, -, [], etc.
Possible Solutions
Implement SPL ArrayObject (as mentioned by cbeer).
Implement Iterator (if ArrayObject is too slow for you).
Use the PECL operator extension (as mentioned by Benson).

For a simple and clean solution in PHP 5.0+, you need to implements the ArrayAccess interface and override functions offsetGet, offsetSet, offsetExists and offsetUnset. You can now use the object like an array.
Example (in PHP7+):
<?php
class A implements ArrayAccess {
private $data = [];
public function offsetGet($offset) {
return $this->data[$offset] ?? null;
}
public function offsetSet($offset, $value) {
if ($offset === null) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->data[$offset]);
}
public function offsetUnset($offset) {
unset($this->data[$offset]);
}
}
$obj = new A();
$obj[] = 'a';
$obj['k'] = 'b';
echo $obj[0], $obj['k']; // print "ab"

It appears not to be a feature of the language, see this bug. However, it looks like there's a package that lets you do some sort of overloading.

Put simply, no; and I'd suggest that if you think you need C++-style overloading, you may need to rethink the solution to your problem. Or maybe consider not using PHP.
To paraphrase Jamie Zawinski, "You have a problem and think, 'I know! I'll use operator overloading!' Now you have two problems."

Related

Instancing an object and calling a method in one line?

php 5.3
Is there a way to do this (viable in java for example)
(new MyClass())->myMethod();
i am receving: Parse error: syntax error, unexpected T_OBJECT_OPERATOR in D.. on line 7
Add
I really need that RFC to be implemented in the next PHP version!
http://wiki.php.net/rfc/instance-method-call
Is there a way we can subscribe to it so it can get more attention?
No, its not possible. There is a RFC for that
http://wiki.php.net/rfc/instance-method-call
But no one knows, when this will come to the userland.
Jacob mentioned the static method. There are other more or less useful methods to achieve the same
function instanciate($className, $arg1 = null) {
$args = func_get_args();
array_shift($args);
$c = new ReflectionClass($className);
return $c->newInstanceArgs($c);
}
instanciate('Classname', 1, 2, 3)->doSomething();
However, I prefer the temporary variable (like in the question).
Update:
I can swear there where an example for the temporary variable stuff in the question in the past. However, I meant this
$x = new Class;
$x->method();
where $x is the temporary variable.
That is not valid syntax. A handy way to achieve what you want is to use a static method to create the object.
In your MyClass:
public static function create() {
return new MyClass();
}
Then you can use:
MyClass::create()->myMethod();
However it is extra code that you have to maintain, if for example the constructor is changed or the class is extended. So you need to weigh up the benefits.
You can do something like this:
function chain_statements($statement1, $statement2) { return $statement2; }
class TClass { public Method() { ...; return $this; } }
$b = chain_statements($a = new TClass(), $a->Method());
... or more generalized:
function chain_statements(array $statements) { return end($statements); }
For example:
function chain_statements($statement1, $statement2) { return $statement2; }
function chain_statements2(array $statements) { return end($statements); }
class TClass
{
public $a = 0;
public function Method1() { $this->a = $this->a + 1; return $this; }
public function Method2() { $this->a = $this->a + 2; return $this; }
}
$b = chain_statements($c = new TClass(), $c->Method1()); echo($b->a);
$b = chain_statements2(array($c = new TClass(), $c->Method1(), $c->Method2())); echo($b->a);
... or even better:
function call_method($object) { return $object; }
$b = call_method(new TClass())->Method2(); echo($b->a);
Not as such. In PHP new is not an expression, but a language construct. The common workaround is to provide a static instantiation method for MyClass::get()->... use.
A more concise alternative is a hybrid factory function:
function MyClass() { return new MyClass; }
class MyClass {
...
}
Which then simplifies the instantiation to MyClass()->doSomething();
You can put it in one statement if you really wanted to. Use eval() ;p
But you probably shouldn't.
I had this same problem a while ago but I found this simple solution which is pretty readable too. I like the fact it uses only the standard PHP functions. There's no need to create any utility functions of your own.
call_user_func(
array(new ClassToInstance(), 'MethodName'),
'Method arguments', 'go here'
);
You can also use call_user_func_array to pass the arguments as an array.
call_user_func_array(
array(new ClassToInstance(), 'MethodName'),
array('Method arguments', 'go here')
);

Is there an integer equivalent of __toString()

Is there a way to tell PHP how to convert your objects to ints? Ideally it would look something like
class ExampleClass
{
...
public function __toString()
{
return $this->getName();
}
public function __toInt()
{
return $this->getId();
}
}
I realize it's not supported in this exact form, but is there an easy (not-so-hacky) workaround?
---------------------- EDIT EDIT EDIT -----------------------------
Thanks everybody! The main reason I'm looking into this is I'd like to make some classes (form generators, menu classes etc) use objects instead of arrays(uniqueId => description). This is easy enough if you decide they should work only with those objects, or only with objects that extend some kind of generic object superclass.
But I'm trying to see if there's a middle road: ideally my framework classes could accept either integer-string pairs, or objects with getId() and getDescription() methods. Because this is something that must have occurred to someone else before I'd like to use the combined knowledge of stackoverflow to find out if there's a standard / best-practice way of doing this that doesn't clash with the php standard library, common frameworks etc.
I'm afraid there is no such thing. I'm not exactly sure what the reason is you need this, but consider the following options:
Adding a toInt() method, casting in the class. You're probably aware of this already.
public function toInt()
{
return (int) $this->__toString();
}
Double casting outside the class, will result in an int.
$int = (int) (string) $class;
Make a special function outside the class:
function intify($class)
{
return (int) (string) $class;
}
$int = intify($class);
Of course the __toString() method can return a string with a number in it: return '123'. Usage outside the class might auto-cast this string to an integer.
Make your objects implement ArrayAccess and Iterator.
class myObj implements ArrayAccess, Iterator
{
}
$thing = new myObj();
$thing[$id] = $name;
Then, in the code that consumes this data, you can use the same "old style" array code that you had before:
// Here, $thing can be either an array or an instance of myObj
function doSomething($thing) {
foreach ($thing as $id => $name) {
// ....
}
}
You can use retyping:
class Num
{
private $num;
public function __construct($num)
{
$this->num = $num;
}
public function __toString()
{
return (string) $this->num;
}
}
$n1 = new Num(5);
$n2 = new Num(10);
$n3 = (int) (string) $n1 + (int) (string) $n2; // 15
__toString() exists as a magic method.
http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring
__toInt() does not.

php Set a anonymous function in an instance

I am just starting out with PHP, and I am wondering if there is a way to add an anonymous function to a class instance.
For instance, lets say...
class A{
public B;
}
$c = new A();
//This is where I am getting a little confused...
//The following wont work
$c->B = function(){echo('HelloWorld');};
$c->B();
What I am hoping to do is reuse the same spit of code in a great number of different applications, and make it so that I can just 'swap-out' and replace functions in specific instances.
I am using php5.3 (so anonymous functions should work, just not in the way that I am using them).
Thanks so very much for your time!!
-GK
You can use the __call magic function for this job. Not a beauty, but it works..
like this:
class A {
public $B;
public function __call($closure, $args)
{
call_user_func_array($this->$closure, $args);
}
}
$c = new A();
$c->B = function () { echo('HelloWorld'); };
$c->B();
FWIW:
PHP 5.3's treatment of anonymous functions is entertaining. This won't work:
$c->B = function() { echo func_get_arg(0); };
$c->B("This fails :(");
This WILL work:
$c->B = function() { echo func_get_arg(0); };
$hilarious = $c->B;
$hilarious("This works!");
To work around this, you need to use a __call hack like the one provided by Oden.
This behavior may change in the future. The array dereferencing RFC was recently committed to PHP's trunk, and the patch has set off a discussion on function call chaining, the syntax of which may allow what you're trying to do without the __call hack. Unfortunately it's proven difficult in the past to get function call chaining working.
# real ugly, but PoC...
class a {
function __call($f, $x) {
call_user_func_array($this->$f, $x);
}
}
$a = new a;
$a->b = function() { echo "Hello world"; };
$a->b();
Sounds like you are describing a Strategy Pattern or Decorator Pattern - there are other ways to achieve this in way which is more easily communicated with other developers who read your code.
You can do something along these lines (which will also work with callbacks that are not closures):
<?php
class A {
private $fun;
function setFun($fun) {
if (!is_callable($fun))
throw new InvalidArgumentException();
$this->fun = $fun;
}
public function fun() {
call_user_func_array($this->fun, func_get_args());
}
}
$c = new A();
$c->setFun(function($a) { echo('HelloWorld ' . $a);});
$c->fun("here");
which gives HelloWorld here.
That said, you should also consider inheritance or the decorator pattern.
This is not an issue anymore by PHP 7;
// no error
$result = ($this->anonFunc)();
$result = ($this->anonFunc)($arg1, $arg2, ...);
See more about AST.
Rather than hooking a __call magic method into your class, you can instead execute the callable directly using call_user_func.
class A {
public $b;
}
$c = new A();
$c->b = function(){echo('HelloWorld');};
call_user_func($c->b); // HelloWorld
Obviously it would be nice for PHP to provide some syntax to execute this directly.

PHP syntax to call methods on temporary objects

Is there a way to call a method on a temporary declared object without being forced to assign 1st the object to a variable?
See below:
class Test
{
private $i = 7;
public function get() {return $this->i;}
}
$temp = new Test();
echo $temp->get(); //ok
echo new Test()->get(); //invalid syntax
echo {new Test()}->get(); //invalid syntax
echo ${new Test()}->get(); //invalid syntax
I use the following workaround when I want to have this behaviour.
I declare this function (in the global scope) :
function take($that) { return $that; }
Then I use it this way :
echo take(new Test())->get();
What you can do is
class Test
{
private $i = 7;
public function get() {return $this->i;}
public static function getNew() { return new self(); }
}
echo Test::getNew()->get();
Why not just do this:
class Test
{
private static $i = 7;
public static function get() {return self::$i;}
}
$value = Test::get();
Unfortunately, you can't do that. It's just the way PHP is, I'm afraid.
No. This is a limitation in PHP's parser.
i often use this handy little function
function make($klass) {
$_ = func_get_args();
if(count($_) < 2)
return new $klass;
$c = new ReflectionClass($klass);
return $c->newInstanceArgs(array_slice($_, 1));
}
usage
make('SomeCLass')->method();
or
make('SomeClass', arg1, arg2)->foobar();
Impossible and why would you create an object this way at all?
The point of an object is to encapsulate unique state. In the example you gave, $i will always be 7, so there is no point in creating the object, then getting $i from it and then losing the object to the Garbage collector because there is no reference to the object after $i was returned. A static class, like shown elsewhere, makes much more sense for this purpose. Or a closure.
Related topic:
http://www.mail-archive.com/internals#lists.php.net/msg44919.html
http://bugs.php.net/bug.php?id=23022&edit=1
http://www.mail-archive.com/internals#lists.php.net/msg07610.html
This is an old question: I'm just providing an updated answer.
In all supported versions of PHP (since 5.4.0, in 2012) you can do this:
(new Test())->get();
See https://secure.php.net/manual/en/migration54.new-features.php ("Class member access on instantiation").
This has come up very recently on php-internals, and unfortunately some influential people (e. g. sniper) active in development of PHP oppose the feature. Drop an email to php-internals#lists.php.net, let them know you're a grownup programmer.

How do you copy a PHP object into a different object type

New class is a subclass of the original object
It needs to be php4 compatible
You could have your classes instantiated empty and then loaded by any number of methods. One of these methods could accept an instance of the parent class as an argument, and then copy its data from there
class childClass extends parentClass
{
function childClass()
{
//do nothing
}
function loadFromParentObj( $parentObj )
{
$this->a = $parentObj->a;
$this->b = $parentObj->b;
$this->c = $parentObj->c;
}
};
$myParent = new parentClass();
$myChild = new childClass();
$myChild->loadFromParentObj( $myParent );
You can do it with some black magic, although I would seriously question why you have this requirement in the first place. It suggests that there is something severely wrong with your design.
Nonetheless:
function change_class($object, $new_class) {
preg_match('~^O:[0-9]+:"[^"]+":(.+)$~', serialize($object), $matches);
return unserialize(sprintf('O:%s:"%s":%s', strlen($new_class), $new_class, $matches[1]));
}
This is subject to the same limitations as serialize in general, which means that references to other objects or resources are lost.
A php object isn't a whole lot different to an array, and since all PHP 4 object variables are public, you can do some messy stuff like this:
function clone($object, $class)
{
$new = new $class();
foreach ($object as $key => $value)
{
$new->$key = $value;
}
return $new;
}
$mySubclassObject = clone($myObject, 'mySubclass');
Its not pretty, and its certianly not what I'd consider to be good practice, but it is reusable, and it is pretty neat.
The best method would be to create a clone method on the Subclass so that you could do:
$myvar = $subclass->clone($originalObject)
Alternatively it sounds like you could look into the decorator pattern php example
I would imagine you would have to invent some sort of a "copy constructor". Then you would just create a new subclass object whilst passing in the original object.

Categories