I am trying to figure out what does bless do in perl - after reading their documentation - I am not very clear. Correct me if I am wrong, it allow to create properties in the class or object?
Someone coded this block of code
package Main::Call;
sub new
{
my ($class, $call) = #_;
my $self = $call;
bless($self, $class);
return $self;
}
So for example:
if (($statement = $db->prepare($sql)) && $statement->execute())
{
while (my $rt = $statement->fetchrow_hashref())
{
my $obj = Main::Call->new($rt);
push #reserv_call_objs, $obj;
}
return \#reserv_call_objs;
}
I am trying to convert this to PHP.
So I am assuming it would be like this?
class Call {
public function __construct($arr) {
foreach($arr as $key => $value)
{
$this->$value = '';
}
}
public function __set($key, $value) {
$this->$key = $value;
}
}
Perl has an unusual object model: An object is a reference that was “blessed” into a class. The bless just annotates the reference so that methods can be called upon the reference.
my $data = 1;
my $ref = \$data; # the reference can be of any type
$ref->foo; # this would die: Cannot call method "foo" on unblessed reference
bless $ref => 'Foo'; # *magic*
$ref->foo; # now this works
package Foo;
sub foo { print "This works\n" }
But usually references are only blessed inside the class'es constructor.
Perl does not dictate how an object should store its data. The most common method is to use a hash reference. This new is similar to your PHP __construct:
sub new {
my ($class, %init) = #_;
return bless \%init => $class;
}
which could be called like Foo->new(key => $value, ...).
What your Perl new does is rather unusual: It blesses the given argument into the appropriate class. This assumes that the $call is already a reference. If the $call was already blessed into some package, then it is re-blessed into this $class.
The most sane way to translate that to PHP is to stuff the $call into an instance's property, roughly like you did.
The bless function associates a class with a reference. That is, what ever you would pass to the new function would become an object of the Main::Call class, as long as it's a reference type. You can pass in a list reference, and it becomes an object. You can pass in a scalar reference, and it becomes an object.
There is no way to do exactly the same thing in PHP, but your attempt comes close to emulating the case when you pass a hash reference to new.
Related
I have a callable $f and I would like to know if it can receive an instance of a certain class Foo as input.
At the moment I'm doing something like
try {
$f($foo);
} catch (\TypeError $e) {
throw new \InvalidArgumentException('The provided function can not evaluate inputs of this type');
}
Is there a way to check this WITHOUT actually invoking the callable? Maybe with reflection or some other dark magic?
If you want to be able to reflect any kind of callable, you'll need to wrap up the logic in a small function. Depending on whether you've got an array, a function name or an anonymous function, you need to create either a ReflectionFunction or ReflectionMethod. Fortunately, these both extend ReflectionFunctionAbstract, so we can type-hint the return value.
function reflectCallable($arg): ReflectionFunctionAbstract {
if (is_array($arg)) {
$ref = new ReflectionMethod(...$arg);
} elseif (is_callable($arg)) {
$ref = new ReflectionFunction($arg);
}
return $ref;
}
This will return you the appropriate object for your callable value, which you can then use to fetch the parameters and act accordingly:
function definedFunc(Foo $foo) {}
$callable = function(Foo $foo) {};
class Bar { public function baz(Foo $foo) {} }
foreach (['definedFunc', $callable, ['Bar', 'baz']] as $callable) {
$reflected = reflectCallable($callable);
if ((string) $reflected->getParameters()[0]->getType() === 'Foo') {
echo 'Callable takes Foo', PHP_EOL;
}
}
See https://3v4l.org/c5vmM
Note that this doesn't do any error handling - you'll probably get warnings/notices if the callable doesn't take any parameters or the first parameter doesn't have a type. It also requires PHP 7+, but hopefully that's not an issue.
It doesn't currently support objects that implement __invoke or static calls defined as "Foo::bar", but they wouldn't be too hard to add if necessary. I've just found something very similar in the source of Twig, which does a more thorough job: https://github.com/twigphp/Twig/blob/v2.8.0/src/Node/Expression/CallExpression.php#L280
You can with ReflectionParameter::getType:
$f = function(Foo $foo) {};
$reflectionFunc = new ReflectionFunction($f);
$reflectionParams = $reflectionFunc->getParameters();
$reflectionType1 = $reflectionParams[0]->getType();
echo $reflectionType1;
output:
Foo
This thread didn't helped me.
If I use
$class_vars = get_class_vars(get_class($this));
foreach ($class_vars as $name => $value) {
echo "$name : $value\n";
}
I get
attrib1_name : attrib2_name : attrib3_name
There are no values. Also a private attribute is shown, which I don't want.
If I use
echo "<pre>";
print_r(get_object_vars($this));
echo "</pre>";
I get
Array
(
[atrrib1_name] => attrib1_value
[attrib2_name] => attrib2_value
)
Here again I have a private attribute and all sub attributes. But this time I have the values. How can I constrain this to one level?
Isn't there a possibility to show all public attributes with their values of an object?
You are seeing non-public properties because get_class_vars works according to current scope. Since you are using $this your code is inside the class, so the non-public properties are accessible from the current scope. The same goes for get_object_vars which is probably a better choice here.
In any case, a good solution would be to move the code that retrieves the property values out of the class.
If you do not want to create a free function for that (why? seriously, reconsider!), you can use a trick that involves an anonymous function:
$getter = function($obj) { return get_object_vars($obj); };
$class_vars = $getter($this);
See it in action.
Update: Since you are in PHP < 5.3.0, you can use this equivalent code:
$getter = create_function('$obj', 'return get_object_vars($obj);');
$class_vars = $getter($this);
You can do this easily with php Reflection api
Extending Mr.Coder's answer, here is a snippet to fetch the public attributes of the object (name and value) as an array
public function getPublicProperties()
{
$results = [];
$reflectionObject = (new ReflectionObject($this));
$properties = $reflectionObject->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
$results[$property->getName()] = $property->getValue($this);
}
return $results;
}
Use get_object_vars.
see: http://dk.php.net/manual/en/function.get-object-vars.php
I Fully recognize what you are trying to achieve so why not have something external like this to help out... (pasted from PHPFiddle)
<?php
final class utils {
public static function getProperties(& $what) {
return get_object_vars($what);
}
}
class ball {
var $name;
private $x, $y;
function __construct($name,$x,$y) {
}
function publicPropsToArray() {
return utils::getProperties($this);
}
function allPropsToArray() {
return get_object_vars($this);
}
}
$ball1 = new ball('henry',5,6);
//$ball2 = new ball('henry',3,4);
echo "<pre>";
print_r($ball1->publicPropsToArray());
echo "\r\n\r\n";
print_r($ball1->allPropsToArray());
echo "\r\n\r\n";
?>
This way I can both access all properties of the object or for something such as a database access layer or similarly for a function that send "safe" data to a view or another un-privileged model I can send just the public properties, but have the behaviour defined within the object.
Sure this leads to coupling with a utility class, but to be fair not all couplings are bad, some are nesecarry to achieve an end goal, dont get bogged down by these things
First, a quote from the ole' manual on ArrayAccess::offsetSet():
This function is not called in assignments by reference and otherwise indirect changes to array dimensions overloaded with ArrayAccess (indirect in the sense they are made not by changing the dimension directly, but by changing a sub-dimension or sub-property or assigning the array dimension by reference to another variable). Instead, ArrayAccess::offsetGet() is called. The operation will only be successful if that method returns by reference, which is only possible since PHP 5.3.4.
I'm a bit confused by this. It appears that this suggests that (as of 5.3.4) one can define offsetGet() to return by reference in an implementing class, thus handling assignments by reference.
So, now a test snippet:
(Disregard the absence of validation and isset() checking)
class Test implements ArrayAccess
{
protected $data = array();
public function &offsetGet($key)
{
return $this->data[$key];
}
public function offsetSet($key, $value)
{
$this->data[$key] = $value;
}
public function offsetExists($key) { /* ... */ }
public function offsetUnset($key) { /* ... */ }
}
$test = new Test();
$test['foo'] = 'bar';
$test['foo'] = &$bar; // Fatal error: Cannot assign by reference to
// overloaded object in
var_dump($test, $bar);
Ok, so that doesn't work. Then what does this manual note refer to?
Reason
I'd like to permit assignment by reference via the array operator to an object implementing ArrayAccess, as the example snippet shows. I've investigated this before, and I didn't think it was possible, but having come back to this due to uncertainty, I (re-)discovered this mention in the manual. Now I'm just confused.
Update: As I hit Post Your Question, I realized that this is likely just referring to assignment by reference to another variable, such as $bar = &$test['foo'];. If that's the case, then apologies; though knowing how, if it is at all possible, to assign by reference to the overloaded object would be great.
Further elaboration: What it all comes down to, is I would like to have the following method aliases:
isset($obj[$key]); // $obj->has_data($key);
$value = $obj[$key]; // $obj->get_data($key);
$obj[$key] = $value; // $obj->set_data($key, $value);
$obj[$key] = &$variable; // $obj->bind_data($key, $variable);
// also, flipping the operands is a syntactic alternative
$variable = &$obj[$key]; // $obj->bind_data($key, $variable);
unset($obj[$key]); // $obj->remove_data($key);
As far as has, get, set, and remove go, they're no problem with the supported methods of ArrayAccess. The binding functionality is where I'm at a loss, and am beginning to accept that the limitations of ArrayAccess and PHP are simply prohibitive of this.
What the manual is referring to are so called "indirect modifications". Consider the following script:
$array = new ArrayObject;
$array['foo'] = array();
$array['foo']['bar'] = 'foobar';
In the above script $array['foo'] = array(); will trigger a offsetSet('foo', array()). $array['foo']['bar'] = 'foobar'; on the other hand will trigger a offsetGet('foo'). Why so? The last line will be evaluated roughly like this under the hood:
$tmp =& $array['foo'];
$tmp['bar'] = 'foobar';
So $array['foo'] is first fetched by ref and then modified. If your offsetGet returns by ref this will succeed. If not you'll get some indirect modification error.
What you want on the other hand is the exact opposite: Not fetch a value by reference, but assign it. This would theoretically require a signature of offsetSet($key, &$value), but practically this is just not possible.
By the way, references are hard to grasp. You'll get lots of non-obvious behavior and this is especially true for array item references (those have some special rules). I'd recommend you to just avoid them altogether.
This does not work with ArrayAccess, you could add yourself a public function that allows you to set a reference to an offset (sure, this looks different to using array syntax, so it's not really a sufficient answer):
class Test implements ArrayAccess{
protected $_data = array();
public function &offsetGet($key){
return $this->_data[$key];
}
...
public function offsetSetReference($key, &$value)
{
$this->_data[$key] = &$value;
}
}
$test = new Test();
$test['foo'] = $var = 'bar';
$test->offsetSetReference('bar', $var);
$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)
Probably such a function was forgotten when ArrayAccess was first implemented.
Edit: Pass it as "a reference assignment":
class ArrayAccessReferenceAssignment
{
private $reference;
public function __construct(&$reference)
{
$this->reference = &$reference;
}
public function &getReference()
{
$reference = &$this->reference;
return $reference;
}
}
class Test implements ArrayAccess{
...
public function offsetSet($key, $value){
if ($value instanceof ArrayAccessReferenceAssignment)
{
$this->offsetSetReference($key, $value->getReference());
}
else
{
$this->_data[$key] = $value;
}
}
Which then works flawlessly because you implemented it. That's probably more nicely interfacing than the more explicit offsetSetReference variant above:
$test = new Test();
$test['foo'] = $var = 'bar';
$test['bar'] = new ArrayAccessReferenceAssignment($var);
$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)
I am trying to json_encode an array of objects who all have magic properties using __get and __set. json_encode completely ignores these, resulting in an array of empty objects (all the normal properties are private or protected).
So, imagine this class:
class Foo
{
public function __get($sProperty)
{
if ($sProperty == 'foo')
{
return 'bar!';
}
return null;
}
}
$object = new Foo();
echo $object->foo; // echoes "foo"
echo $object->bar; // warning
echo json_encode($object); // "{}"
I've tried implementing IteratorAggregate and Serializable for the class, but json_encode still doesn't see my magic properties. Since I am trying to encode an array of these objects, an AsJSON()-method on the class won't work either.
Update! It seems the question is easy to misunderstand. How can I tell json_encode which "magic properties" exist? IteratorAggregate didn't work.
BTW: The term from the PHP documentation is "dynamic entities". Whether or not the magic properties actually exist is arguing about semantics.
PHP 5.4 has an interface called JsonSerializable. It has one method, jsonSerialize which will return the data to be encoded. This problem would be easily fixed by implementing it.
json_encode() doesn't "asks" the object for any interface. It directly fetches the HashTable pointer that represents the properties of an object by calling obj->get_properties(). It then iterates (again directly, no interface such as Traversable, Iterator etc. is used) over this HashTable and processes the elements that are marked as public. see static void json_encode_array() in ext/json/json.c
That makes it impossible to have a property to show up in the result of json_encode() but not to be accessible as $obj->propname.
edit: I haven't tested it much and forget about "high performance" but you might want to start with
interface EncoderData {
public function getData();
}
function json_encode_ex_as_array(array $v) {
for($i=0; $i<count($v); $i++) {
if ( !isset($v[$i]) ) {
return false;
}
}
return true;
}
define('JSON_ENCODE_EX_SCALAR', 0);
define('JSON_ENCODE_EX_ARRAY', 1);
define('JSON_ENCODE_EX_OBJECT', 2);
define('JSON_ENCODE_EX_EncoderDataObject', 3);
function json_encode_ex($v) {
if ( is_object($v) ) {
$type = is_a($v, 'EncoderData') ? JSON_ENCODE_EX_EncoderDataObject : JSON_ENCODE_EX_OBJECT;
}
else if ( is_array($v) ) {
$type = json_encode_ex_as_array($v) ? JSON_ENCODE_EX_ARRAY : JSON_ENCODE_EX_OBJECT;
}
else {
$type = JSON_ENCODE_EX_SCALAR;
}
switch($type) {
case JSON_ENCODE_EX_ARRAY: // array [...]
foreach($v as $value) {
$rv[] = json_encode_ex($value);
}
$rv = '[' . join(',', $rv) . ']';
break;
case JSON_ENCODE_EX_OBJECT: // object { .... }
$rv = array();
foreach($v as $key=>$value) {
$rv[] = json_encode((string)$key) . ':' . json_encode_ex($value);
}
$rv = '{' . join(',', $rv) .'}';
break;
case JSON_ENCODE_EX_EncoderDataObject:
$rv = json_encode_ex($v->getData());
break;
default:
$rv = json_encode($v);
}
return $rv;
}
class Foo implements EncoderData {
protected $name;
protected $child;
public function __construct($name, $child) {
$this->name = $name;
$this->child = $child;
}
public function getData() {
return array('foo'=>'bar!', 'name'=>$this->name, 'child'=>$this->child);
}
}
$data = array();
for($i=0; $i<10; $i++) {
$root = null;
foreach( range('a','d') as $name ) {
$root = new Foo($name, $root);
}
$data[] = 'iteration '.$i;
$data[] = $root;
$root = new StdClass;
$root->i = $i;
$data[] = $root;
}
$json = json_encode_ex($data);
echo $json, "\n\n\n";
$data = json_decode($json);
var_dump($data);
There is at least one flaw: It doesn't handle recursion, e.g.
$obj = new StdClass;
$obj->x = new StdClass;
$obj->x->y = $obj;
echo json_encode($obj); // warning: recursion detected...
echo json_encode_ex($obj); // this one runs until it hits the memory limit
From your comment:
I am asking about the magic properties, not the methods. – Vegard Larsen 1 min ago
There is no such thing as a magic property.
All you have right now is a magic method that gets called when you try to access a non-visible property.
Let's say it again
$obj->foo does not exist
The way a magic method is implemented is not a concern of PHP, and it can't magically know that you are using your magic method to 'magically' make it look like there is $obj->foo.
If a property does not exist, it will not be put into the object when you json_encode it.
Furthermore, even if json_encode knew that __get was active, it wouldn't know what value to use to call it.
This is an incredibly old thread, but to not confuse other posters who are interested in how magic methods are used to get/set properties in PHP and how that affects JSON encoding, here is my understanding.
Including __get and __set methods in a class give you an interface to deal with dynamically named properties for a given class. Most people define an internal associative array that does the housekeeping.
The reason your foo/bar example doesn't show up in JSON encoding is because you are simply creating an association that returns a value. It's not setting a property in the object itself to that value.
If you did something like:
public function set($name, $value) {
$this->data[$name] = $value;
}
Then if you called :
$foo = new ObjectClass;
$foo->set('foo','bar');
now there is a element in that array $data['foo'] = 'bar'
if you json_encode the object, then that relationship will be represented.
json_encode does not encode methods, only properties. There's no way around it. For your code, the functional equivalent of your magic __get, would be to include in the constructor a call to set, that "hardwires" the value of the property named "foo".
Ultimately, I don't know what specifics you are wrestling with as you didn't provide much in the way of detail. However, json_encoding a object or an array will simply give you a list of properties that are available. If you have to pass through an interpreting function and are relying on that somehow, that's a different problem that I unfortunately have no answer for.
I hope this helps even though it is part of a downrated thread, which ironically, IMO, contains the solution.
I'd create a method on the object to return the internal array.
class Foo
{
private $prop = array();
public function __get($sProperty)
{
return $this->prop[$sProperty];
}
public function __set($sProperty, $value)
{
$this->prop[$sProperty] = $value;
}
public function getJson(){
return json_encode($this->prop);
}
}
$f = new Foo();
$f->foo = 'bar';
$json = $f->getJson();
This is the correct behavior
JSON is only able to contain data not methods - it is meant to be language independent so encoding object methods would not make sense.
How could it know your properties?
$object = new Foo();
echo $object->foo; // how do you know to access foo and not oof?
// how do you expect json_encode to know to access foo?
magic methods are syntactic sugar, and mostly fire back when you use them. this is one such case.
echo json_encode($object); // "{}"
of course it's empty, $object has zero public properties, just a magic method and the ensuing syntactic sugar (turned sour)
i have found the following that worked for me creating classes with dynamic properties and outputing them with json_encode. Maybe it helps other people to.
http://krisjordan.com/dynamic-properties-in-php-with-stdclass
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.