Dynamically convert procedural PHP to object-based
Moderator: General Moderators
Dynamically convert procedural PHP to object-based
Hi,
Slightly odd question this, and I may be going down totally the wrong path. I am building a PHP 'message centre', with modules that retrieve and/or produce messages in proprietary format. For example, one possible chain of events is that the POP3 module will retrieve emails from a mail box, convert them to a generic message format and pass them to a phpBB module to insert into a forum.
(N.B. I will be making a phpBB3 module in due course which I imagine will be easier, but for now it's phpBB2 as that's much more commonly used at this stage).
Because of the way the phpBB code is written (need I mention globals?), there are complications with using the phpBB code to help with inserting posts. Basically, either I include the phpBB function files into the scope of my application, and have to deal with the consequences of function-name collisions, uncontrollable database objects and global variables, or I copy all the functions I need into my own source files, using a more useful OOP approach.
I thought of a third possibility, which would be to parse the phpBB function files, find the functions I need and rewrite them dynamically on the fly, changing global variables to class properties and function calls to method calls.
Perhaps this is stupid, but it does have a few advantages:
- reduces the size of my application as I don't include phpBB files in my source code (and remember, I will eventually have any number of different modules, all of which might well suffer from similar problems)
- Easier to keep my app up to date with the phpBB source code, as long as I make the parser intelligently. The phpBB files will be updated by the end user, so hopefully no changes will need to be made to my code.
and an obvious disadvantage:
- I'll need to run the parser every time my application needs to make a phpBB post. This seems like a ridiculous waste of resources.
Any thoughts? Are there examples available of people parsing and modifying PHP source code from other applications on the fly?
Thanks for any help!
Slightly odd question this, and I may be going down totally the wrong path. I am building a PHP 'message centre', with modules that retrieve and/or produce messages in proprietary format. For example, one possible chain of events is that the POP3 module will retrieve emails from a mail box, convert them to a generic message format and pass them to a phpBB module to insert into a forum.
(N.B. I will be making a phpBB3 module in due course which I imagine will be easier, but for now it's phpBB2 as that's much more commonly used at this stage).
Because of the way the phpBB code is written (need I mention globals?), there are complications with using the phpBB code to help with inserting posts. Basically, either I include the phpBB function files into the scope of my application, and have to deal with the consequences of function-name collisions, uncontrollable database objects and global variables, or I copy all the functions I need into my own source files, using a more useful OOP approach.
I thought of a third possibility, which would be to parse the phpBB function files, find the functions I need and rewrite them dynamically on the fly, changing global variables to class properties and function calls to method calls.
Perhaps this is stupid, but it does have a few advantages:
- reduces the size of my application as I don't include phpBB files in my source code (and remember, I will eventually have any number of different modules, all of which might well suffer from similar problems)
- Easier to keep my app up to date with the phpBB source code, as long as I make the parser intelligently. The phpBB files will be updated by the end user, so hopefully no changes will need to be made to my code.
and an obvious disadvantage:
- I'll need to run the parser every time my application needs to make a phpBB post. This seems like a ridiculous waste of resources.
Any thoughts? Are there examples available of people parsing and modifying PHP source code from other applications on the fly?
Thanks for any help!
Not in PHP itself, but there are PHP -> AST parsers. Having abstract syntax tree for a script you can apply modifications to it and output modified result.
One of these parsers is phpc (though it's written in C++). Another would be a parser used by PHPAspect internally.
As a clever trick I may suggest you to implement a custom stream wrapper using stream_wrapper_register which would rewrite the source code on the fly (before it gets to PHP interpreter)
One of these parsers is phpc (though it's written in C++). Another would be a parser used by PHPAspect internally.
As a clever trick I may suggest you to implement a custom stream wrapper using stream_wrapper_register which would rewrite the source code on the fly (before it gets to PHP interpreter)
Wow - this is a whole new concept for me! How exciting!Weirdan wrote:As a clever trick I may suggest you to implement a custom stream wrapper using stream_wrapper_register which would rewrite the source code on the fly (before it gets to PHP interpreter)
I understand the very basic example given here, but I'm not quite sure how I would use such a class to do what you suggest. Could you just get me started along the right lines?
I guess I should pass the path to the phpBB file in the stream URL:
Code: Select all
fopen('myfunction://path_to_phpBB_file.php');Sorry, basic questions but I hope you can give me a few pointers!
OK, I'm getting somewhere. Is this the right way to start:
I didn't realise I could use so many of the PHP file functions - I thought I was limited to fopen(), etc.
Code: Select all
require_once 'myfunction://path_to_phpBB_file.php';I didn't realise I could use so many of the PHP file functions - I thought I was limited to fopen(), etc.
You can use file_get_contents() (not making an http request) to get the raw php code of the phpbb functions you want, then unset() the rest from memory, and assign them to your method scope. Since you would be retreiving the PHP code as a string, use of eval() would be needed (although subject to files being edited and security risks).
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
He doesn't make any http requests. 'something://' is a way to specify you're requesting resource using 'something' stream wrapper. http just happens to be one of these wrappers (more are available, including zip://, phar://, ftp://, string://) and custom wrappers (implemented either in extensions or by userland php code) may be registering, giving you even more flexibility over what are you getting from functions and language constructs operating on streams.not making an http request
Glad it worked for you... originally this idea is from this paper by Sebastian Bergmann and Günter Kniesel on GAP (Generic Aspects for Php)
Sorry - been away for a few days and missed your reply.
The solution inspired by Weirdan's 'clever trick' seems to be working beautifully. In short, what I do is this:
Great fun! I just got all my tests to pass this evening, and I can make a post to phpBB using function calls like the following:
...where call_func() simply returns:
The solution inspired by Weirdan's 'clever trick' seems to be working beautifully. In short, what I do is this:
- I use a helper class called m2f_external_interface
- Create a instance of the class in the var $external. The constructor sets up a stream handler using stream_wrapper_register ( string $protocol, string $classname ), where $protocol is an arbitrary string (not important what you use, just needs to be unique for each instance of $external) and $classname is the class you will use to parse and transform the PHP files.
- Using setters, pass in an array of the functions I want to parse, any constants that are required by the code and any global variables (phpBB uses these in almost every function!)
- Then run $external->load('/path/to/file') for every file I need to parse. Here's where the magic happens. I form the arrays for required functions, constants and globals, and the filepath to parse, into a query string using http_build_query, then initiate the parser like this: include($protocol . '://spacer?' . $query). I found I needed to add the spacer because of complications with the way parse_url() works with UNIX and Windows URIs.
- Calling include() using the magic protocol will initiate the parser defined in $classname. See the comprehensive template here for a guide to creating your own parser.
- In the parser I define a prefix $prefix to be added to all function and class names, as well as global variables.
- After parsing the URI I can get the arrays of function names, etc into local scope.
- foreach ($query['globals'] as $key => $var) $GLOBALS[$prefix][$key] = $var;
- In short, I read the file into memory, get the functions I need into a buffer (I use token_get_all() - see the manual page for hints on getting code blocks from tokens), add the prefix to all function and class names (declarations and usage), find examples of global $var; and replace corresponding instances of $var with $GLOBALS[$prefix]['var'] , and replace constants with the runtime values from the constants array.
- A new PHP file is built up from these function and class declarations, and is passed back to the stream handler using the stream_read() method.
- I now have all the phpBB functions defined with a custom prefix. Within those functions, any globals, constants and further function calls will also be under my control. I even pass a mock db object in as a global (that's the phpBB technique), trapping all db calls phpBB makes and using my own db object to handle them. So I can guarantee that any errors in the phpBB functions will be handled gracefully by my parser.
- The next step will be to cache the results of the parse in a static file, which can be included on subsequent requests to prevent all the overheads.
Great fun! I just got all my tests to pass this evening, and I can make a post to phpBB using function calls like the following:
Code: Select all
$this->_external->call_func('user_notification', array($mode, $post_data, $subject, $forum_id, $topic_id, $post_id, $notify_user));Code: Select all
call_user_func_array($this->_prefix . $function, $args)- stereofrog
- Forum Contributor
- Posts: 386
- Joined: Mon Dec 04, 2006 6:10 am
Care to explain why
is any better than
in other words, why do you need a stream wrapper here?
Code: Select all
include "blah://something";Code: Select all
include_blah("something");The alternative would be to transform the code from the files and send it to the PHP interpreter using eval(), right? Well, I admit I hadn't considered that, but I am very happy with my solution which I consider quite elegant. I've learnt a whole new section of the PHP manual, which must count for something!
If there's another method you have in mind, I'd be interested to hear that too!
"If eval() is the answer, you're almost certainly asking the wrong question." -- Rasmus Lerdorf
If there's another method you have in mind, I'd be interested to hear that too!
"If eval() is the answer, you're almost certainly asking the wrong question." -- Rasmus Lerdorf
- stereofrog
- Forum Contributor
- Posts: 386
- Joined: Mon Dec 04, 2006 6:10 am
Well, internally include() is just a combination of file_get_contents + eval, so eval might be"wrong", but this is what you use every time you're using include and require.
But I agree, what we learn on the way is sometimes more important than what we achieve.
"The movement is everything, the goal is nothing" -- someone.
But I agree, what we learn on the way is sometimes more important than what we achieve.
"The movement is everything, the goal is nothing" -- someone.