I use an homemade MVC system, in which Views access the model by being within the context of a method, and therefore able to access $this.
Example of a view, included dynamically :
...
<div>
Hello <?= $this->user->name ?>
</div>
...
Now, I have some code that I would like to factorize into functions, with some extra parameters.
For example :
function colored_hello($color) {
?>
<div style="background-color:<?= $color ?>">
Hello <?= $this->user->name ?>
</div>
<?
}
The problem is that I do not have access to $this, since the function is not a method.
But I do not want to spoil my model or controller with presentation stuff.
Hance, I would like to be able to call this function dynamically, as a method.
Like aspect oriented programming :
# In the top view
magic_method_caller("colored_hello", $this, "blue")
Is it possible ?
Or do you see a better way of doing it ?
Take a look at Closure::bindTo
You'll have to define/call your functions slightly differently, but you will be able to access $this from inside your object.
class test {
private $property = 'hello!';
}
$obj = new test;
$closure = function() {
print $this->property;
};
$closure = $closure->bindTo($obj, 'test');
$closure();
Pass $this as a property, but in all seriousness: you shouldn't really have functions in your view files.
it is a bit a hack but you can use debug_backtrace() to get the callers object. But I think you can only public values:
function colored_hello($color) {
$tmp=debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
$last=array_pop($tmp);
$caller = $last['object'];
print_r($tmp);
print_r($last);
print_r($caller);
?>
<div style="background-color:<?= $color ?>">
Hello <?= $caller->user->name ?>
</div>
<?
}
(code is not testet but it gives you a hint :-) )
You could alternatively pass it into the function:
function coloured_hello($object, $color) {
//Code
$object->user->name;
}
Related
I have a class that generates data based on a few things. I would like to format that data from the outside. So I am trying to pass a function into the class so that it would format that data. I have looked at many examples, but it seems this is unique.
Can anybody give an idea of how to do this? The following code gives an error.
<?php
class someClass {
var $outsideFunc; // placeholder for function to be defined from outside
var $somevar='Me'; // generated text
function echoarg($abc){
$outsideFunc=$this->outsideFunc; // bring the outside function in
call_user_func($outsideFunc,$abc); // execute outside function on text
echo $abc;
}
}
function outsidefunc($param){ // define custom function
$param='I am '.$param;
}
$someClass=new someClass();
$someClass -> outsideFunc = 'outsideFunc'; // send custom function into Class
$someClass -> echoarg($someClass->somevar);
$someClass -> outsidefunc = 'outsidefunc';
In PHP, function names are not case sensitive, yet object property names are. You need $someClass->outsideFunc, not $someClass->outsidefunc.
Note that good OOP design practice calls for the use of getter and setter methods rather than just accessing properties directly from outside code. Also note that PHP 5.3 introduced support for anonymous functions.
Yeah. You are right. Now there is no error. But it does not work either.
By default, PHP does not pass arguments by reference; outsidefunc() does not actually do anything useful. If you want it to set $param in the caller to something else, and do not want to just return the new value, you could change the function signature to look like this:
function outsidefunc(&$param) {
You would also need to change the way you call the function, as call_user_func() does not allow you to pass arguments by reference. Either of these ways should work:
$outsideFunc($abc);
call_user_func_array($outsideFunc, array(&$abc));
Why not pass your function as an argument?
<?php
class someClass {
public $somevar="Me";
public function echoarg($abc,$cb=null) {
if( $cb) $cb($abc);
echo $abc;
}
}
$someClass = new someClass();
$someClass->echoarg($someClass->somevar,function(&$a) {$a = "I am ".$a;});
i am not sure what exactly you are looking for, but what i get is, you want to pass object in a function which can be acheive by
Type Hinting in PHP.
class MyClass {
public $var = 'Hello World';
}
function myFunction(MyClass $foo) {
echo $foo->var;
}
$myclass = new MyClass;
myFunction($myclass);
OP, perhaps closures are what you're looking for?
It doesn't do EXACTLY what you're looking for (actually add function to class), but can be added to a class variable and executed like any normal anonymous function.
$myClass->addFunc(function($arg) { return 'test: ' . $arg });
$myClass->execFunc(0);
class myClass {
protected $funcs;
public function addFunc(closure $func) {
$this->funcs[] = $func;
}
public function execFunc($index) { $this->funcs[$index](); } // obviously, do some checking here first.
}
ok I do have the code below
<?php
class foo{
public $bar = NULL;
public function boo(){
$this->bar();
}
}
$mee = new foo();
//save a closure function on the property
$mee->bar = function(){
echo 'hahaha';
};
//invoke the closure function by using a class method
$mee->boo();
?>
and you can see it running here http://codepad.org/s1jhi7cv
now what i want here is to store the closure function on the class method.
well closures are possible as i read the documentation about it here http://php.net/manual/en/functions.anonymous.php
is this possible? did i went to something wrong? please correct me
Your example code at codepad.org does not work because codepad.org uses PHP 5.2.5, and closure support was only added in 5.3.
However, your code will also not work in a PHP version that supports closures, although you will get a different error: http://codepad.viper-7.com/Ob0bH5
This is a limitation of PHP at present. $obj->member() looks for a method named member and will not look at properties to see if they are callable. It is, frankly, annoying.
The only way I am aware of to make this work without call_user_func()/call_user_func_array() is:
public function boo() {
$func = $this->bar;
$func();
}
You need to exploit some magic functionality of PHP (__call) to make use of that. Extend from Extendable for example:
class Extendable {
static function import($context) {
$self = new static();
while (is_callable($context)) $context = $context($self);
if (is_array($context) || is_object($context) || is_a($context, 'Traversable')) {
foreach($context as $key => $value)
$self->$key = &$value; # preserve keys if
}
return $self;
}
public function __call($name, $args) {
if (isset($this->$name) && is_callable($this->$name)) {
return call_user_func_array($this->$name, $args);
}
throw new BadFunctionCallException(sprintf('Undefined function %s.', $name));
}
}
And you can do the job. It's not that nice. Background and examples are in one of my blog posts:
PHP: Extending stdClass with Closures (plus Visitor)
You can naturally implement that magic functionality your own, too.
Use call_user_func() function:
<?php
class foo{
public $bar = NULL;
public function boo(){
call_user_func($this->bar);
}
}
$mee = new foo();
//save a closure function on the property
$mee->bar = function(){
echo 'hahaha';
};
//invoke the closure function by using a class method
$mee->boo();
This will display "ahahah"
Hope it helps.
You will not be able to do that.
Take for example this code:
class T {
function foo() {
echo 'T::foo';
}
}
$t = new T;
$t->foo = function() {
echo 'Closure::foo';
};
$t->foo();
It works fine on PHP 5.4.6 and/or PHP 5.3.16, however it will result in T::foo getting printed.
This happens because methods, in PHP, are not modifiable class properties, as they are for example in javascript.
However,
$foo = $t->foo;
$foo();
will print Closure::foo as expected.
PHP is not a prototype based language hence you cannot redefine functions
Use __call to catch all non-defined methods and then look up the closure and invoke it. Take a look at my post on this SitePoint thread.
Starting at php 7, you can put round brackets around the instance and method to call the method like so: ($this->bar)();.
This appears to cause a syntax error on earlier versions however.
Is it possible to add methods to functions?
For example:
<?
function func(){
;
}
//add method
func->test = function(){
;
}
func->test();
func();
I'm coming from a javascript background, and therefore I'm used to 'everything is an object'.
EDIT:
I was just explaining where the misconception may often come from for new phpers. I understand the above code doesn't work.
EDIT 2
Figured it out.
class myfunc_class{
function __invoke(){
//function body
}
function __call($closure, $args)
{
call_user_func_array($this->$closure, $args);
}
}
$func = new myfunc_class;
$func->test = function(){
echo '<br>test<br>';
};
$func->test();
$func();
Even sexier :)
class func{
public $_function;
function __invoke(){
return call_user_func_array($this->_function,func_get_args());
}
function __construct($fun){
$this->_function = $fun;
}
function __call($closure, $args)
{
call_user_func_array($this->$closure, $args);
}
}
$func = new func(function($value){
echo $value;
});
$func->method = function(){
echo '<br>test<br>';
};
$func('someValue');
$func->method();
No.
Not everything is an object in PHP. In fact the only thing that is an object is, well, an object. More specifically, and generally, an instantiation of a class.
Your code converted to PHP
// function_object.php
<?php
class FunctionObject {
public method func() {
// do stuff
}
}
?>
In other code you would use it like this:
<?php
// example.php in same folder as function_object.php
include 'function_object.php';
$FuncObj = new FunctionObject;
$FuncObj->func();
Also: read more about PHP & OOP
No, because an object is a different PHP language construct than a function. Functions do not have properties, but are instead simply execution instructions.
But, if func were instead a pre-defined class, then yes... with a bit of witchcraft, ignoring public outcry, foregoing readability and PHP coding standards, and by using closures with the __call() magic method...
class func
{
function __call($func, $args)
{
return call_user_func_array($this->$func, $args);
}
}
$obj = new func;
$obj->test = function($param1, $param2)
{
return $param1 + $param2;
};
echo $obj->test(1,1);
This won't work as you'd think without __call(), because by $obj->test(1,1), PHP thinks you're trying to call a non-existent method of func when out of object scope. But inside, being that the new "test" property is of a type: closure, the call_user_func_array() just sees the "test" property as just another function, so you can hide this bit of trickery from outside scope.
You would need your function func() to return an object, then you'd be able to do something like: func()->test();
But please note that your way of handling objects is not right in PHP and I suggest that you go read the OO documentations here.
In difference to javacript, in PHP not everything is an object. Therefore you need to differ between function and class.
If you want to create an object, you need to define the class first.
class myClass {
}
You can then add as many functions to the class as you need. But you need to define them first:
class myClass {
function test() {
echo "test!\n";
}
}
When everything is ready, you can bring it to life then:
$class = new myClass;
$class->test();
Checkout the manual for more.
You can't do what you're trying to do, but you can define functions inside of other functions.
This example outputs text:
function a() {
function b() { echo 'Hi'; }
}
a();
b();
Output: HiHi
This example outputs an error:
function a() {
function b() { echo 'Hi'; }
}
b();
Output: ERROR
I have a function that is inside a class method.
In the method I can refer to $this but I cannot in the function. It will return this error:
Fatal error: Using $this when not in
object context in
/var/www/john/app/views/users/view.ctp
on line 78
Here is an example of what I mean:
class View
{
var $property = 5;
function container()
{
echo $this->property;
function inner()
{
echo "<br/>Hello<br/>";
echo $this->property;
}
inner();
}
}
$v = new View();
$v->container();
You can test it here pastie
Is there a work around to make this work?
I know I can pass $this in as a parameter, but is there any other way? Using global $this gives an error also.
If your curious why I need this, its because my method is a view in an MVC model (or so it seems - I am using Cake), and in the view I need to use a function, and in the function I need to refer to $this.
do not make a function within another function, try this:
class View {
var $property = 5;
function container() {
echo $this->property;
$this->inner();
}
function inner() {
echo "<br/>Hello<br/>";
echo $this->property;
}
}
why not pass it with a parameter?
function inner($instance)
{
echo "<br/>Hello<br/>";
echo $instance->property;
}
inner($this);
I cannot yet comment, so I'm posting this answer instead.
This question seems CakePHP related to me, and I think you're overdoing the View. As an example consider reading Post Views from the CakePHP Blog Example
To sum up: if you are inside a CakePHP View, then you just output HTML with embedded PHP. The View has access to variables that you have set in the Controller action (i.e. UserProfiles::index). What I'd suggest is using something like the following:
<h2>UserProfile for <?php echo $user->name; ?></h2>
<?php if( $user->isAdmin() ): ?>
<p>You're an admin</p>
<?php else: ?>
<p>You're just a user</p>
<?php endif; ?>
Also I'd suggest looking at Elements, you can include them conditionally if required conditions are met ;).
When PHP comes across a function definition, it defines the function into the global scope. So although you declare your function within a method, the scope of the function is actually global. Therefore the only way to access $this from within inner() would be to pass it as a parameter. And even then it would behave slightly differently than $this because you wouldn't be within the scope of the object.
In PHP, how do you use an external $var for use within a function in a class? For example, say $some_external_var sets to true and you have something like
class myclass {
bla ....
bla ....
function myfunction() {
if (isset($some_external_var)) do something ...
}
}
$some_external_var =true;
$obj = new myclass();
$obj->myfunction();
Thanks
You'll need to use the global keyword inside your function, to make your external variable visible to that function.
For instance :
$my_var_2 = 'glop';
function test_2()
{
global $my_var_2;
var_dump($my_var_2); // string 'glop' (length=4)
}
test_2();
You could also use the $GLOBALS array, which is always visible, even inside functions.
But it is generally not considered a good practice to use global variables: your classes should not depend on some kind of external stuff that might or might not be there !
A better way would be to pass the variables you need as parameters, either to the methods themselves, or to the constructor of the class...
Global $some_external_var;
function myfunction() {
Global $some_external_var;
if (!empty($some_external_var)) do something ...
}
}
But because Global automatically sets it, check if it isn't empty.
that's bad software design. In order for a class to function, it needs to be provided with data. So, pass that external var into your class, otherwise you're creating unnecessary dependencies.
Why don't you just pass this variable during __construct() and make what the object does during construction conditional on the truth value of that variable?
Use Setters and Getters or maybe a centralized config like:
function config()
{
static $data;
if(!isset($data))
{
$data = new stdClass();
}
return $data;
}
class myClass
{
public function myFunction()
{
echo "config()->myConfigVar: " . config()->myConfigVar;
}
}
and the use it:
config()->myConfigVar = "Hello world";
$myClass = new myClass();
$myClass->myFunction();
http://www.evanbot.com/article/universally-accessible-data-php/24