define anonymous function using values from current scope? - php

I am trying to create an anonymous function but need to access variables from the current scope in it's definition:
class test {
private $types = array('css' => array('folder' => 'css'));
public function __construct(){
//define our asset types
foreach($this->types as $name => $attrs){
$this->{$name} = function($file = ''){
//this line is where is falls over!
//undefined variable $attrs!
return '<link href="'.$attrs['folder'].'/'.$file.'" />';
}
}
}
}
$assets = new test();
Obviously this example is very very minimalistic but it gets across what I am trying to do.
So, my question is,
How can I access the parent scope only for the definition of the function?
(once defined I obviously don't need that context when the function is called).
Edit #1
Ok so after using Matthew's answer I have added use as below; but now my issue is that when I call the function I get no output.
If i add a die('called') in the function then that is produced, but not if I echo or return something.
class test {
private $types = array('css' => array('folder' => 'css'));
public function __construct(){
//define our asset types
foreach($this->types as $name => $attrs){
$this->{$name} = function($file = '') use ($attrs){
//this line is where is falls over!
//undefined variable $attrs!
return '<link href="'.$attrs['folder'].'/'.$file.'" />';
}
}
}
public function __call($method, $args)
{
if (isset($this->$method) === true) {
$func = $this->$method;
//tried with and without "return"
return $func($args);
}
}
}
$assets = new test();
echo 'output: '.$assets->css('lol.css');

function($file = '') use ($attrs)

Related

Get variable from out of Class OOP PHP

I wanna get variable from out of class.
Example,
config.php
$config['function'] = array('filter_validate','form');
controller.php
class Controller{
public function __construct()
{
foreach ($config['function'] as $key => $function_class) {
$function_class = new $function_class();
}
}
}
But, I can't get $config['function'] variable in Controller. How can do that?
Solution #1 (with parameter):
class Controller {
public function __construct($config) {
foreach ($config['function'] as $key => $function_class) {
$function_class = new $function_class();
}
}
}
Solution #2 (with global - NOT recommended):
class Controller {
public function __construct() {
global $config;
foreach ($config['function'] as $key => $function_class) {
$function_class = new $function_class();
}
}
}
You need to pass config to the constructor, like this:
class Controller{
public function __construct($config)
{
foreach ($config['function'] as $key => $function_class) {
$function_class = new $function_class();
}
}
}
$config['function'] = array('filter_validate','form');
$controller = new Controller($config);
functions outside any class are global an can be called from anywhere. The same with variables.. just remember to use the global for the variables.
<?php
function abc() { }
$foo = 'bar';
class SomeClass {
public function tada(){
global $foo;
abc();
echo 'foo and '.$foo;
}
}
?>
There are many ways, the most modern right now is with a fluent, getter / setter. one one of many examples, not tested:
public function config(array|string $arg, array|string $default)
{
// assume lonley arg is a getter
if(is_string($arg)) return $this->variableBag[$arg];
// assume arg is a setter when array
if(is_array($arg)) return $this->variableBag[$arg[0]??$arg['key'] = ?? $arg['1'] ?? $arg['value'];
// else assume if second is set a default val
return isset($this->variableBag[$default]) ?$this->variableBag[$default] : $default
}
```

php declare public variable inside function

I want to declare a variable inside a class with an unknown name
class Example {
function newVar($name, $value) {
$this->$name = $value;
}
}
And I want to use it that way
$c = new Example();
$c->newVar('MyVariableName', "This is my Value");
echo($c->MyVariableName);
The Important thing is, that I do not know the name of the variable. So I cannot put a public $MyVariable inside the class.
Is that in anyway possible? and if yes, can i do this with different scopes (private, protected, public) ?
U should use magic methods __get and __set (example without checking):
class Example {
private $data = [];
function newVar($name, $value) {
$this->data[$name] = $value;
}
public function __get($property) {
return $this->data[$property];
}
public function __set($property, $value) {
$this->data[$property] = $value;
}
}
$c = new Example();
$c->newVar('MyVariableName', "This is my Value");
echo($c->MyVariableName);
// This is my Value
$c->MyVariableName = "New value";
echo($c->MyVariableName);
// New value
See http://php.net/manual/en/language.oop5.magic.php
If i am understanding this correctly you can tweak a little bit by using key value array
class Example {
private $temp;
function __construct(){
$this->temp = array();
}
function newVar($name, $value) {
$this->temp[$name] = $value;
}
function getVar($name){
return $this->temp[$name];
}
}
$c = new Example();
$c->newVar('MyVariableName', "This is my Value");
echo($c->getVar('MyVariableName'));
Instead of using private you can use protected as well.
Your looking for magic calling. In PHP you can use the __call() function to do stuff like that. Have a look here: http://www.garfieldtech.com/blog/magical-php-call
Off the top of my head, something like
function __call($vari, $args){
if(isset($this->$vari){
$return = $this->$vari;
}else{
$return = "Nothing set with that name";
}
}
This will also work for private, protected and public. Can also use it to call methods as required in a class

Anonymous functions in a class

is there a better way to call an anonymous function inside a class? Here is a simple example that clearifies what I mean.
class foo
{
private $call_back_1 = null;
private $call_back_2 = null;
function __construct($func1, $func2)
{
$this->call_back_1 = func1;
$this->call_back_2 = func2;
}
function provokeCallBacks()
{
//this does not work, and gives an error
$this->call_back_1();
//I would like to avoid this
$method = $this->call_back_2;
$method();
}
}
$call1 = function(){ echo "inside call 1"};
$call2 = function(){ echo "inside call 2"};
$test = new foo(call1, call2);
$test->provokeCallBacks();
* Update 1: Please ignore any syntax error as I have written this on the fly for demo puposes. *
Inside foo:provokeCallBacks, I am trying to call the anonymous functions how ever the first way does not works and gives an error. The second one works but it's a bit stupid that I have to use a temp variable called "$method" to make the call.
I want to know if there exists a better way to call the anonymous function.
call_user_func($this->call_back_1);
No, it's not possible to call an anonymous function via $this.
Another options is;
call_user_func($this->call_back_1);
Being PHP loosely typed, it can't do like {$this -> callback}(); you have to store it in a temp variable or to use call_user_func() either.
EDIT - consider something like this:
class Lambdas
{
protected $double;
protected $triple;
public function __construct($double, $triple)
{
$this -> double = $double;
$this -> triple = $triple;
}
public function __call($name, $arguments)
{
if( is_callable($this -> {$name}) ){
return call_user_func_array($this -> {$name}, $arguments);
}
}
}
$lambdas = new Lambdas(
function($a){ return $a * 2;},
function($a){ return $a * 3;}
);
echo $lambdas -> double(2); // prints 4
echo $lambdas -> triple(2); // prints 6
Dirty and dangerous, but you might succeed using eval..
class foo
{
private $call_back_1 = null;
private $call_back_2 = null;
function __construct($func1, $func2)
{
$this->call_back_1 = func1;
$this->call_back_2 = func2;
}
function provokeCallBacks()
{
eval($this->call_back_1);
eval($this->call_back_2);
}
}
call1 = 'echo "inside call 1"';
call2 = 'echo "inside call 2"';
$test = foo(call1, call2);
$test->provokeCallBacks();
I know your question has been answered but you can try changing your approch ..
class Foo {
private $calls = array();
function __set($key, $value) {
$this->calls[$key] = $value;
}
function __call($name, $arg) {
if (array_key_exists($name, $this->calls)) {
$this->calls[$name]();
}
}
function __all() {
foreach ( $this->calls as $call ) {
$call();
}
}
}
$test = new Foo();
$test->A = function () {
echo "inside call 1";
};
$test->B = function () {
echo "inside call 2";
};
$test->A(); // inside call 1
$test->B(); // inside call 2
$test->__all(); // inside call 1 & inside call 2

How to add methods dynamically

I'm trying to add methods dynamically from external files.
Right now I have __call method in my class so when i call the method I want, __call includes it for me; the problem is I want to call loaded function by using my class, and I don't want loaded function outside of the class;
Class myClass
{
function__call($name, $args)
{
require_once($name.".php");
}
}
echoA.php:
function echoA()
{
echo("A");
}
then i want to use it like:
$myClass = new myClass();
$myClass->echoA();
Any advice will be appreciated.
Is this what you need?
$methodOne = function ()
{
echo "I am doing one.".PHP_EOL;
};
$methodTwo = function ()
{
echo "I am doing two.".PHP_EOL;
};
class Composite
{
function addMethod($name, $method)
{
$this->{$name} = $method;
}
public function __call($name, $arguments)
{
return call_user_func($this->{$name}, $arguments);
}
}
$one = new Composite();
$one -> addMethod("method1", $methodOne);
$one -> method1();
$one -> addMethod("method2", $methodTwo);
$one -> method2();
You cannot dynamically add methods to a class at runtime, period.*
PHP simply isn't a very duck-punchable language.
* Without ugly hacks.
You can dynamically add attributes and methods providing it is done through the constructor in the same way you can pass a function as argument of another function.
class Example {
function __construct($f)
{
$this->action=$f;
}
}
function fun() {
echo "hello\n";
}
$ex1 = new class('fun');
You can not call directlry $ex1->action(), it must be assigned to a variable and then you can call this variable like a function.
if i read the manual right,
the __call get called insted of the function, if the function dosn't exist
so you probely need to call it after you created it
Class myClass
{
function __call($name, $args)
{
require_once($name.".php");
$this->$name($args);
}
}
You can create an attribute in your class : methods=[]
and use create_function for create lambda function.
Stock it in the methods attribute, at index of the name of method you want.
use :
function __call($method, $arguments)
{
if(method_exists($this, $method))
$this->$method($arguments);
else
$this->methods[$method]($arguments);
}
to find and call good method.
What you are referring to is called Overloading. Read all about it in the PHP Manual
/**
* #method Talk hello(string $name)
* #method Talk goodbye(string $name)
*/
class Talk {
private $methods = [];
public function __construct(array $methods) {
$this->methods = $methods;
}
public function __call(string $method, array $arguments): Talk {
if ($func = $this->methods[$method] ?? false) {
$func(...$arguments);
return $this;
}
throw new \RuntimeException(sprintf('Missing %s method.'));
}
}
$howdy = new Talk([
'hello' => function(string $name) {
echo sprintf('Hello %s!%s', $name, PHP_EOL);
},
'goodbye' => function(string $name) {
echo sprintf('Goodbye %s!%s', $name, PHP_EOL);
},
]);
$howdy
->hello('Jim')
->goodbye('Joe');
https://3v4l.org/iIhph
You can do both adding methods and properties dynamically.
Properties:
class XXX
{
public function __construct($array1)
{
foreach ($array1 as $item) {
$this->$item = "PropValue for property : " . $item;
}
}
}
$a1 = array("prop1", "prop2", "prop3", "prop4");
$class1 = new XXX($a1);
echo $class1->prop1 . PHP_EOL;
echo $class1->prop2 . PHP_EOL;
echo $class1->prop3 . PHP_EOL;
echo $class1->prop4 . PHP_EOL;
Methods:
//using anounymous function
$method1 = function () {
echo "this can be in an include file and read inline." . PHP_EOL;
};
class class1
{
//build the new method from the constructor, not required to do it here by it is simpler.
public function __construct($functionName, $body)
{
$this->{$functionName} = $body;
}
public function __call($functionName, $arguments)
{
return call_user_func($this->{$functionName}, $arguments);
}
}
//pass the new method name and the refernce to the anounymous function
$myObjectWithNewMethod = new class1("method1", $method1);
$myObjectWithNewMethod->method1();
I've worked up the following code example and a helper method which works with __call which may prove useful. https://github.com/permanenttourist/helpers/tree/master/PHP/php_append_methods

PHP OOP, perform a function once a certain variable has been set

How can i perform a function once a variable's value has been set?
say like
$obj = new object(); // dont perform $obj->my_function() just yet
$obj->my_var = 67 // $obj->my_function() now gets run
I want the object to do this function and now having to be called by the script.
Thanks
EDIT
my_var is predefined in the class, __set is not working for me.
Use a private property so __set() is invoked:
class Myclass {
private $my_var;
private $my_var_set = false;
public function __set($var, $value) {
if ($var == 'my_var' && !$this->my_var_set) {
// call some function
$this->my_var_set = true;
}
$this->$var = $value;
}
public function __get($var, $value) {
return $this->$name;
}
}
See Overloading. __set() is called because $my_var is inaccessible and there is your hook.
I'd recommend to create a setter function for $obj and include the relevant function call there. So basically your code would look somehow like this:
$obj = new ClassOfYours();
$obj->setThatValue("apple");
Of course you would have to take care that all assignments to ThatValue need to be
done through that setter in order make it work properly. Assuming that you're on php5 I'd set that property to private, so all direct assignments will cause an runtime error.
A good overview about OOP in php can be found in this article on devarticles.com.
HTH
To acheive exactly what you describe, you'd have to use a magic setter.
class ObjectWithSetter {
var $data = array();
public function my_function() {
echo "FOO";
}
public function __set($name, $value) {
$this->data[$name] = $value;
if($name == 'my_var') {
$this->my_function();
}
}
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error(
'Undefined property via __get(): ' . $name .
' in ' . $trace[0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}
/** As of PHP 5.1.0 */
public function __isset($name) {
return isset($this->data[$name]);
}
public function __unset($name) {
unset($this->data[$name]);
}
}
Assuming you want to call my_function() once you set a value, that case you can encapsulate both the operations into one. Something like you create a new function set_my_var(value)
function set_my_var(varvalue)
{
$this->my_var = varvalue;
$this->my_function();
}

Categories