Calling PHP code from Python

Many years ago our organization had a dying change management system. As the organization began looking for a new enterprisey system, our team felt it was a good time to roll out a release management system for our own process.

We decided to roll our own solution because at the time we were the only team using release management, and this wasn’t a priority for the organization to support.  Built on PHP and JQuery, it did what we needed. Though as we move towards the future, we have been seeing cracks that need to be filled.

One crack is that our architecture design wasn’t great. Now updating the system or rolling out new features has become difficult. We are re-architecting the solution, but because of old design considerations we haven’t been able to  figure out how to add API services so  we can be more flexible on the front end, including building a mobile app.

As I was thinking about how much I want to roll out a mobile version of our site, I began to think about how we could use Python, one of my current favorite languages, and particularly flask, to bolt on a solution to provide api services. My thoughts moved to two places-

  1. Building our api services in PHP.  I really have no experience in this, so it hasn’t been an option I’ve explored.
  2. Building a fully separate microservice in Python, duplicating some of our workflow engine rules and SQL queries.  This scares me. Having essentially two separate solutions would be a maintanance nightmare.
  3. Building a light framework in Python that hooks into existing PHP code. Using the existing PHP code, so one update affects both solutions, and having a powerful Python based api service to feed a mobile app.

So I have started looking into this. This is the very beginning, faithfully calling PHP code from within Python.

 

Example.php

Our PHP function(s). These functions could have parameters.

There’s nothing special to do on this side. Write some standard PHP, it can or doesn’t have to return a value. Our function returns some text.

Our return value would be what is passed back to the Python call.

<?php

function readTheFile(ourFile) {
 $myfile = fopen($ourFile, "r") or die("unable to open the file");
 return fread($myfile, filesize($ourFile));
 
 fclose($myfile);
}

?>

dict.txt

Here I’m simulating data in a file, but in the function above we could return data from anywhere.

stuff
and things
and other things

Example.py

Here’s where the magic happens.

We’re creating an OS process to open the file with PHP, and we have the actual PHP call as a block of text that will be executed.

In php(code), we take in a small amount of PHP code that actually calls the PHP function from the file above. It opens up a program called “php” and pipes the input, output and stderr back to us in Python using subprocess.Popen(). In Python 3.5 it’s recommended to use run(), we don’t here.

We then call subprocess.communicate, and send our PHP code block defined in the Python code, encoded in a byte stream, through stdin to the PHP call made with subprocess.Popen(). the return result comes back in our o variable.

When we’re done, we clean things up by trying to kill the subprocess, here defined in p. At the end, we return the values received from the PHP code in o.

Stored in the code variable we have our PHP code that echoes the return value of readTheFile() PHP code back to o.  We could make our include and readTheFile() parameter both variables passed in, which we will do at some other time.

We then call our Python defined php() function, passing in the Python defined PHP code block as a parameter. This starts the process of opening the subprocess “php”, piping the byte encoded “code” text block to that subprocess, and then getting the echoed value back and stored into the res variable.

After that we can do what we like with the information returned, process it further or in our case printing it out.

import subprocess

def php(code):
 p = subprocess.Popen(['php'], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)

 o = p.communicate(code.encode())[0]
 try:
 os.kill(p.pid, signal.SIGTERM)
 except:
 pass

 return o


code = """<?php
 include('example.php');
 echo readTheFile('dict.txt');

?>
"""

res = php(code)
print("And here is the result of the PHP call yo {}".format(res))


I hope that was more than clear as mud!

Next time we will look at further parameterizing our Python code to move towards a framework. After that we will explore building an API service around this code.

Published by

Jason Woods

Jason is a self proclaimed geek, gourmand, beer and espresso nut and father to a smart-as-a-whip young lad. In his spare time he manages the Integrated Core Services tribe at the University of Chicago Medicine.

Leave a Reply

Your email address will not be published. Required fields are marked *