PHP 5中的方法拦截。*

I'm implementing a Log system for PHP, and I'm a bit stuck.

All the configuration is defined in an XML file, that declares every method to be logged. XML is well parsed and converted into a multidimensional array (classname => array of methods). So far, so good.

Let's take a simple example:

#A.php
class A {
    public function foo($bar) {
        echo ' // Hello there !';
    }

public function bar($foo) {
    echo " $ù$ùmezf$z !";
}
}

#B.php
class B {
public function far($boo) {
    echo $boo;  
}
}

Now, let's say I've this configuration file:

<interceptor>
<methods class="__CLASS_DIR__A.php">
        <method name="foo">
    <log-level>INFO</log-level>
    <log-message>Transaction init</log-message>
        </method>
</methods>  
<methods class="__CLASS_DIR__B.php">
        <method name="far">
    <log-level>DEBUG</log-level>
    <log-message>Useless</log-message>
        </method>
</methods>
</interceptor>

The thing I'd like AT RUNTIME ONLY (once the XML parser has done his job) is:

#Logger.php (its definitely NOT a final version) -- generated by the XML parser
class Logger {
public function __call($name,$args) {
    $log_level = args[0];
    $args = array_slice($args,1);
    switch($method_name) {
        case 'foo':
        case 'far':
        //case .....
            //write in log files
            break;

    }
    //THEN, RELAY THE CALL TO THE INITIAL METHOD
 }
}

    #"dynamic" A.php
class A extends Logger {
    public function foo($log_level, $bar) {
    parent::foo($log_level, $bar);
        echo ' // Hello there !';
    }

public function bar($foo) {
    echo " $ù$ùmezf$z !";
}
}

#"dynamic" B.php
class B extends Logger {
public function far($log_level, $boo) {
    parent::far($log_level, $bar);
    echo $boo;  
}
}

The big challenge here is to transform A and B into their "dynamic" versions, once the XML parser has completed its job.

The ideal would be to achieve that without modifying the code of A and B at all (I mean, in the files) - or at least find a way to come back to their original versions once the program is finished.

To be clear, I wanna find the most proper way to intercept method calls in PHP.

What are your ideas about it?

PS: and of course, there should be NO CONSEQUENCE on the client code (no different if interception is enabled or not).

You could use eval() to actually define the classes, but you should be very careful. The eval() function can be very dangerous.

Something like this:

$parentName = 'Bar';

eval('class Foo extends ' . $parentName . ' { }');

http://php.net/eval

This solution once again uses eval, but I'll post it for your consideration anyway because I think it's a really nice way of dynamic inheritance.

The method here is to use an intermediary class which extends some default class that can be changed (in this instance to a different class also extending the default).

I'm uncertain as to what it is in your setup that is not allowing for this kind of working -- if you clarify this I could perhaps provide a better recommendation.

<?php

/*
 * One of the following classes will be the superclass of the Child
 */

class Ancestor {
    function speak() {
        echo 'Ancestor <br />';
    }
}

class Mum extends Ancestor {
    function speak() {
        parent::speak();
        echo 'Mum <br />';
    }
}

class Dad extends Ancestor {
    function speak() {
        parent::speak();
        echo 'Dad <br />';
    }
}

/*
 * Decide on which class we wish to make the superclass of our Child
 */

$parentClass = null;

if (isset($_GET['parent'])) {
    $parentClass = $_GET['parent'];
    if (!class_exists($parentClass)) {
        $parentClass = "Ancestor";
    }
}

if (!is_null($parentClass)) {
    eval("class ChildParent extends $parentClass {};");
} else {
    class ChildParent extends Ancestor {};
}

if (class_exists('ChildParent')) {
class Child extends ChildParent
{
    function speak() {
        parent::speak();
        echo 'Child <br />';
    }
}
}

/*
 * Show what's going on
 */

echo '<a href="?">Either</a> | <a href="?parent=Mum">Mum</a> | <a href="?parent=Dad">Dad</a> <br />';

$child = new Child();
$child->speak();*