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.
Related
I need to check if two PHP object are equal in terms of equal values. Of course I could easily add an isEqualTo(...) method to the class that compares all relevant values. However the concrete class will change in the near future and I would like to know if there is any automated way to do this.
Example:
class Contact {
private name; // String
private phone; // Int
private someObject; // Custom Object
public function getName() {
return $this->name;
}
public function setName($newName) {
$this->name = $newName;
}
public function getPhone() {
return $this->phone;
}
public function setPhone($newPhone) {
$this->phone = $newPhone;
}
public function getSomeObject() {
return $this->someObject;
}
public function setSomeObject($newObj) {
$this->someObject = $newObj;
}
// Manual Solution
public function isEqualTo($contact) {
result = $this->name == $contact->getName();
result &= $this->phone == $contact->getPhone();
result &= $this->someObject == $contact->getSomeObject();
return result;
}
}
This would obviously work. Of course I am aware of the the limitation of comparing someObject (need to be the exact some object to be true) but this is OK.
However the class Contact will be extended in the near future. Everytime I add new properties/values to the class I have to add them to isEqualTo as well. No big deal but a little bit cumbersome.
So, is there any way to implement isEqualTo to automatically all available public properties?
I found get_class_vars and get_object_vars but these methodes will not work with getters and setters but only with vars that can be accessed directly.
I found get_class_methodes but this return all methodes and not only getters and setters. Filtering the methodes names by get... and set... would work of course, but this would be more like a hack than a "nice and clean" soltution.
In short: Is there any "correct" way to automatically check two PHP object for equality?
I wouldn't go so far as to say that this is the "correct" way, but since you don't want to implement/maintain your own comparison function/method you might be interested in php's default behaviour including the comparison of protected/private properties:
<?php
class Foo {
private $x,$y,$z;
public function __construct($x,$y,$z) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
$f1 = new Foo(1,2,3);
$f2 = new Foo(4,5,6);
$f3 = new Foo(1,2,3);
var_dump(
$f1==$f2,
$f1==$f3
);
prints
bool(false)
bool(true)
which might or might not be sufficient for you.
As pointed out by Alma Do, circular references like e.g.
<?php
class Foo {
private $x,$y,$z;
public function __construct($x,$y,$z) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
public function setZ($z) {
$this->z = $z;
}
}
$f1 = new Foo(1,2,3);
$f2 = new Foo(4,5,6);
$f3 = new Foo(1,2,3);
$f1->setZ($f3);
$f3->setZ($f1);
var_dump(
$f1==$f2,
$f1==$f3
);
will cause a Fatal error: Nesting level too deep - recursive dependency?.
You could use Reflection to do this, but i don't think it's a good idea. Maybe serialize() is a better solution?
function isEquals(self $obj){
return serialize($obj)===serialize($this)
}
I'm looking for a way to maintain the $this->property = "value" syntax, but use getter and setter methods.
I found several references to the magic functions __get() and __set(), but I'm looking to provide accessors on more of a case by case basis.
I then found this: https://wiki.php.net/rfc/propertygetsetsyntax-v1.2, which seems like exactly what I was looking for, but alas doesn't seem to have been implemented.
Is there a way to do this without class-wide functions to check every property assignment?
For me, you have two options:
You want to use magic methods to cover all getters and setters, and you'll store your data that would be got/set in a property like $this->_data[] to avoid conflicts with other properties
You want to create specific getters/setters and you manually define them in the classes you want them to be used in (or high up the inheritance chain so they're available to every class that extends it - useful in MVC architecture).
The magic methods approach is a good "cover all bases" approach whereas the individual approach is better for clarity and knowing exactly what is available to implementations/children.
A good example of option 1 (magic methods) is available in the manual, here.
I would add that if you want a "case by case" basis for properties, you could also add a whitelist/blacklist into your magic methods to include/exclude a specific set of properties, example extending what is in the manual:
private $data = array();
private $whitelist = array('PropertyIWant', 'AnotherOneIWant');
// ...
public function __get($name)
{
// ...
if (array_key_exists($name, $this->data) && in_array($name, $this->whitelist)) {
return $this->data[$name];
}
// ...
You could use call:
class Test {
protected $a;
protected $b;
protected $valueForC;
protected $otherData = array('d' => null);
public function __call($method, $args) {
if (preg_match('#^((?:get|set))(.*)$#', $method, $match)) {
$property = lcfirst($match[2]);
$action = $match[1];
if ($action === 'set') {
$this->$property = $args[0];
} else {
return $this->$property;
}
}
}
public function getD() {
return $this->otherData['d'];
}
public function setD($value)
{
$this->otherData['d'] = $value;
}
}
$x = new Test();
$x->setA('a value');
$x->setB('b value');
$x->setValueForC('c value');
$x->setD('special value for D');
echo $x->getA() ."\n";
echo $x->getB() ."\n";
echo $x->getValueForC() ."\n";
echo $x->getD() ."\n";
I am using PHP 5.2.x and want to encode objects of my custom PHP classes with only private members. One of the private members is an array of objects of another custom class.
I tried the solution outlined in https://stackoverflow.com/a/7005915/17716, but that obviously does not work recursively. The only solution that I can see is extending the json_encode method somehow ,so that the class' version of the json_encode method is called instead of the default method.
For reference, the code is as follows:
Class A {
private $x;
private $y;
private $z;
private $xy;
private $yz;
private $zx;
public function f1() {
...
}
public function f2() {
...
}
.
.
.
public function getJSONEncode() {
return json_encode(get_object_vars($this));
}
}
class B {
private $p; //This finally stores objects of class A
private $q;
private $r;
public function __construct() {
$this->p = array();
}
public function fn1() {
...
}
public function fn2() {
...
}
.
.
.
public function getJSONEncode() {
return json_encode(get_object_vars($this));
}
}
class C {
private $arr;
public function __construct() {
$this->arr = array();
}
public function fillData($data) {
$obj = new B();
//create objects of class B and fill in values for all variables
array_push($this->arr, $obj)
}
public function returnData() {
echo $this->arr[0]->getJSONEncode(); //Edited to add
}
}
What would be the best way to achieve this nested json encoding?
Edited to Add:
The output I get when the returnData method is executed is:
{"p":[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}],"q":"Lorem Ipsum","r":"Dolor Sit Amet"}
Whilst I believe you would be better off writing a proper export/encode function for each of your classes (which would construct a public object from both private and public values just for encoding - you could be quite clever and use php's reflection ability) - instead - you could make use of the following code:
/// example class B
class TestB {
private $value = 123;
}
/// example class A
class TestA {
private $prop;
public function __construct(){
$this->prop = new TestB();
}
}
function json_encode_private($obj){
/// export the variable to find the privates
$exp = var_export($obj, true);
/// get rid of the __set_state that only works 5.1+
$exp = preg_replace('/[a-z0-9_]+\:\:__set_state\(/i','((object)', $exp);
/// rebuild the object
eval('$enc = json_encode('.$exp.');');
/// return the encoded value
return $enc;
}
echo json_encode_private(new TestA());
/// {"prop":{"value":123}}
So the above should work, but I wouldn't recommend using eval anywhere in php - just because I always hear alarm bells quietly off in the distance :)
update
Just had a thought of what might make this a little safer, rather than using eval you could use create_function which would limit some of its creational powers, or at least the scope of those powers...
function json_encode_private($obj){
$exp = var_export($obj, true);
$exp = preg_replace('/[a-z0-9_]+\:\:__set_state\(/i','((object)', $exp);
$enc = create_function('','return json_encode('.$exp.');');
return $enc();
}
update 2
Had a chance to play around with another way of converting an object with private properties, to an object with public properties - using only a simple function (and no eval). The following would need to be tested on whichever version of PHP you are using as it's behaviour - again - might not be reliable.... due to the weird \0Class Name\0 prefixing in the converted private properties (see comments in code).
For more info on this strange prefixing behaviour:
http://uk3.php.net/language.types.array.php#language.types.array.casting
Anyway, so using a test class:
class RandomClass {
private $var = 123;
private $obj;
public function __construct(){
$this->obj = (object) null;
$this->obj->time = time();
}
}
We can use the following function to convert it to a public object:
function private_to_public( $a ){
/// grab our class, convert our object to array, build our return obj
$c = get_class( $a ); $b = (array) $a; $d = (object) null;
/// step each property in the array and move over to the object
/// usually this would be as simple as casting to an object, however
/// my version of php (5.3) seems to do strange things to private
/// properties when casting to an array... hence the code below:
foreach( $b as $k => $v ){
/// for some reason private methods are prefixed with a \0 character
/// and then the classname, followed by \0 before the actual key value.
/// This must be some kind of internal protection causing private
/// properties to be ignored. \0 is used by some languges to terminate
/// strings (not php though, as far as i'm aware).
if ( ord($k{0}) === 0 ) {
/// trim off the prefixed weirdnesss..?!
$e = substr($k, 1 + strlen($c) + 1);
/// unset the $k var first because it will remember the \0 even
/// if other values are assigned to it later on....?!
unset($k); $k = $e;
}
/// so if we have a key, either public or private - set our value on
/// the destination object.
if ( $k !== '' && $k !== NULL && $k !== FALSE ) {
$d->{$k} = $v;
}
}
return $d;
}
So if we put it all together:
$a = new RandomClass();
echo json_encode( private_to_public( $a ) );
/// {"var":123,"obj":{"time":1349777323}}
Again your best / most reliable bet is to either bespokely code your conversion methods for each class, or create some kind of generalised solution using Class Reflection, but that latter is far more involved, more involved than a StackOverflow answer... at least with the amount of time I have free ;)
further info
The above code will work when trying to access objects from anywhere, the reason for implementing this was because my first attempt was obviously to use the following:
echo json_encode( get_object_vars($a) );
/// you will get {} which isn't what you expect
It seems that if you want to use get_object_vars you have to use it from a context that has access to all the properties, i.e. from inside the class you are exposing:
public function getAllProperties(){
return get_object_vars( $this );
}
So, imagine we'd added the above to the RandomClass definition:
echo json_encode( $a->getAllProperties() );
/// you will get {"var":123,"obj":{"time":1349777323}}
This works because the members of a class have access to all the class's properties public or private.... so as I say, working this way is far far superior; superior, but not always possible.
I can't quite understand why the output of this code is '1'.
My guess is that php is not behaving like most other OO languages that I'm used to, in that the arrays that php uses must not be objects. Changing the array that is returned by the class does not change the array within the class. How would I get the class to return an array which I can edit (and has the same address as the one within the class)?
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function getArr()
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = $t->getArr();
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
EDIT: I expected the output to be 0
You have to return the array by reference. That way, php returns a reference to the array, in stead of a copy.
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function & getArr() //Returning by reference here
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = &$t->getArr(); //Reference binding here
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
This returns 0.
From the returning references subpage:
Unlike parameter passing, here you have to use & in both places - to
indicate that you want to return by reference, not a copy, and to
indicate that reference binding, rather than usual assignment, should
be done
Note that although this gets the job done, question is if it is a good practice. By changing class members outside of the class itself, it can become very difficult to track the application.
Because array are passed by "copy on write" by default, getArr() should return by reference:
public function &getArr()
{
return $this->arr;
}
[snip]
$tobj_arr = &$t->getArr();
For arrays that are object, use ArrayObject. Extending ArrayObject is probably better in your case.
When you unset($tobj_arr[0]); you are passing the return value of the function call, and not the actual property of the object.
When you call the function again, you get a fresh copy of the object's property which has yet to be modified since you added 5 to it.
Since the property itself is public, try changing:
unset($tobj_arr[0]);
To: unset($t->arr[0]);
And see if that gives you the result you are looking for.
You are getting "1" because you are asking PHP how many elements are in the array by using count. Remove count and use print_r($tobj_arr_fresh)
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')
);