PHP Call-time pass-by-reference unavoidable? - php

Given the following interface:
interface ISoapInterface {
public static function registerSoapTypes( &$wsdl );
public static function registerSoapOperations( &$server );
}
And the following code:
$soapProvider = array( "FilePool", "UserList" );
foreach( $soapProvider as $provider ) {
call_user_func( array( $provider, "registerSoapTypes" ), &$server->wsdl );
call_user_func( array( $provider, "registerSoapOperations" ), &$server );
}
FilePool and UserList both implement ISoapInterface.
PHP will complain about the two calls inside the foreach stating:
Call-time pass-by-reference has been deprecated
So I looked that message up, and the documentation seems quite clear on how to resolve this. Removing the ampersand from the actual call.
So I changed my code to look like this:
$soapProvider = array( "FilePool", "UserList" );
foreach( $soapProvider as $provider ) {
call_user_func( array( $provider, "registerSoapTypes" ), $server->wsdl );
call_user_func( array( $provider, "registerSoapOperations" ), $server );
}
Now PHP complains
Parameter 1 to FilePool::registerSoapTypes expected to be reference, value given
Parameter 1 to FilePool::registerSoapOperations expected to be reference, value given
In addition to that, the functionality is now broken. So this obviously can't be the solution.

From the call_user_func:
Note that the parameters for call_user_func() are not passed by reference.
To invoke static methods you can use Class::method() syntax, supplying a variable for the Class and/or method parts:
$soapProvider = array( "FilePool", "UserList" );
foreach( $soapProvider as $provider ) {
$provider::registerSoapTypes($server->wsdl);
$provider::registerSoapOperations($server);
}

While call_user_func does not pass parameters by reference, call_user_func_array can.
$callback = array($provider, 'blahblahblah');
call_user_func_array($callback, array( &$server ));
The only real difference is that it expects an array of parameters instead of a list of parameters like call_user_func (similar to the difference between sprintf and vsprintf)...

Related

how to turn associative array into function parameters in php

Is there a way to turn an associative array into parameters for a function.
I have a simply array such:
$arr = [
'id' => 321654,
'name' => 'action1'
];
I have a static function in a class:
class someclass{
static function someFunction( $id, $name ){
//the rest of the method
}
}
I can call the class by variables, eg:
$class = 'someclass';
$method = 'somFunction';
return $class::$method( );
I can also pass is in a definite qty of function parameters, or an array
return $class::$method( $arr );
In this example's case I could hard code the params to:
return $class::$method( $arr['id'], $arr['name'] );
But how would i pass an unknown qty of keys. Another run may contain 1 key or 4 or 10...
Thanks to #Rizier123 comment:
This worked very nicely:
call_user_func_array( ''.$class.'::'.$method, $arr );

Error trying to add parserHook to mediawiki

I'm running Mediawiki 1.23 and using the Syntaxhighlight plugin. 90% of the time, we use SQL as the specified language. E.g.,:
<syntaxhighlight lang="sql">
select 'foo';
</syntaxhighlight>
So I thought, "Why not just have a separate "sql" tag that invokes highlighter and sets the language to SQL? I.e.,:
<sql>
select 'foo';
</sql>
So I tried the following, but it doesn't work. I'm probably misusing PHP and I could use some help.
In LocalSettings.php:
require_once "$IP/extensions/SyntaxHighlight_GeSHi/SyntaxHighlight_GeSHi.php";
In SyntaxHighlight_SeSHi.php I added the third setHook:
function efSyntaxHighlight_GeSHiSetup( &$parser ) {
$parser->setHook( 'source', array( 'SyntaxHighlight_GeSHi', 'parserHook' ) );
$parser->setHook( 'syntaxhighlight', array( 'SyntaxHighlight_GeSHi', 'parserHook' ) );
$parser->setHook( 'sql', array( 'SyntaxHighlight_GeSHi', 'parserHookSql' ) );
return true;
}
And finally in SyntaxHighlight_SeSHi.class.php I try to keep all the values that were coming in from the parser, but adding (or replacing) the "lang" value, and then call the original parserHook:
class SyntaxHighlight_GeSHi {
private static $initialised = false;
private static $languages = null;
public static function parserHookSql( $text, $args = array(), $parser ) {
$args['lang']='sql';
self::parserHook($text,$args,$parser);
}
public static function parserHook( $text, $args = array(), $parser ) {
global $wgSyntaxHighlightDefaultLang, $wgUseSiteCss, $wgUseTidy;
wfProfileIn( __METHOD__ );
self::initialise();
...
...
When I do this, the page renders but the rendered text from the sql tag is "UNIQ088c1443c530026e-sql-00000007-QINU", so I'm obviously doing something wrong.
So any help with my PHP, or maybe I'm extending mediawiki the wrong way... In either case, thanks in advance!
return self::parserHook($text,$args,$parser);

Passing the right parameters to variable methods in PHP

I'm setting up a PHP API that will expose functionality and vend data to my users, and I'm looking for an elegant way of passing to my functions the arguments that my users are "passing" to me. I can determine what method my users are calling ($_POST['method']) and depending on the method, zero or more arguments to the method. I validate them using a metadata array. For example:
$methods = array(
'say_hello' => array('name'),
'say_goodbye' => array(),
'do_something' => array( 'foo', 'bar' )
);
I have corresponding functions for these:
function say_hello( $name ) { printf( "Hello, %s, $name ); }
function say_goodbye() { printf( "Goodbye!" ); }
function do_something( $foo, $bar ) { printf( "%d + %d = %d.", $foo, $bar, $foo+$bar ); }
When a POST comes in, I check that the method they're requesting is an array key of mine (so they're not passing in $_POST['method'] = 'exec' or anything nefarious), and I can do the actual call as:
$method = $_POST['method'];
$method(); // make the call
And knowing the method also allows me to determine what - if any - arguments should go into it:
$args = $methods[ $method ]; // an array of 0-2 items
But is there a good way for me to combine this without a big if-elseif-elseif-... ?
if( 'say_hello'==$method )
$method( $_POST['name'] );
elseif( ... )
...
Something like Python's myfunc(*args) is what I'm looking to do, that would let me somehow accomplish:
$method = $_POST['method'];
$args = $methods[ $method ];
$method( $args );
Right now, my best bet is to load the arguments in the methods:
function do_something()
{
$foo = $_POST['foo'];
$bar = $_POST['bar'];
...

Converting code with Anonymous functions to PHP 5.2

I have some PHP 5.3 code which builds an array to be passed to a view. This is the code I have.
# Select all this users links.
$data = $this->link_model->select_user_id($this->user->id);
if (count($data) > 0) {
# Process the data into the table format.
$table = array
(
'properties' => array
(
'delete_link_column' => 0,
),
'callbacks' => array
(
# Callback for the name link.
function($value) {
return sprintf('%s', $value, $value);
},
# Callback for the category link.
function($value) {
return sprintf('%s', $value, $value);
},
# Callback for the creation date.
function($value) {
return date('jS M Y', $value);
},
# Callback for the delete link.
function($value) {
return sprintf('delete', $value);
},
),
'columns' => array
(
'name', 'category', 'creation date',
),
'data' => array
(
),
'sorting' => array
(
'sort' => false,
),
);
However the problem is that I cannot use anonymous functions in PHP 5.2, which is the server I must upload this schoolwork. The view requires callback functions to be defined so it can call them.
What would be the neatest way to convert this PHP code to not using anonymous functions? Thanks.
You could call one of those function like so:
$func = $callbacks[0];
$func();
Which also works with create_function() and using strings for named functions like so:
function test() {
echo "test";
}
$func = 'test';
$func();
$func = create_function('' , 'echo "test 2"; ');
$func();
Also, if the calling is done using call_user_func you can use array($object, 'func_name') to call a public method on an object or a class with array('Class_Name', 'func_name'):
class Test {
public static function staticTest() { echo "Static Test"; }
public function methodTest() { echo "Test"; }
public function runTests() {
$test = array('Test', 'staticTest');
call_user_func($test);
$test = array($this, 'methodTest');
call_user_func($test);
}
}
$test = new Test();
$test->runTests();
Anonymous functions are great for ephemeral one-offs, like event listeners in patterns like Observer.
However, since you've already formalized an interface (callbacks for rendering names, categories, creation dates, and a delete link) you may as well go the extra step of defining a 'renderer' interface that requires those 4 methods to be implemented. Instead of passing callbacks you'd pass a single renderer subclass to the view, which could then be used to call the appropriate methods. The view could also validate it by checking the parent class. That would still allow you to swap renderers in the spirit of portable, reusable OOP without requiring anonymous functions.
Is there a situation where your callbacks would ever be coming from arbitrary code (e.g. plugins)? If not, there's really no benefit to making those callbacks anonymous. It might seem like you're saving a little namespace bloat, but you're also making it tougher to debug or document.

How to use call_user_func_array with an object with a __construct method in PHP

How could I use call_func_array to create a new object with a __construct method (with some not optional arguments).
Here's the code:
$urls = array(
'view' => array(
'view/(\d+)',
array('controller' => 'test', 'action' => 'view'),
array(1 => 'id'),
),
);
foreach ($urls as $name => $args) {
$route = call_user_func_array(Zend_Controller_Router_Route_Regex, $args);
$router->addRoute($name, $route);
}
$ref = new ReflectionClass('Zend_Whatever');
foreach ($urls as $name => $args) {
$route = $ref->newInstanceArgs($args);
$router->addRoute($name, $route);
}
The signature for the constructor is
($route, [ $defaults = array()], [ $map = array()], [ $reverse = null])
Thus, I would have an array with the empty arguments, and merge that with the actual arguments for each router in your loop. If you want to make it simple to specify only the last option, then use string keys in your config array, and in your default array.
$blank_opts = array('', array(), array(), null); //default options and blank route
foreach ($urls as $name => $args) {
//replaces default options with options from the array, if set
$opts = array_replace($blank_opts, $args);
//create the new router
$route = new Zend_Controller_Router_Route_Regex($opts[0], $opts[1], $opts[2], $opts[3]);
$router->addRoute($name, $route);
}
For one, you seem to have a syntax error (as Zend_Controller_Router_Route_Regex needs to be a string.
Thus, one could think this would work:
$route = call_user_func_array(array('Zend_Controller_Router_Route_Regex', '__construct'), $args);
However, as far as I know, the first element of the array (the first parameter) can either be a string when calling a static class method or an instance of your class for any method. Neither is the case here. Thus, I would just do it this way:
$route = new Zend_Controller_Router_Route_Regex;
call_user_func_array(array('Zend_Controller_Router_Route_Regex', 'setOptions'), $args);
You might have to use array($args) instead of $args depending on the type of that variable.
EDIT No, I am wrong. There is no setOptions() function for the routes. Let me check something...
EDIT2 A rather ugly hack I found in the PHP manual regarding call_user_func_array() is the following:
$route = new Zend_Controller_Router_Route_Regex;
call_user_func_array(array($route, '__construct'), $args);
However, I did not test this and it might not work (if it can work at all) depending on the implementation of the constructor (Does it accept no parameters being passed to it? Does it just setup the class or are other things done, too?). Please tell me if it worked...

Categories