Hooking into function calls in php - php

A little background: At runtime I would like to be able to inspect the currently called functions javadoc-style documentation, to determine its formal (typed) declaration. This would allow runtime type checking (for simple and complex types) by means of reflection (at a cost) during debugging and testing, something that I would find immensely helpful.
So, in php I would like for a user defined function to get called whenever any other function is about to get called. That is, if a function foo() gets called, I would like to have my callHookHandler() function called immediately before.
One solution would be to implement __call() in all user defined classes, but that is both unwieldy and doesn't include support for functions defined outside classes, so I am looking for a better solution.

This sounds a bit of a fun one so I'm going to give answering it a try.
I hope this helps you. Let me know how it goes.
So, what you are asking can be done, and here's how:
For Functions:
Get all defined functions with $function = get_defined_functions().
Loop through the $functions['user'] key and inspect each one with the ReflectionFunction class. You'll need to get:
The comment using ->getDocComment()
The arguments using ->getParameters()
Do some magic (I'll let you figure out how to parse the comment using some regular extressions and match it up with the parameter list from the reflection. Don't forget optional parameters!)
Rename the function using runkit_function_rename
Generate code in a string that checks the parameters and calls the renamed function
Generate a parameter list as a string
Create a new function with runkit_function_add using the code you generated in step #5 and the parameter list from step #6.
For Classes:
Get a list of classes with $classes = get_declared_classes();
Loop through each one and inspect it with ReflectionObject and ->getMethods() to get the methods. Make sure that the class is not internal with ->isInternal() because we can't do anything about the internal classes.
In an inner loop... go through each method using the ReflectionMethod class. Get the arguments and PHPDoc/JavaDoc comments just like you did with normal functions.
Do the same thing you did with the functions only use runkit_method_add and runkit_method_rename instead.
Downsides:
You won't be able to do the checking on internal class methods and functions (which is fine because they won't have doc comments anyway).
This is a lot of work! I left a lot of parts up to your imagination to avoid this being the length of a short book.
Please send me this or open source it and let me know when you finish, I really want to use this myself. Contact info is on my website which is in my profile ;)
Alternatively:
You can use XDebug's function trace along with reflection then analyze the results after the fact so that you don't have to dynamically edit the code. If you want to write unit-test you could even automate it.
Hope type checking makes it into future versions of PHP and wait: https://wiki.php.net/rfc/typechecking
Notes:
This class reference has a potentially useful example of parsing docComments in the comments section of the page:
http://us.php.net/manual/en/class.reflectionmethod.php
References
get_defined_functions
get_declared_classes
ReflectionFunction
ReflectionObject
ReflectionMethod
runkit

Alternative way to hook into function call is to use a trick with namespaces: Intercepting Execution of System Functions in PHP
You can also use the Go! framework to define an aspect to intercept the execution of system functions automatically.

Related

Can I put PHP extension classes, functions, etc. in a namespace?

I am writing a PHP extension in C, and I would like to put the classes, functions, and variables I am creating in a namespace. I have not been able to find anything in the extension documentation regarding namespaces. To be clear, I want the equivalent of
namespace MyNamespace{
class MyClass{
}
}
but in a C extension. More specifically, I am looking for a function or macro in the Zend C API that allows me to assign a PHP namespace to a class or function I have written in C. Is this possible?
Putting a class in a namespace is very simple. Where normally you would initialize a class with
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "MyClass", my_class_methods);
instead write the second line as
INIT_CLASS_ENTRY(ce, "MyNamespace\\MyClass", my_class_methods);
The namespace does not need to be included in the method declarations or in the members of the my_class_methods array to properly match them with the class.
To use Namespaces in PHP extensions, you are basically just putting a prefix in front of the class or function name.
I'm not really a PHP internals developer, so the specifics are not entirely clear to me how this works, unfortunately there is very, very little information online that I could find about this as well (I really put Google through it's paces), and the article below is the best I could find.
However, it seems this article hints at the correct solution, which seems to be, that when you register the function with the Zend engine/PHP internals, you do so like "myNS\\MyFunc" and it should then be accessible from the myNS defined there. I would try out a few different variations with this, and see how far that gets you.
Your best option would be to ask in #php-internals on Freenode (if you can get an invitation) or on the PHP Mailing list.
If you manage to find a solution, the Internet seems to be in need of a good article on how one would accomplish this.
Source http://www.php-cpp.com/documentation/namespaces
A namespace is nothing else than a class or function prefix. If you want your classes or functions to appear in a specific namespace, you simply have to add a prefix to the class or function name....
Update: I've updated my answer to try to be more clear. I'm sorry it took so long, I originally replied from my Phone while I was traveling, with every intention of coming back and responding to your original comment, but I genuinely forgot about it until I got a notification from SO about comments. My apologies.

Method name clashing with language construct

I simply want to name a method in my class list(). If I try and do so:
class MyClass {
function list () {
// Do some stuff here
}
}
...I get a parse error (Unexpected T_STRING...). The same is true if I use echo or print - so I am guessing that I am not allowed to name a class method the same as a language construct, and while I cannot find any documentation that explicitly states this to be the case, I suppose it does make sense.
My question is, does anyone know of a work around to this, that will work in PHP 4.3.10? I know this can be done in PHP 5.3+ with closures (or I assume it can, I haven't actually tried) No versions of PHP to date support doing this with closures, but can anyone think of a work-around that does not rely on this, baring in mind that it is the method name that is important here?
EDIT N.B.
I am fully aware of how ancient and dead PHP4 is, but I have no option for this particular project. It is to be run on a platform distributed with PHP 4.3.10, which is a very low resource BusyBox platform with a MIPS processor, for which no compilers are provided. I have managed to create a cross-compiler and successfully build PHP 5.2 for it (I haven't tried 5.3 yet, it would probably involve compiling a newer Apache as well) but regardless of this, the manufacturer insist that this invalidates their warranty.
The dilemma you face will stay until you choose A or B.
A: Choose another function name, one that is not a reserved word.
B: Change PHP so that it provides the language features you need.
__call is not available in PHP 4. list is a reserved word since long time.
And you have not outlined what your specific problem with the class interface is, so I don't see much room for more alternatives.
As B does not look like a real option here, you need to take A.
Hope this helps that you can come to a conclusion.
Reserved keywords are reserved:
you cannot use any of the following words as constants, class names, function or method names
So rename your methods.
You've got two options.
1) rename your method. Unless there's a real reason you can justify your method's name then this is the one you should do.
2) namespace your class. If you wrap your class in its own namespace then you can reuse names that are normally reserved or predefined functions, or defined elsewhere. However you should only use this approach if you know what you're doing, otherwise you can end up causing a lot of confusion.
I'd opt for option 1 unless you have a really really compelling reason why your method name must have the same name as a PHP reserved word.
EDIT: You're using PHP 4 so option 2 is off the table. Your only choice is using a different name for your method. That, or upgrade to PHP 5.3. Even if you don't go with namespacing in the end I'd strongly advise upgrading to PHP 5.3 anyway, as PHP 4 is dead and has been for a long time. There will be no security patches released for it ever again so by running it you're basically making your server very insecure.
Kind of a dirty hack, but you could use the magic methods. PHP4 doesn't really support the __call like php5 does, but it could work with the 'overload' construct. I haven't tested this so I can't be sure..
( http://php.net/manual/en/function.overload.php )
class MyClass {
function __call($func, $args){
if ($func == 'list')
return $this->_list($args);
}
function _list () {
// Do some stuff here
}
}
overload('MyClass');
$myclass = new MyClass();
$myclass->list();

it is possible to extend a php function?

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.

How unique is PHP's __autoload()?

PHP's __autoload() (documentation) is pretty interesting to me. Here's how it works:
You try to use a class, like new Toast_Mitten()(footnote1)
The class hasn't been loaded into memory. PHP pulls back its fist to sock you with an error.
It pauses. "Wait," it says. "There's an __autoload() function defined." It runs it.
In that function, you have somehow mapped the string Toast_Mitten to classes/toast_mitten.php and told it to require that file. It does.
Now the class is in memory and your program keeps running.
Memory benefit: you only load the classes you need. Terseness benefit: you can stop including so many files everywhere and just include your autoloader.
Things get particularly interesting if
1) Your __autoload() has an automatic way of determining the file path and name from the class name. For instance, maybe all your classes are in classes/ and Toast_Mitten will be in classes/toast_mitten.php. Or maybe you name classes like Animal_Mammal_Weasel, which will be in classes/animal/mammal/animal_mammal_weasel.php.
2) You use a factory method to get instances of your class.
$Mitten = Mitten::factory('toast');
The Mitten::factory method can say to itself, "let's see, do I have a subclass called Toast_Mitten()? If so, I'll return that; if not, I'll just return a generic instance of myself - a standard mitten. Oh, look! __autoload() tells me there is a special class for toast. OK, here's an instance!"
Therefore, you can start out using a generic mitten throughout your code, and when the day comes that you need special behavior for toast, you just create that class and bam! - your code is using it.
My question is twofold:
(Fact) Do other languages have similar constructs? I see that Ruby has an autoload, but it seems that you have to specify in a given script which classes you expect to use it on.
(Opinion) Is this too magical? If your favorite language doesn't do this, do you think, "hey nifty, we should have that" or "man I'm glad Language X isn't that sloppy?"
1 My apologies to non-native English speakers. This is a small joke. There is no such thing as a "toast mitten," as far as I know. If there were, it would be a mitten for picking up hot toast. Perhaps you have toast mittens in your own country?
Both Ruby and PHP get it from AUTOLOAD in Perl.
http://perldoc.perl.org/perltoot.html#AUTOLOAD:-Proxy-Methods
http://perldoc.perl.org/AutoLoader.html
Note that the AutoLoader module is a set of helpers for common tasks using the AUTOLOAD functionality.
Do not use __autoload(). It's a global thing so, by definition, it's somewhat evil. Instead, use spl_autoload_register() to register yet another autoloader to your system. This allows you to use several autoloaders, what is pretty common practice.
Respect existing conventions. Every part of namespaced class name is a directory, so new MyProject\IO\FileReader(); should be in MyProject/IO/FileReader.php file.
Magic is evil!
The Mitten::factory method can say to itself, "let's see, do I have a subclass called Toast_Mitten()? If so, I'll return that; if not, I'll just return a generic instance of myself - a standard mitten. Oh, look! __autoload() tells me there is a special class for toast. OK, here's an instance!"
Rather such tricky code, use simple and verbose one:
try {
$mitten = new ToastMitten();
// or $mitten = Mitten::factory('toast');
} catch (ClassNotFoundException $cnfe) {
$mitten = new BaseMitten();
}
I think this feature comes in very handy, and I have not seen any features like it else where. Nor have I needed these features else where.
Java has something similar. It's called a ClassLoader. Probably other languages too, but they stick with some default implementation.
And, while we're at this. It would have been nice if __autoload loaded any type of symbols, not just classes: constants, functions and classes.
See Ruby's Module#const_missing
I just learned this: Ruby has a method on Module called const_missing that gets called if you call Foo::Bar and Bar isn't in memory yet (although I suppose that Foo has to be in memory).
This example in ruby-doc.org shows a way to use that to implement an autoloader for that module. This is in fact what Rails uses to load new ActiveRecord model classes, according to "Eloquent Ruby" by Russ Olsen (Chapter 21, "Use method_missing for flexible error handling", which also covers const_missing).
It's able to do this because of the "convention over configuration" mindset: if you reference a model called ToastMitten, if it exists, it will be in app/models/toast_mitten.rb. If you could put that model any place you wanted, Rails wouldn't know where to look for it. Even if you're not using Rails, this example, and point #1 in my question, shows how useful it can be to follow conventions, even if you create them yourself.

Is there anyway to get the order of the OOP method being called?

For example lets say I have a set of classes and methods to do so:
$obj->method1()->method2();
Is there anyway for method1() to know with in itself that its the first method being called or for method2 to know that its the last?
Some more details
I just want to be able to build a set of these calls so that it either returns an instance of itself if the call to the method isnt at the end of the chain or return something different if its at the end.
For example
$obj->method1()->method2(); #Here method 2 will return lets say a string.
$obj->method1()->method2()->method3(); #Since method2 isnt at the end of the chain, it should return an instance of itself (or another object) here so that the chain could continue.
EDIT: anyone whoz trying to do this - it is a bad design pattern.
This does seem to be a duplicate. Refer to this question for more answers.
Not out of the box, not even with a Stack trace.
I guess you could put something together using constants or global variables:
Don't try this at home!
$GLOBALS["chain"] = array();
$obj->method1()->method2(); // method1 adds member to $GLOBALS["chain"],
// array_push($GLOBALS["chain"], __FUNCTION__);
// method2 does the same...
print_r($GLOBALS["chain"]);
That would give you the full chain - not yet which one is the last one, to do that, you would have to pass a flag to method2().
But it would be horribly hacky and pollute your code.
Is there a specific reason you need this for?
All you could do is find out which methods have been called so far, by setting some kind of global state in the class. But you can't find out what methods are being called after a method, and you wouldn't be able to tell the difference between methods in one chain and methods in another:
$obj->m1()->m2();
$obj->m3(); // You would think that m1() and m2() came before this in the same chain
You would need to have a method at the end of each chain to clear the global state in the class.
Since it seems you need to see which method comes next in a chain, this won't work for you.
I would say that this is a really bad design pattern, at least for PHP (and every other language I've worked in). Each method should do one thing only. If you need a method to either return a string or an object depending on what you need it for later, you are doing something wrong.
Granted, I have done something like this before. It was a meta-information class for images submitted by users -- you could set it up like this:
$meta = new ImageMeta();
$meta->first_name("foo")->last_name("bar")->email("baz")->id("guid");
But, if you did this:
$meta->first_name();
it would return a string. The default value for the first parameter was NULL, and if the method got NULL, it returned a string. Otherwise it set (and escaped) an internal value and returned $this.
At first I thought it was kind of cool, but it turned out to be a mistake. I hate using that class now. Just make one method/function do one thing only and you will be much happier.
I don't think this is possible, no -- at least, I've never seen anything about this.
(Out of curiosity : why would you need that ? Maybe it would be possible to use onther solution to solve your actual problem ?)
The only way this is possible is to let the methods save some global state.
If both methods are in the same class, you could add a variable to the class and let each class set a unique value.
But the question is if this is desirable. This kind of behavior is often not very smart in the long run.

Categories