Accessing private/protected properties of an object in anonymous function in PHP - php

I'm trying to dump elements of an object's private property through an anonymous function - of course I could achieve this in any number of other ways, but this highlights a PHP conundrum I can't solve off the top of my head, short of $foo = $this and using $foo - but THAT won't give me the private stuff, so... suggestions ?
Sample code:
class MyClass
{
private $payload = Array( 'a' => 'A element', 'b' => 'B element');
static $csvOrder = Array('b','a');
public function toCSV(){
$values = array_map(
function($name) use ($this) { return $this->payload[$name]; },
self::$csvOrder
);
return implode(',',$values);
}
}
$mc = new MyClass();
print $mc->toCSV();

I believe there is absolutely no way to do directly what you propose.
However, you can work around it either by making the anonymous method a class method (this is not what you asked for, but it could be a practical solution) or pulling everything you need out of $this explicitly and passing the extracted values into the function:
class MyClass
{
private $payload = Array( 'a' => 'A element', 'b' => 'B element');
static $csvOrder = Array('b','a');
public function toCSV(){
$payload = $this->payload;
$values = array_map(
function($name) use ($payload) { return $payload[$name]; },
self::$csvOrder
);
return implode(',',$values);
}
}

You can hack around the limitation by creating a wrapper that utilizes Reflection to allow you to access all properties and methods. You can use it like this then:
$self = new FullAccessWrapper($this);
function () use ($self) { /* ... */ }
Here a sample implementation of the wrapper, taken from here:
class FullAccessWrapper
{
protected $_self;
protected $_refl;
public function __construct($self)
{
$this->_self = $self;
$this->_refl = new ReflectionObject($self);
}
public function __call($method, $args)
{
$mrefl = $this->_refl->getMethod($method);
$mrefl->setAccessible(true);
return $mrefl->invokeArgs($this->_self, $args);
}
public function __set($name, $value)
{
$prefl = $this->_refl->getProperty($name);
$prefl->setAccessible(true);
$prefl->setValue($this->_self, $value);
}
public function __get($name)
{
$prefl = $this->_refl->getProperty($name);
$prefl->setAccessible(true);
return $prefl->getValue($this->_self);
}
public function __isset($name)
{
$value = $this->__get($name);
return isset($value);
}
}
Obviously the above implementation doesn't cover all aspects (e.g. it can't use magic properties and methods).

As you said yourself, it is private and therefore in accessible.
You can:
Pass $this->payload as a parameter to the anonymous function.
Create a method in the class and use it instead.

Related

PHP Class __Constructor parameters are NULL once function call happened

After all, searching enough in SO I couldn't find out a most generic with best practice solution for PHP __constructor with multiple parameter into a class function
I am trying to define a function inside a PHP class. Where I will be using this function multiple times through a simple function call. Where the function will be having 2 or more parameters.
When I do a function call by passing the parameter, it's just NULL when it reaches the __constructor.
Why it's NULL?
Also, note that there are objects nested inside the function addFruitCheckBox.
What I am doing wrong?
I may also wanted to pass a function call instead of $this->addFruitCheckBoxItemName sometimes.
There are lot of specific problems and solutions in SO. However, I believe this generic question will help me and all, for passing mulitple parameter into __constructor function in a PHP class
ini_set('display_errors', 1);
$_GET['SELECTEDTABNAME'] = 'properties';
/* all include files that are involved in function call within a function will be declared here */
class AddFruitController
{
protected $addFruitCheckBoxItemName;
protected $addFruitCheckBoxLabel;
protected $addFruitMenuItemName;
protected $addFruitChoiceItemName;
protected $addFruitTimeItemName;
public $trustedFruits;
public $trustedFruitsModel;
public $trustedFruitsSpeed;
public $addNewFruit;
public $additionalTub;
public $chooseParent;
public $FruitDown;
public $FruitSell;
public $timeTitle;
public $addFruitbutton;
public function __construct($addFruitCheckBoxItemName, $addFruitCheckBoxLabel, $addFruitMenuItemName, $addFruitTimeItemName)
{
global $interpreterMan, $fetchSeedForSapling;
// var_dump($this->addFruitCheckBoxLabel);
// var_dump($this->addFruitCheckBoxItemName);
$this->trustedFruits = $interpreterMan("Trusted Fruits");
$this->trustedFruitsModel = $interpreterMan("Model");
$this->trustedFruitsSpeed = $interpreterMan("Speed");
$this->addNewFruit = $interpreterMan("New Fruit");
$this->additionalTub = $interpreterMan("Additional Options");
$this->chooseParent = $interpreterMan("Choose Parent");
$this->FruitDown = $interpreterMan("Download Schedule");
$this->FruitSell = $interpreterMan("Install Schedule");
$this->timeTitle = $interpreterMan("Time");
$this->addFruitbutton = $interpreterMan("Add Fruit(s)");
$this->addFruitCheckBoxItemName = $addFruitCheckBoxItemName;
$this->addFruitCheckBoxLabel = $addFruitCheckBoxLabel;
$this->addFruitMenuItemName = $addFruitMenuItemName;
$this->addFruitChoiceItemName = $addFruitChoiceItemName;
var_dump($addFruitChoiceItemName);
$this->addFruitTimeItemName = $addFruitTimeItemName;
}
public function addFruitMenu()
{
global $interpreterMan;
$theWidfetch = new FruitMenu();
$theWidfetch->AssignAddJsCode(false);
$theWidfetch->AssignChoiceOrder(array($interpreterMan("English")));
$theWidfetch->AssignChoiceText(array($interpreterMan("English") => $interpreterMan("English")));
$theWidfetch->AssignGroupHeader($this->addFruitMenuItemName);
$theWidfetch->AssignItemName($this->addFruitMenuItemName);
$theWidfetch->AssignSaveLocation($this->addFruitMenuItemName);
$theWidfetch->AssignValueToUse("ipad");
$theWidfetch->WaterPath(true, true);
}
public function addFruitChoiceTable()
{
global $fetchSeedForSapling, $interpreterMan;
$weekChoiceSelection = new FruitChoiceTable();
$weekChoiceSelection->AssignAddJsCode(false);
$weekChoiceSelection->AssignChoiceOrder(
array("sun", "mon", "tue", "wed", "thu", "fri", "sat"));
$weekChoiceSelection->AssignChoiceText(array(
"sun" => $interpreterMan("SUN"),
"mon" => $interpreterMan("MON"),
"tue" => $interpreterMan("TUE"),
"wed" => $interpreterMan("WED"),
"thu" => $interpreterMan("THU"),
"fri" => $interpreterMan("FRI"),
"sat" => $interpreterMan("SAT"),
));
var_dump($weekChoiceSelection->AssignGroupHeader($this->addFruitChoiceItemName));
$weekChoiceSelection->AssignItemName("Weekday");
$weekChoiceSelection->AssignNumColumns(7);
$weekChoiceSelection->AssignValueToUse($fetchSeedForSapling("dayOfWeek"));
$weekChoiceSelection->WaterPath(true, true);
}
public function addFruitTime()
{
global $fetchSeedForSapling;
$FruitTimeSelect = new FruitTime();
$FruitTimeSelect->AssignGroupHeader($addFruitTimeItemName);
$FruitTimeSelect->AssignItemName($addFruitTimeItemName);
$FruitTimeSelect->AssignValueToUse($fetchSeedForSapling("minuteOfDay"));
$FruitTimeSelect->WaterPath(true, true);
}
public function addFruitCheckBox()
{
global $fetchSeedForSapling;
$addFruitCheckBoxObj = new FruitCheckbox();
$addFruitCheckBoxObj->AssignAddJsCode(false);
$addFruitCheckBoxObj->AssignCheckboxLabel($this->addFruitCheckBoxLabel);
$addFruitCheckBoxObj->AssignItemName($this->addFruitCheckBoxItemName);
$addFruitCheckBoxObj->AssignSaveLocation("somejob");
$addFruitCheckBoxObj->AssignValueToUse($fetchSeedForSapling("somejob"));
$addFruitCheckBoxObj->WaterPath(true, true);
}
}
For creating such complex objects, I suggest you to use Builder Design Pattern instead of assigning properties dynamically and directly.
Note: For better, you can add a layer of interface which Builder classes will implement. And you can have multiple Builder classes which generate different complex objects as per different use cases. Hope this make sense.
Try this code snippet here
<?php
class Builder {
public static function getMyClass($a, $b, $c) {
$myClass = MyClass::getInstance();
$myClass->setA($a);
$myClass->setB($b);
return $myClass;
}
}
class MyClass {
protected $a=0;
protected $b=0;
public static function getInstance() {
$myClass = new MyClass();
return $myClass;
}
function setA($a) {
$this->a = $a;
}
function setB($b) {
$this->b = $b;
}
}
$myClass = Builder::getMyClass("a", "b", "c");
print_r($myClass);
Explanation: In the above mentioned code we have a Builder class which is responsible for building such complex objects.
But still if you are still more towards dynamic assignment approach which nobody recommends, you can see this post
http://php.net/manual/en/language.oop5.decon.php
class MyClass {
protected $a1;
protected $a2;
protected $a3;
public function __construct($a1, $a2, $a3) {
$this->a1 = $a1;
$this->a2 = $a2;
$this->a3 = $a3;
}
}

Why PHP `__invoke` Not Working When Triggered from an Object Property

I wonder whether this is a bug or normal. Let’s say I have a class with some magical functions:
class Foo {
public function __toString() {
return '`__toString` called.';
}
public function __get($key) {
return '`__get(' . $key . ')` called.';
}
public function __invoke($x = "") {
return '`__invoke(' . $x . ')` called.';
}
}
And then create an instance in an object property like this:
$object = (object) [
'foo' => 'bar',
'baz' => new Foo
];
Then test it:
echo $object->baz;
echo $object->baz->qux;
echo $object->baz('%'); // :(
It is broken in the last echo: Call to undefined method stdClass::baz()
Currently, the only solution I can do is to store the __invoke part in a temporary variable and then call that variable as a function like this:
$x = $object->baz;
echo $x('%'); // :)
It works fine when I instantiate the class in an array property:
$array = [
'baz' => new Foo
];
echo $array['baz'];
echo $array['baz']->qux;
echo $array['baz']('%'); // :)
By the way, I need this ability on my object for something related to API:
$foo = (object) ['bar' => new MyClass];
echo $foo->bar; → should trigger __toString
echo $foo->bar->baz; → should trigger __get
echo $foo->bar(); → should trigger __invoke
echo $foo->bar->baz(); → should trigger __call
All of them should return a string.
Can this be done in PHP completely? Thanks.
No can do.
The line in question is simply ambigous, and the error message shows you how ... It is more logical to try to access the baz() method of your $object object.
That's just the context given by the parser when it sees $object->baz()
As already mentioned in the comments, you can remove that ambiguity, help the parser by telling it that $object->baz is itself an expression that needs to be executed first:
($object->baz)('arg');
PHP is also itself a program, and has to know how to execute something before executing it. If it could blindly try every possible "magic" method on every object in a $foo->bar->baz->qux chain, then it wouldn't be able to tell you what the error is when it is encountered - it would just silently crash.
I have solved my problem by detecting the existence of an __invoke method inside the __call method of a class.
class MyStdClass extends stdClass {
protected $data = [];
public function __construct(array $array) {
$this->data = $array;
}
public function __get($key) {
return isset($this->data[$key]) ? $this->data[$key] : null;
}
public function __call($key, $args = []) {
if (isset($this->data[$key])) {
$test = $this->data[$key];
// not an object = not an instance, skip!
if (!is_object($test)) {
return $this->__get($key);
}
if (!empty($args) && get_class($test) && method_exists($test, '__invoke')) {
// or `return $test(...$args)`
return call_user_func([$test, '__invoke'], ...$args);
}
}
return $this->__get($key);
}
public function __set($key, $value = null) {
$this->data[$key] = $value;
}
public function __toString() {
return json_encode($this->data);
}
public function __isset($key) {}
public function __unset($key) {}
}
So, instead of converting the array into object with (object), here I use:
$object = new MyStdClass([
'foo' => 'bar',
'baz' => new Foo
]);

Is there a way to perform this with PHP? Static methods

Looks like impossible do something like that:
class Service
{
public function get($argument1, $argument2)
{
echo "Arguments: $argument1, $argument2.";
}
}
class Main
{
public function __callStatic($method, $arguments)
{
$method = "get".ucfirst($method);
$arguments = implode(", ", $arguments);
$object = self::$method;
return $object->get($arguments);
}
public static function getService()
{
return new Service;
}
}
$main = new Main;
$main::Service("1", "2"); // Should output 'Arguments: 1, 2.'
The problem is the self mess everthing, with this will be possible.
Use self::$variable, self this we have a variable in the Main context.
$object = self don't work too.
$object = clone self dont' work too.
define("method", $method);
self::method will not work because self thinks is a class constant property.
this can't be used.
Is there a way to perform this?
Just pass the original $arguments directly to call_user_func_array:
return call_user_func_array(array($object, "get"), $arguments);
Then, just make a static call to Main:
Main::service("1", "2");
See it here in action: http://codepad.viper-7.com/phhqy6

Dynamic constants in PHP?

Is there a way to create a class's constants dynamically? I know this sounds a bit odd but let me explain what I'm trying to do:
I have a Enum class who's attributes are defined by static const definitions
This class extends the PHP SplEnum class
Rather than type in each of these definitions in code I'd like to have a static initialiser go out to the database and pull the enumerated values
Maybe somethings like this:
class myEnum extends SplEnum {
public static function init () {
$myNameValuePair = DB_Functions::get_enum_list();
foreach ( $myNameValuePair as $name => $value) {
$const = array ( self , $name );
$const = $value;
}
}
}
I recognise that this won't actually work as it doesn't set CONST's but rather static variables. Maybe my whole idea is hair brained and there's a better technique to this. Anyway, any method to achieve the end goal is greatly appreciated.
UPDATE
I think it might be helpful to be a little more clear on my goals because I think it's entirely possibly that my use of Constants is not a good one. Basically I want to achieve is typical of the Enumerated list's requirements:
Constrain function signatures. I want to be able to ask for a "set" of values as an input to a function. For instance:
public function do_something ( ENUM_Types $type ) {}
Simple and Compact. Allow for a simple and compact syntax when used in code. For instance with the use of constants I might write a conditional statement something like:
if ( $my_var === ENUM_Types::TypeA ) {}
Dynamic enumeration. I'd like this enumeration to be managed through the frontend and stored in the database (I'm using wordpress admin screens for this in case anyone cares). At run time this "list" should be pulled out of the DB and made available to the code as an enumeration (or similar structure that achieves the goals above).
Wrap your "enum" values in a singleton and implement the (non-static) magic __get method:
<?php
class DynamicEnums {
private static $singleton;
private $enum_values;
public static function singleton() {
if (!self::$singleton) {
self::$singleton = new DynamicEnums();
}
return self::$singleton;
}
function __construct() {
$this->enum_values = array( //fetch from somewhere
'one' => 'two',
'buckle' => 'my shoe!',
);
}
function __get($name) {
return $this->enum_values[$name]; //or throw Exception?
}
public static function values() {
return self::singleton()->enum_values; //warning... mutable!
}
}
For bonus points, create a (non-OO) function that returns the singleton:
function DynamicEnums() {
return DynamicEnums::singleton();
}
Consumers of "DynamicEnums" would look like:
echo DynamicEnums::singleton()->one;
echo DynamicEnums()->one; //can you feel the magic?
print_r(DynamicEnums::values());
[edit] More enum-like.
Q: Is there a way to create a class's constants dynamically?
The answer is 'Yes', but don't do that :)
class EnumFactory {
public static function create($class, array $constants) {
$declaration = '';
foreach($constants as $name => $value) {
$declaration .= 'const ' . $name . ' = ' . $value . ';';
}
eval("class $class { $declaration }");
}
}
EnumFactory::create('darkSide', array('FOO' => 1, 'BAR' => 2));
echo darkSide::FOO . ' ' . darkSide::BAR;
Next question...
Q: Constrain function signatures. I want to be able to ask for a "set" of values as an input to a function. For instance: public function do_something ( ENUM_Types $type ) {}
According to the manual, in that case $type is must be an instance of the ENUM_Types class. But for constant it is impossible (they can't contain objects).
But wait... We can use such trick:
class Enum {
protected static $_constantToClassMap = array();
protected static function who() { return __CLASS__; }
public static function registerConstants($constants) {
$class = static::who();
foreach ($constants as $name => $value) {
self::$_constantToClassMap[$class . '_' . $name] = new $class();
}
}
public static function __callStatic($name, $arguments) {
return self::$_constantToClassMap[static::who() . '_' . $name];
}
}
class EnumFactory {
public static function create($class, $constants) {
$declaration = '';
foreach($constants as $name => $value) {
$declaration .= 'const ' . $name . ' = ' . $value . ';';
}
eval("class $class extends Enum { $declaration protected static function who() { return __CLASS__; } }");
$class::registerConstants($constants);
}
}
EnumFactory::create('darkSide', array('FOO' => 1, 'BAR' => 2));
EnumFactory::create('aaa', array('FOO' => 1, 'BAR' => 2));
echo (aaa::BAR() instanceof aaa) ? 'Yes' : 'No'; // Yes
echo (aaa::BAR() instanceof darkSide) ? 'Yes' : 'No'; // No
And after that we can use a "type hinting":
function doSomething(darkSide $var) {
echo 'Bu!';
}
doSomething(darkSide::BAR());
doSomething(aaa::BAR());
Q: Simple and Compact. Allow for a simple and compact syntax when used in code. For instance with the use of constants I might write a conditional statement something like: if ( $my_var === ENUM_Types::TypeA ) {}
You can use values of your pseudo-constants in such form:
if (darkSide::FOO === 1) {}
Q: Dynamic enumeration. I'd like this enumeration to be managed through the frontend and stored in the database (I'm using wordpress admin screens for this in case anyone cares). At run time this "list" should be pulled out of the DB and made available to the code as an enumeration (or similar structure that achieves the goals above).
You can init your enumeration by passing array to the EnumFactory::create($class, $constants):
EnumFactory::create('darkSide', array('FOO' => 1, 'BAR' => 2));
You could do something like Const = $$constant. Then you could set $contant = whatever. OR you could use a protected property since you want it to be dynamic and Constants are not. Example:
class Foo {
protected $test = '';
function set($bar){
$this->test = $bar;
}
function get($bar){
return $this->test;
}
}
$foobar = new Foo();
$foobar->set('test');
echo $foobar->get('test');
I do not recommend it, but eval() ... please don't.
I've modified autoloaders to automatically define Exception types that are missing or misspelled. Reason: You can catch an uncaught exception, but you cannot recover from the PHP_FATAL when instantiating a typo in your exception class.

How do I create a dictionary of functions in PHP?

I want to have a dictionary of functions. With this dictionary, I could have a handler that accepts a function name and an array of arguments, and executes that function, returning the value it returns if it returns anything. The handler would throw an error if the name does not correspond to an existing function.
This would be very simple to implement Javascript:
var actions = {
doSomething: function(){ /* ... */ },
doAnotherThing: function() { /* ... */ }
};
function runAction (name, args) {
if(typeof actions[name] !== "function") throw "Unrecognized function.";
return actions[name].apply(null, args);
}
But since functions aren't really first class objects in PHP, I can't figure out how to do this easily. Is there a reasonably simple way to do this in PHP?
$actions = array(
'doSomething' => 'foobar',
'doAnotherThing' => array($obj, 'method'),
'doSomethingElse' => function ($arg) { ... },
...
);
if (!is_callable($actions[$name])) {
throw new Tantrum;
}
echo call_user_func_array($actions[$name], array($param1, $param2));
Your dictionary can consist of any of the allowable callable types.
I don't clearly get what you mean.
If you need an array of functions just do:
$actions = array(
'doSomething'=>function(){},
'doSomething2'=>function(){}
);
You can than run a function with $actions['doSomething']();
Of course you can have args:
$actions = array(
'doSomething'=>function($arg1){}
);
$actions['doSomething']('value1');
You could use PHP's __call() for that:
class Dictionary {
static protected $actions = NULL;
function __call($action, $args)
{
if (!isset(self::$actions))
self::$actions = array(
'foo'=>function(){ /* ... */ },
'bar'=>function(){ /* ... */ }
);
if (array_key_exists($action, self::$actions))
return call_user_func_array(self::$actions[$action], $args);
// throw Exception
}
}
// Allows for:
$dict = new Dictionary();
$dict->foo(1,2,3);
For static invocations, __callStatic() can be used (as of PHP5.3).
If You plan to use this in object context You do not have to create any function/method dictionary.
You can simply raise some error on unexisting method with magic method __call():
class MyObject {
function __call($name, $params) {
throw new Exception('Calling object method '.__CLASS__.'::'.$name.' that is not implemented');
}
function __callStatic($name, $params) { // as of PHP 5.3. <
throw new Exception('Calling object static method '.__CLASS__.'::'.$name.' that is not implemented');
}
}
Then every other class should extend Your MyObject class...
http://php.net/__call
// >= PHP 5.3.0
$arrActions=array(
"doSomething"=>function(){ /* ... */ },
"doAnotherThing"=>function(){ /* ... */ }
);
$arrActions["doSomething"]();
// http://www.php.net/manual/en/functions.anonymous.php
// < PHP 5.3.0
class Actions{
private function __construct(){
}
public static function doSomething(){
}
public static function doAnotherThing(){
}
}
Actions::doSomething();
http://php.net/manual/en/function.call-user-func.php
call_user_func will let you execute your functions from their names as a string and pass them parameters, I don't know the performance implications of doing it this way though.

Categories