Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Php function to force parent function to return 2

Status
Not open for further replies.

ThomasJSmart

Programmer
Sep 16, 2002
634
0
0
Is this possible?

Code:
function parent_function(){
  $data = 'middle';
  echo 'start';
  child_function($data);
  echo 'end';
}

function child_function($data){
  parent::return $data;
}

the result i want when parent_function() is called is that only 'start' is echo'd, but that the child function forces the parent function to return before it can reach the 'end' echo.

i need this for plugin hooks in an application, i want to allow a plugin (the child function) the option to return a value without letting the parent function complete. ideally without having some checking method in the parent function that checks for some return var in the child function return.

thanks!
Thomas





site | / blog |
 
No. Functions do not have child parent relationships.

Only classes can have parents and descendants. The parent:: call there would just cause a syntax error because its not expected there.

What exactly are you attempting to do?


----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
yea didn't think so :/ will have to use another way.


In short, I have an application with a bunch of classes and in those classes a bunch of functions.
In many of these functions I have plugin hooks and in most cases the hooks can simply edit the data and return it, or, if no plugin is present, then it just continues unaltered.

but there are some cases where it would be good if the plugin had the option to stop the parent function from continuing and just return the data as is.


for example, standard data edit plugin (greatly simplified):

Code:
class foo{
 private $plugins = null;

 public function __construct(){
   $this->plugins = new plugins();
 }
 
 function create_data(){
   $data = 'abcdef';   
   $data = $this->plugins->execute('hookname',$data);
   $data .= 'XYZ';
   return $data;
 }
}

class plugins{
 // some magic that registers the available plugins with their hooks

 public function execute($hook,$data){
   // magic to check if this hook has a plugin associated with it and what its name is
   // if no plugin then it just returns $data unaltered. 

   $data = $this->my_plugin($data);
   return $data;
 }
 
 public function my_plugin($data){
   $data .= '1234567890';
   return $data;
 }
}


// RUN
$foo = new foo();
echo $foo->create_data();

// result:
// abcdef1234567890XYZ

Now what I want to achieve is to have "create_data" return abcdef123456789, without the XYZ


Code:
class foo{
 public $plugins = null;

 public function __construct(){
   $this->plugins = new plugins();
 }
 
 function create_data(){
   $data = 'abcdef';   
   $data = $this->plugins->execute('hookname',$data); // return after this,
   $data .= 'XYZ'; // do not do this
   return $data;
 }
}

class plugins{
 // some magic that registers the available plugins with their hooks

 public function execute($hook,$data){
   // magic to check if this hook has a plugin associated with it and what its name is
   // if no plugin then it just returns $data unaltered. 

   $data = $this->my_plugin($data);
   return $data;
 }
 
 public function my_plugin($data){
   $data .= '1234567890';
   return $data; // AND FORCE create_data TO RETURN THIS WITHOUT CONTINUING
 }
}


// RUN
$foo = new foo();
echo $foo->create_data();

// wanted result:
// abcdef1234567890

i could of course change how data is returned.
so instead of my_plugin returning $data it could return array('data'=>$data,'return'=>true);

then in create data we check for the 'return' key and it's state and return or not.

but this feels messy, such a solution is not optional for the plugin developer they HAVE to return an array like that, even if return is false, and it's inconsistent with the 75% of the plugin hooks that do not need this option or those would have to be developed to always return an array, with 'return' just being an optional key/flag.





site | / blog |
 
This does break one of the unwritten rules of programming style (only one exit point to a function), but you could do something like this:
Code:
 function create_data(){
   $data = 'abcdef';   
   $data = $this->plugins->execute('hookname',$data); // return after this,
// if $data has been modified, get out;
   if ($data != 'abcdef') then 
     return $data;
   $data .= 'XYZ'; // do not do this
   return $data;
 }
 
but this results in the same dilemma as checking for a return flag. in most plugins the return data is changed, but the parent function should continue as normal. having this hook quit the function when it changes would be different behaviour and inconsistent again. Also, the return data for this plugin may only sometimes want to quit the parent function and not always, depending on the situation and data.

definitely a tricky situation :p I think the answer is the always array return with optional keys or simply avoid needing this feature.

site | / blog |
 
The only way I can think of to do something like what you want is by altering the call stack, and the only language I know of that allows you to do that is assembler. I am pretty sure PHP does not allow you to modify the call stack like that (most high level languages don't) for security reasons. You are probably correct in rethinking the solution such that this "feature" won't be needed.
 
two things.

1 check out the WordPress hooks and actions layer. avoids reinventing the wheel.
2. rather than forcing development to return an n-dinensional array to the plugin instead do this (return a dummy object of a known class)

Code:
class quitObject{}


function myplugin(){

  return new quitObject;

}


function pluginhandler($data){
  foreach($activeplugins as $plugin):
      $_data = $plugin($data);

      if($_data instanceof quitObject) return $data;

      $data =$_data;
endforeach;}

apologies for poor formatting: the editor does not play well with my phone...
 
the full version of my plugin class is actually quite similar to the wordpress one ;)
i didnt use their actions part though, didnt see much use for it in this application.

returning a quitobject is indeed quite pretty, but still the same issue of the extra check, cant be helped though and this is certainly quite elegant and allows some restricting of what can be returned by the plugins.

thanks all :)

site | / blog |
 
yes - i thought about it for a couple of days before replying, to see whether there was a way of modifying the stack from within the stack. There is (as these kind of plugins are called from a call_user_func process by iterating through an array - the current plugin can remove future actions).

This works fine if the function is an _action_ call, as typically it is the last call in the calling function. Not so good if it is a filter though. although it would still be possible by interposing a known value in the call stack.

but whatever solution I came up with, there would still need to be an if/else check in the calling function (or some other conditional such as while/do-while loop). An alternative way of doing it might be this

Code:
while( FALSE !== ($func = next($_GLOBALS['actions']['myPluginHandler']))):
 if(!isset($_data)) $data = $_data;
 $_data = call_user_func( $func, $data );
endwhile;
//then the last $data is what you want

and to interrupt execution flow of the plugins, you would interpose a FALSE in the call-stack. or even delete the rest of the call stack, but directly manipulating the superglobal.

but basically, i think you ought to re-address the architecture a bit - a plugin should not be able to stop a core function from firing only part of itself. that could lead to 'bad things'. instead why not insert the part 'after' the plugin call as a 'core' plugin with a high execution number (so that it executes last in the stack) and then allow plugins to manipulate the stack? that seems (to me) a more logical architectural design for a plugin.
 
i looked at something similar to delete future plugins in the queue which worked ok but had trouble applying it to the parent.

placing it after the parent function would have been a bit inefficient, the code further down the parent function is quite intensive which is why i want to skip it. Think of it as a function that does some simple calculation, passes this to the plugin hook and then the plugin can decide it doesnt need it after all and just end there. if it does need it then it lets the data back into the parent function and the "heavy lifting" happens.
Simply returning false or empty string from the plugin to skip it wouldn't work because that might be a valid value but your previous idea for the quit object certainly would work. i think this, allong with some other objects, would be quite a useful addition to this little plugin engine.

alternatively i could do a plugin before the parent function, split out the "easy calculation" bit from the parent and do some changes to the architecture. but the above seems more interesting to work on, lol :)





site | / blog |
 
Simply returning false or empty string from the plugin to skip it wouldn't work because that might be a valid value but your previous idea for the quit object certainly would work. i think this, allong with some other objects, would be quite a useful addition to this little plugin engine.

ah. no. take another look. the plugin does not return false in my code, but the name of the next plugin becomes boolean FALSE so the plugin execution stops there. If the name is anything other than false, then call_user_func gets called with the name of the function.

i think I would have another look at my idea of putting the later code (the code you want conditionally to avoid) in a plugin or action. it's a neater architecture.

and if you are relying on the plugin functions returning the data _and_ a quitobject then consider passing the arguments by reference and then returning a true/false for whether the plugin stack should continue execution. That's another neat architectural solution

Code:
do{
  $result = call_user_func(next($_GLOBALS['plugins']['myactionName']), $data);
  //you'd probably abstract this to a do_action call; which adds added complexity ...
while ($result !== false);

function myPluginName( &$data){
 //do something
 $data = dosomethingto($data);
 return true;
 return false; //to stop parent execution
}

registerPlugin('myActionName', 'myPluginName', 100);
 
great.

there is (another) solution. but dog-ugly.

instead of requiring plugins as separate functions they could be phrased as code within the current scope.

Code:
function myplugin(){
 foreach($plugins as $plugin):
   include $plugin;
 endforeach;
}

Code:
<?php
//in the current scope
//do stuff with $data and
//return if you need to.  return will stop execution of the [i]'calling'[/i] script
?>

i seem to remember that there are some limitations about including code within classes and/or class methods. i don't fully recall these but probably you can't include methods/properties through include() as the parser would need to know everything about an object before it was instantiated.
 
ps .. i really don't recommend my last post above. but it does 'address' the initial question ;-)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top