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.
Related
Since PHP7 it seems to be possible to directly call anonymous functions like this:
(function () { echo 'Hello!'; })();
I found this by accident in some open source code base. I tried to find any hint on this in the PHP documentation (including changelogs and RFCs). There seems to be none, it seems fully undocumented.
Is it safe to call functions this way or should I use call_user_func(); like in the old days? Is this documented somewhere?
I know of these questions and answers:
IIFE (Immediately Invoked Function Expression) in PHP?
https://stackoverflow.com/a/35044603/1708223
They just say it's supposed to work and how it works. I know this. My question is not about the "how"! It's about whether this is actually an official PHP feature or if it'S merely working "by accident" (because it seems undocumented).
The reason you won't find this documented as a feature in the main manual is because it isn't one, it's just a consequence of other features:
function () { echo 'Hello!'; } creates a Closure object
putting () after a Closure object executes that closure
That's not quite the same as accidental, but I can see why you might think that, because it didn't work in PHP 5.
The reason it changed is that PHP's parser was overhauled in PHP 7.0 to use more consistent rules when parsing variables, as well as introducing an extra stage called an Abstract Syntax Tree. This changed the meaning of some code, but allowed for other cases that logically should have been possible, but were previously hard to implement.
Allowing code that was previously a syntax error is not generally considered a compatibility break, so not every piece of syntax enabled by this change made it into migration guides.
However, removing this functionality - even if not documented - would be a compatibility break, so at the very least you will get some notice that it's going to change, and it would be in a "major version" (9.0, 10.0, etc). Since it's a useful piece of syntax, there's no reason to suppose it will ever stop working, unless there's some even more useful feature that conflicts with it for some reason.
Just to put some thoughts together: (function () { echo 'Hello!'; }) defines a class instance of type Closure. Putting the closure into a variable helps to understand that:
$x = (function () { echo 'Hello!'; });
echo gettype($x);
This prints object, since PHP 5.3. The result of $x(); hasn't changed since PHP 5.3 either. But what has changed is the unified handling of variables - this enables that you don't have to define a variable explicitly, but call the closure directly as if you had put it in a variable first
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.
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
I am new to PHP, and working on a class based web site. So my question is simple. Can I compile the site like I can in .NET?
This would be helpful to me because I changed my IsAAction interface to accept an error callback function as a parameter of the Execute function, and I would like all of my implementations to break so I can easily see where to change them.
Thanks in advance.
PHP can be compiled, as in you can compile it to machine code and prevent a parsing everytime you access a file. That the way accelerator like APC work. But you can't compile it like a .NET DLL with every error checked in advance. It would go against its dynamic nature. Late binding mean it will always check for error at runtime
For your particuliar problem, I would suggest adding Unit Tests to your project. They are very good to have anyway, and they will help you catch error in your interface implementation. I personally couldn't live without them. You may want to check PHPUnit or SimpleTests.
EDIT To understand why PHP can't check your error, just check this snippet of code.
if ($_GET['impl'] == 'interface1') {
interface MyInterface {
function foo($var);
}
} else {
interface MyInterface {
function bar($var, $var2);
}
}
class Implementation implements MyInterface { //Will crash or not depending on the URL!
function foo($var) {}
}
Yes and no. See:
Can you "compile" PHP code?
And:
http://www.phpcompiler.org/
And:
http://www.scriptol.com/apollo.php
https://github.com/facebook/hiphop-php/wiki - although I'm sure there are other answers on here that you'd find useful.
There's a opened topic on stackoverflow that is related.
Can you “compile” PHP code?
Some parties such as Facebook rewrote PHP runtime, or part of it so they make it possible to run compiled php code or other source codes, pretty much whatever you like.
HipHop for PHP
Its not an easy task. But you can do it.
Not to my knowledge. PHP is a script language.
This code don't work. Why not?
<?php
function test()
{
echo 'test';
}
runkit_function_rename('test', 'test2');
test2();
?>
What I really want is this. I'm using a system that have a function. When I'm on localhost I want that function to do something different. I want to override the function with own stuff.
All alternatives are welcome as well.
Do you have the PECL extension installed?
http://www.php.net/manual/en/runkit.installation.php
This » PECL extension is not bundled with PHP.
I never had any luck with Runkit either.
You asked for alternatives, and I can definitely recommend this one:
Patchwork
Patchwork is a PHP function-override library. In other words, it does much the same job as Runkit.
The main difference is that it is written in pure PHP - no extensions to install; just a require_once() at the top of your code.
The flip side of this is that because it's pure PHP, it can only replace functions defined within your program; ie it can't override a PHP built-in function like Runkit can. The example in your question will work fine with Patchwork, but trying to override a PHP function like mysql_query() is not possible.
However, unlike Runkit, it works perfectly, so if you can live with that limitation, I'd strongly recommend it.
Another alternative to Runkit that you might want to try is PHP Test Helpers. This is a PHP extension, and covers pretty much the same ground as Runkit. It's written by the same author as PHPUnit, so it should be pretty good. However I didn't have much joy when I tried to install this either, so I can't really comment on it much.
I note from your comments elsewhere on this question that you're running Windows (ie WAMP). Neither Runkit nor PHP Test Helpers are provided with Windows executables; in order to use either of them in Windows you need to compile the extension yourself from the C source code. For this reason, if you're on Windows, then Patchwork is your only sensible choice.
What I really want is this. I'm using a system that have a function. When I'm on localhost I want that function to do something different. I want to override the function with own stuff.
All alternatives are welcome as well.
function test() {
if($_SERVER['HTTP_HOST'] == 'localhost' {
// do one thing
} else {
// do other thing
}
}
If you're set on using runkit, you'd need to use runkit_function_redefine, not runkit_function_rename to make the same function do different things.
As explained earlier, it's probably best to differentiate inside of a function body regarding the value of $_SERVER['HTTP_HOST'].
Although I'd personally see this as bad style, you can even define function inside of other functions or blocks.
This snippet defines one function get_template_part():
if($_SERVER['HTTP_HOST'] == 'localhost' {
function get_template_part() {
}
} else {
function get_template_part() {
}
}
Unfortunately, this wouldn't help in your case, since get_template_part() is already defined outside your reach.
Someone might also experience that runkit_function_* functions are not working although the runkit library is installed correctly. This is because these functions are broken for some PHP versions (probably at least all 5.2.*) as can be seen here: https://bugs.php.net/bug.php?id=58205