How to Replace (Monkeypatch) PHP Functions

Is it possible to replace (monkeypatch) PHP functions?

This is a bit late, but I just want to point out that since PHP 5.3, it is actually possible to override internal functions without using a PHP extension.

The trick is that you can redefine an internal PHP function inside a namespace. It's based on the way PHP does name resolution for functions:

Inside namespace (say A\B), calls to unqualified functions are resolved at run-time. Here is how a call to function foo() is resolved:

  1. It looks for a function from the current namespace: A\B\foo().
  2. It tries to find and call the global function foo()

Is it possible to overwrite a function in PHP

Edit


To address comments that this answer doesn't directly address the
original question. If you got here from a Google Search, start here

There is a function available called override_function that actually fits the bill. However, given that this function is part of The Advanced PHP Debugger extension, it's hard to make an argument that override_function() is intended for production use. Therefore, I would say "No", it is not possible to overwrite a function with the intent that the original questioner had in mind.

Original Answer

This is where you should take advantage of OOP, specifically polymorphism.

interface Fooable
{
public function ihatefooexamples();
}

class Foo implements Fooable
{
public function ihatefooexamples()
{
return "boo-foo!";
}
}

class FooBar implements Fooable
{
public function ihatefooexamples()
{
return "really boo-foo";
}
}

$foo = new Foo();

if (10 == $_GET['foolevel']) {
$foo = new FooBar();
}

echo $foo->ihatefooexamples();

monkey patching in php

In the case of http://till.klampaeckel.de/blog/archives/105-Monkey-patching-in-PHP.html what actually makes the difference is the \ character used in front of the second strlen.

When you are using namespaces you can either use a namespace and directly invoke the methods/classes declared in the namespace:

use TheNamespace;
$var = new TheClass();

Or invoke the class explicitly by using something like:

$var = new \TheNamespace\TheClass();

So by invoking \strlen() instead of strlen() you are explicitly requesting PHP to use the default strlen and not the strlen defined for this namespace.

As for monkey patching you could use runkit (http://ca.php.net/runkit). Also with regards to patchwork there are a fair amount of examples in their website (http://antecedent.github.com/patchwork/docs/examples.html). You could check the magic method example that is replacing a function in a class.

Monkey patching in PHP 7

You might wanna use smth like:

class Module
{
private $version;

public function __construct($version){
$this->version = $version;
}

public function methodToTest()
{
if ($this->version === '4.0.0') {
// allow for additional options/methods
} else {
// use a subset
}
}
}

or another option would be injecting not version but a provider for that (if you know you will have some bit of complicated logic for versioning control -- so you can split the logic between Module and Provider as appropriate):

class Module
{
private $versionProvider;

public function __construct($provider){
$this->versionProvdier = $provider;
}

public function methodToTest()
{
if ($this->versionProvider->getVersion() === '4.0.0') {
// it could be even $this->versionProvider->newFeaturesAreSupported()
} else {
// some other stuff
}
}
}

and still another could be implementing some proxy class like

class Module
{
public function methodToTest()
{
$myMonostateProxy = new MyMonostateProxy();
$version = $myMonostateProxy->getVersion();
if ($version === '4.0.0') {
// allow for additional options/methods
} else {
// use a subset
}
}
}

so you can mock your monostate separately (probably via reflectioning on privtates or via its public interface, anyway don't forget to tearDown it). Real implementation of it would just call that uncontrollable Driver::getVersion().

I think first two options are cleaner but require some efforts for creation (as you need some injection to perform).
Third has that hidden dependecy and is somewhat tricky in testing and thus not quite clean and needs more efforts to maintaine but hides all that choice stuff inside itself making regular usage easier.

PHP Monkey Patching for 3rd party classes

The Patchwork library is so easy and I make it work with few tries.

http://patchwork2.org/

  1. Monekey patch any internal and other methods.
  2. Easy to use.

Can I wrap functions in PHP?

Frame challenge: if you did succeed in this, it would be using a blacklist to achieve security, which is basically impossible to do effectively. For every function you replace with a "safe" version, there will be ten you hadn't thought of that can be used in a malicious way.

Instead, you should either use a whitelist or a sandbox.

In a whitelist approach, you don't let the user enter normal PHP at all, but a special set of functionality that you've carefully picked to allow them to do what they need. That could be an actual subset of PHP that you parse with something like nikic/php-parser, a templating language like Twig, or a completely new language you write a simple parser for.

In a sandbox approach, you allow the user to enter full PHP, but you run it in an isolated environment where they can't affect your real server. Any access to the file system or network would only be accessing virtual resources, and if the process abuses CPU or memory resources, the entire sandbox can be terminated. See for instance how the 3v4l.org site is hosted.

How to overwrite existing PHP functions without APD?

Yes, by using

  • runkit_function_redefine — Replace a function definition with a new implementation

It's a PECL extension and can be installed via PEAR. But keep in mind that redeclaring functions does not substitute for proper refactoring. You are just exchanging one evil for another.

Also see Is it possible to replace (monkeypatch) PHP functions?



Related Topics



Leave a reply



Submit