When reading YIi guide at this link: http://www.yiiframework.com/wiki/327/events-explained/, I have see this line:
...So earlier before raising the event we should have called, maybe inside the initialization method of the component, something like this:
$myComponent->onForestRan = array(new SomeOtherClass, 'eventHandler1');
I understand that this code will attach a handler to event, and the array on the left-hand side is a PHP callback function. However, what I really don't understand is its syntax, does it call a onforestRan() function (which is previously defined on $component--> see the Yii link above), if so it will not valid as it lack of $event argument. Or, if it is a callback, then I have never seen a way of using callback like this (if it is a callback where is call_user_func() or usort()..). Its syntax is really odd to me.
Could some one help me with this?
Thank so much!
It's not a callback perse, but you're telling Yii what action (i.e. what function) to perform on the event.
In the example given, when onForestRan happens, the eventHandler1 function from SomeOtherClass will be triggered.
When that page describes a callback, it's saying that whatever you assign to $myComponent->onForestRan needs to be a callback function. The callback isn't executed at that point, you're just letting Yii know which callback(s) to use when the event occurs.
More details:
onForestRan is a special property of $myComponent, as documented here. More details can be seen in CComponent. You can search the CComponent source for lines similar to line 113, where you can see the beginning of the logic for the special property. This is very similar to action* methods in the controller.
$CComponent->onWhatever is a special language construct in Yii. The "property" onWhatever is recognized in CComponent's magic __set-method and then handled as an event attachment.
Let me quote shamelessly from http://phpmaster.com/yii-under-the-hood-2/:
public function __set($name, $value){
if (strncasecmp($name, "on", 2) === 0 && method_exists($this, $name)) {
$name = strtolower($name);
if (!isset($this->_e[$name])) {
$this->_e[$name] = new CList();
}
return $this->_e[$name]->add($value);
}
}
The implementation first checks if the value of $name starts with the text “on” and that a method also exists with the same name as the value. If it does, Yii assumes $value is a representation of a callback which it needs to attach to the event defined by $name. Yii has a private member variable $_e which holds an array of callbacks keyed by event names, and it simply adds the callback to the list for the particular event key.
$_e => array(
'onUserRegistered' => array(
0 => array(object, 'sendMyEmail')
),
'onSomeOtherEvent'=>array(
0 => function(){}
1 => ...
)
)
Related
In this comment in the PHP manual, a user suggested a class which would implement an error handler capable to implement primitive type hinting for PHP.
I understand how that class operates, however, I would like to know if it is possible to bring it to another level: instead of only checking the argument value to see if it matches the "typehint", would it be possible to replace the value with another one if it does match, and how?
I was thinking about something along the line of:
if(gettype($funcValue) == $typeHint) {
// here replace $funcValue, for example $funcValue = new Example($funcValue);
}
and then have the function call proceed with the new value?
I have searched the manual but found nothing which could allow me to "hook" in the parameters' values, but it could be there and I simply didn't find it.
I am impressed by the flexibility of Yii events. I am new to Yii and I want to know how to pass parameters to Yii event handlers?
//i have onLogin event defined in the login model
public function onLogin($event)
{
$this->raiseEvent("onLogin", $event);
}
I have a login handler defined in the handler class. This event handler method takes a parameter:
function loginHandler($param1)
{
...
}
But here, I am confused as to how to pass a parameter to the login event handler:
//i attach login handler like this.
$loginModel->onLogin = array("handlers", "loginHandler");
$e = new CEvent($this);
$loginModel->onLogin($e);
Am I doing something wrong? Is there another approach for this?
Now I have an answer for my own question. CEvent class has a public property called params where we can add additional data while passing it to event handler.
//while creating new instance of an event I could do this
$params = array("name" => "My Name");
$e = new CEvent($this, $params);
$loginModel->onLogin($e);
//adding second parameter will allow me to add data which can be accessed in
// event handler function. loginHandler in this case.
function loginHandler($event)// now I have an event parameter.
{
echo $event->params['name']; //will print : "My Name"
}
If you want to use onBeginRequest and onEndRequest you can do it by adding the next lines into your config file:
return array (
'onBeginRequest'=>array('Y', 'getStats'),
'onEndRequest'=>array('Y', 'writeStats'),
)
or you can do it inline
Yii::app()->onBeginRequest= array('Y', 'getStats');
Yii::app()->onEndRequest= array('Y', 'writeStats');`class Y {
public function getStats ($event) {
// Here you put all needed code to start stats collection
}
public function writeStats ($event) {
// Here you put all needed code to save collected stats
}
}`
So on every request both methods will run automatically. Of course you can think "why not simply overload onBeginRequest method?" but first of all events allow you to not extend class to run some repeated code and also they allow you to execute different methods of different classes declared in different places. So you can add
Yii::app()->onEndRequest= array('YClass', 'someMethod');
at any other part of your application along with previous event handlers and you will get run both Y->writeStats and YClass->someMethod after request processing. This with behaviors allows you create extension components of almost any complexity without changing source code and without extension of base classes of Yii.
well im learning to create a wordpress plugin
i downloaded one and read the codes, and i saw this
i assume 'foo' is the tag where it will add action to..
but what does the array() exactly do?
add_action('foo', array('foo1', 'foo2'));
i looked at http://codex.wordpress.org/Function_Reference/add_action
and there is no clear definition about it ..
Right, the first argument is the tag (to which you'll be adding the action), and the second argument specifies the function to call (i.e. your callback).
The second argument takes in a PHP callback, and as such, accepts a number of valid forms. Check this out for all of them :
PHP Callback Pseudo-Types
The type you've shown above is of type 2. The first element of the array specifies a class, and the second element specifies which function of the class you'd like to call.
So with the example you've given above, what that will do is that, whenever the foo() action is called, it will eventually call foo1->foo2() as well.
The second argument of the add_action function is the function to be called with the hook.
function hello_header() {
echo "I'm in the header!";
}
add_action('wp_head', 'hello_header');
The usage of an array as the second argument is to pass a objects method rather than just a regular function.
Have a read up on the how the call_user_func works. Should provide some more insight.
http://us2.php.net/manual/en/language.pseudo-types.php#language.types.callback(dead link)
I have a object built through a factory containing my parameters read from the url.
From this object, I can get the language parameter
$language =
$my_parameters->getLanguage();
$language is NULL if it isn't been set.
$language may also be invalid ( $language->isValid() returns false ).
So, to build my page, I need some parameters.
The page is also built trough a factory. Then I know which parameters I need to build it. If it miss parameters, I build them with a valid default value according to the page asked.
At this point, into the page factory, if a have an invalid parameter, I throw an exception.
My page object contains a body object that needs a language parameter. I know that my parameters are valid when I build my body object.
Into my body object, I retrieve the language
$language =
$my_parameters->getLanguage();
At this point, $language ** MUST ** be valid.
So I verify again
$language = $my_parameters->getLanguage();
if( is_null( $language ) or !$language->isValid() ) {
throw new Exception( 'Language must be valid.' );
}
If I need 4 parameters, I have 4 ifs that verify if the object is not NULL and not invalid.
I do it because the method is public where $language is used in the body object .
And the body object may be built outside the factory. Who knows...
Is it correct to verify in that case ?
What are the best-practices about that ?
Here is the case for not checking for null in a recent blog post from the Google Testing blog.
The argument is that it gets in the way of writing clear, easy unit tests, because you can't actually fake the pieces that don't matter, because your exceptions/assertions will be thrown.
The author (Miško Hevery) does qualify the commentary by saying that if it's an external API, it might still be worth checking for an error condition.
i know very little about your domain, but in the general case, i like to assert(not null) all over the place, because typically if i end up with a null object somewhere, its a bug.
it's also good practice to prefer reference types which typically can't even be null.
I come from a very old school of C programming; so my thing is that variables that are not being used or that have been free()'d should always be NULL. That's just my opinion though.
Edit: Building on that, you should always check to see if a variable is NULL before using it as well. If the variable is NULL and shouldn't be then you should log an error. Crashing should not be a feature.
You can make life simpler for yourself by splitting the getLanguage() into two methods:
function getLanguageIfValid() {
// this method return a Language object, but only if it can be created
// correctly and the isValid() method returns TRUE. If the Language object
// can't be created correctly, then it will return null instead.
}
function getLanguageMustBeValid() {
// this method will always return an instance of Language, or else
// an exception will be thrown
if($return = $this->getLanguageIfValid())
return $return;
throw new Exception("Couldn't get Language object");
}
Once you've done that, in places where it is reasonable to say that the language item might not be created properly, you use the first method:
// we may or may not be able to get our Language object here
if($language = $my_parameters->getLanguageIfValid())
do_stuff($language);
If you're certain the language object should be created, then use the 2nd method which will throw the exception for you.
// we know the Language object is created at this point
$language = $my_parameters->getLanguageMustBeValid();
do_stuff($language);
So the answer to your question is No - you don't have to verify an object is not null as long as you can get it from a function that us guaranteed not to return null.
Throw your exception from ->getLanguage().
To me, exceptions should be thrown automatically. What you are doing seems like a mixture of error-code checkup and exception throwing.
I'm writing a construct in PHP where a parser determins which function to call dynamically, kind of like this:
// The definition of what to call
$function_call_spec = array( "prototype" => "myFunction",
"parameters" => array( "first_par" => "Hello",
"second_par" => "World"));
// Dispatch
$funcPrototype = $function_call_spec["prototype"];
$funcPrototype(); // Here we call function 'myFunction'.
This is all fine and dandy. But now comes the next step, passing the parameters, which I don't really know if it's possible the way I want to do it. It never stops amazing me however what script languages can do these days, so here goes:
One could pass the parameters to the function like this:
// Here we call function 'myFunction' with the array of parameters.
$funcPrototype( $function_call_spec["parameters"] );
However, I want to declare 'myFunction' properly with clear arguments etc:
function myFunction( $first_par, $second_par )
{
}
The question then follows - Is there any way to pass parameters to a function dynamically simply by looping through the parameter array?
To clarify, I don't want to do it like this:
$funcPrototype( $function_call_spec["parameters"]["first_par"],
$function_call_spec["parameters"]["second_par"] );
Because this requires my code to statically know details about myFunction, which goes against the whole idea.
Instead I would want to do it in some way like this maybe:
// Special magic PHP function which can be used for invoking functions dynamically
InvokeFunction( $funcPrototype, $function_call_spec["parameters"] );
Which then results in myFunction being called and all parameters in the array gets passed to each individual parameter variable in the prototype.
Any comments are welcome.
Regards.
/R
PS: None of the code in this post has been tested for typos etc.
You should use call_user_func_array which can call any function or method and takes parameteres from an array.
Alternatively you can use ReflectionFunction::invokeArgs, but there's no benefit over call_user_func_array unless you already use this class for someting else (like checking whether function you call accepts appropriate number and types of arguments).
call_user_func_array($funcPrototype, $function_call_spec["parameters"]);
You might want to create a wrapper that names the function to your preference, such as:
function InvokeFunction($function, $args = array()) {
return call_user_func_array($function, (array)$args);
}
With this function you can call it in 3 different ways:
$return = InvokeFunction('doStuff');
$return = InvokeFunction('doStuff', $single_arg);
$return = InvokeFunction('doStuff', $multiple_args);
call_user_func_array() is the best choice if you don't need to enforce the contract, otherwise use ReflectionFunction.
http://us2.php.net/create_function
When you use create_function(), your arguments are not evaluated until runtime. Pretty sweet.