Long story short I'm working with a legacy codebase that makes heavy use of PHP's built-in call_user_func_array function. I'm trying to get it to work with PHP 5.3 (upgrading from 5.2), but have run into the issue described here (in the "Passing non-arrays" section):
http://sixohthree.com/1486/migrating-to-php-5-3-call_user_func_array
In a nutshell, the problem is that between PHP versions 5.2 and 5.3 the bevavior of this function was changed so that it basically does nothing and returns NULL if the second parameter is not a proper array/object/associative array. 5.2 did not do this, and as such the codebase I'm working with makes no effort to ensure that it passes a parameter of the correct type. This causes problems.
To fix it I could follow the instructions in the blog post and hunt down every single call_user_func_array call in the codebase and patch them up, but that would be extremely tedious. Alternately, I noticed that PHP has a built-in override_function API call that can be used to override the built-in functions. That's handy, but what I want to do is more like extending the built-in function.
Ideally what I'd like is to be able to replace the implementation of call_user_func_array with something roughly like:
function call_user_func_array($method, $params) {
$params = is_array($params) ? $params : array($params);
return old_call_user_func_array($method, $params);
}
...where old_call_user_func_array is the built-in call_user_func_array function.
Is this possible, and if so, how?
You can use rename_function which is also in the APD extension so you should already have it if you have override_function installed:
rename_function('call_user_func_array', 'old_user_func_array');
function call_user_func_array($method, $params) {
$params = is_array($params) ? $params : array($params);
old_call_user_func_array($method, $params);
}
Hi your question is actually answered in the third or fourth comment posting in the online PHP documentation. There is often very useful information and examples in the comments section (mind you there is also somtimes stuff that is patently incorrect!) I've copied the relevant portion here for your convenience:
... if you use rename_function to rename the original function to a third name, then call the third name in the OVERRIDING function, you will get the desired effect:
rename_function('strlen', 'new_strlen');
override_function('strlen', '$string', 'return override_strlen($string);');
function override_strlen($string){
return new_strlen($string);}
or you can put your code in a namespace:
namespace Phpoverride
{
function call_user_func_array($method, $params) {
return \call_user_func_array($method, $params);
}
}
http://php.net/namespaces
Related
Is there a way using function_exists that I could check that if for example if 'mysql_query' is called and therefore I can run some code via the if statement? to basically error it so that I can go and change it to PDO?
function_exists() is only for checking if a function is defined rather than if a function is being called.
However you can rename and override a PHP function to get the desired effect, using a wrapper as #wogsland mentioned in the comments. This method requires APD installed. For example:
<?php
// First rename existing function
rename_function('mysql_query', 'original_mysql_query');
// Override function with another
override_function('mysql_query', '$query', 'return override_mysql_query($query);');
// Create the other function
function override_mysql_query($query)
{
echo "Calling original_mysql_query($query)";
return original_mysql_query($query);
}
In php 7, mysql_* functions are deleted and you are then able to redefine them pointing them to their new functions, such as mysqli or PDO.
But to answer your question, you could also rename the internal functions in PHP explained in this page:
http://php.net/manual/en/function.runkit-function-rename.php
And create a user function with the same name as the function you're trying to call and let it do something else instead. But I would not recommend it.
As NoChecksum also stated, "rename_function" and "override_function" is also an option.
I have come across this line in the eloquent ORM library:
return with(new static)->newQuery();
I've never seen "with" used before, and cannot find it in the PHP documentation. I'm guessing "with" is a stop-word in most searches, so I am not even getting close.
Never having encountered "with" in many years of programming PHP, I feel like I'm missing out. What does it do? I did come across one passing comment regarding the ORM, that mentioned "with" is no longer needed in PHP-5.4, but that was as much as was said. If that is accurate, it would be good to know what the PHP-5.4 equivalent is.
Update: Details supporting the answer:-
I found this helper function in Laravel's Immuminate/Support/helpers.php helper script:
if ( ! function_exists('with'))
{
/**
* Return the given object. Useful for chaining.
*
* #param mixed $object
* #return mixed
*/
function with($object)
{
return $object;
}
}
as mentioned in a few of the answers. That global-scope function allows an object to be created and methods run in t, in one statement. It is (somehow) registered in the composer autoload_files.php script when Laravel is installed, so it gets loaded on every page, even though it contains no classes.
Thanks all. It pays not to assume that everything must be a namespaced class in modern frameworks.
It's a function that will look something like this:
function with($obj) {
return $obj;
}
It's shorter version and more readable version for new ExampleObj()->newQuery().
with function is not build in PHP. It's workaround for older versions than PHP 5.4. As #Rocket Hazmat pointed in PHP 5.4+ you can do just new ExampleObj()->newQuery() so with function here allow you to keep readable backward compatibility.
The with function is a helper provided by Laravel as documented here. The best documentation is the code and as you can see, it simply returns the object. In 5.4 / 5.4 you are better off just surrounding the expression in parens to avoid the overhead of an unnecessary function call.
I think #Izkata hit the nail on the head in the comments.
[I'm guessing] this is a workaround for something along the lines of new Foo()->newQuery() not working in some version of PHP
In PHP 5.4 the following was added:
Class member access on instantiation has been added, e.g. (new Foo)->bar().
(Source: http://www.php.net/manual/en/migration54.new-features.php)
So, I'd assume with looks something like this:
function with($x){
return $x;
}
In PHP 5.4+, you can do (new static)->newQuery(), but with an older version, you can't. with is probably there so you can do with(new static)->newQuery(), which will work in any PHP version.
I know you can do something like this in PHP5:
function sayHi() {
echo "Hi!";
}
$func = "sayHi";
$func();
It is called a variable function in the documentation. But the docs don't say anything about what versions of PHP this works on. This could mean it works on ALL versions of PHP, but I doubt it. Specifically, does this work on PHP4?
For php 4.3 they are working. Build-in function test here, your source test here.
Definitely Yes. As you can see in here when there is a dependency in a certain function they set it below the title.
No dependcy in version of php
VARIABLE Functions
With dependency in version of php
INTVAL Function
note: so whenever you have concerns on a certain function that you will use regards on your php version better consult php.net for info.
My question is if it's possible to extend a declared function.
I want to extend mysql_function to add mysql query that insert into a table some logs : 'query' - the parameter of mysql_query, date,page...etc
My question is if it's possible to extend a declared function.
No.
You can extend a class method and call parent::methodname() to run the previous code (which is almost what you ask for), but for normal functions, there is no way to do this.
There are some esoteric PHP extensions that allow overriding functions, but I assume that's not what you need and their use is rarely practical.
What you probably want to do is create a new function, and call the existing function in it.
No, you cannot do that. Either enable the MySql Query Logs or wrap the code doing the queries into a Logging Decorator or use an abstraction like Zend_Db that can take a Profiler or use a transparent logging plugin for mysqlnd
You need to write a function that will take your query, log the sql first, runs your query, then return the results.
E.G
<?php
function mysql_query_log($sql)
{
mysql_query('insert into .... values ...');
$r = mysql_query($sql);
$results;
//do the normal thing you do with mysql here
return $results;
}
This is not extending a function though, you can only extend a class
It's not possible.
You should have created your own API (or use an existing one) to access the DB so when you need logging you can simply enhance your own API function. It also comes very handy if you need some custom error handling function. Refactor the code.
Well.. PHP says this: http://php.net/manual/en/function.override-function.php
from http://php.net/manual/en/function.rename-function.php
bool rename_function ( string $original_name , string $new_name )
Renames a orig_name to new_name in the global function table. Useful
for temporarily overriding built-in functions.
I believe that if you rename the original to original_mysql_query, then add your replacement function which does your logging and then calls original_mysql_query etc, that you will achieve your goal, assuming that you have the way to inject the rename on every page that will call MySQL_query. Most large sites have common code that is included at the top of every page that could do that for you.
There is also a built in php function called override_function (mentioned by ChrisH). It is not fully documented in the php man page but the user comments below the doc give you the information that you need to use it if you prefer it to the rename_function function. There was a discussion about being limited to one override if you needed to call the original function from the replacement. Using the rename_function instead of the override function eliminates that potential restriction.
In PHP, get_included_files() returns an array with the names of included files.
In a similar fashion, is there any way to get an array with the names of called functions with parameters?
In this way, Is any way to get an array with the names of called functions with parameters?
No.
What you can do is a debug_backtrace() which will show all the function calls (with parameters) that lead to the execution of the line you are doing the backtrace from (the "call stack"), but that's different from all functions that were ever called in the script.
What do you want to do? Maybe there's a different approach.
I was searching for something similar and found xdebug's tracing very useful.
Here's an example of how it could look like:
http://devzone.zend.com/1135/tracing-php-applications-with-xdebug/
I was trying to achieve what you want and finally came up with an reasonable solution.
Make a class named Debug and include that above every file you want to debug in. Build yourself a function that prints nicely the information stored in $calls.
class Debug {
private static $calls;
public static function log($message = null)
{
if(!is_array(self::$calls))
self::$calls = array();
$call = debug_backtrace(false);
$call = (isset($call[1]))?$call[1]:$call[0];
$call['message'] = $message;
array_push(self::$calls, $call);
}
}
Call this function everytime you declare a function first line in the functionbody: Debug::log($message(optional) )
Not that I'm aware.
You can however use debug_backtrace to get the currently active function/method hierarchy.
I don't think there's a way to do what you want. Sorry.
The closest I can get is the function_exists() function, which will tell you whether a specific function has been loaded.
What exactly do you want to achieve here? I can't see a use case (outside of a php_info() type screen) that would require a list of available functions.
You will have to install it as an extension, but a profiler like XHProf will give you a breakdown of which functions are called and how long they take, as well as a callgraph.
XHProf or Webgrind/KCachegrind will show you the functions called, but not their parameters.
You could also use get_defined_functions, which gives you a list of all functions defined. But it won't show you which functions have actually been called, and with what parameters.
If you really need to know the parameters, I don't know of any tools other than a custom logger like the one Henze provided in his answer.