Using Apache Thrift to Integrate Python Script and PHP

Introduction On our previous article Integrating python script and php we showcased how a person can execute python script using the symfony process c...

5 min read

Introduction

On our previous article Integrating python script and php we showcased how a person can execute python script using the symfony process component, however that process is efficient and suitable if the execution will only take place maybe once a day, for high frequency execution that approach might not be ideal due to the following
  1. High overhead - The execution starts python and stops it for every execution
  2. Scaling - Execution can only be done within the same server
  3. Output - The output only handles strings through the cli
  4. Concurrency - PHP will have to wait for the process to finish
Thus, the recommended approach is to make use of Apache Thrift 
What is Apache Thrift
Apache Thrift is an open-source framework used for scalable cross-language services development. Originally developed at Facebook and now hosted by the Apache Software Foundation, it allows you to define data types and service interfaces in a single definition file and then generate code in various programming languages. In simple terms, it's a translation that allows a particular service written in one programming language, such as PHP, to communicate with a service written in another programming language, such as Python. What enables Apache Thrift to facilitate communication between one programming language and another is what is called Remote Procedure Calls.

Pre-requisites

You will need the Thrift compiler installed on your system and the Thrift libraries for both languages.
MacOs: brew install thrift
PHP: Install the extension via Composer: composer require apache/thrift
Python: Install the library: pip3 install thrift
You can verify if the installation was successful by using: thrift -version on the cli then it will output the version in the following format: Thrift version 0.22.0 ,

Define the Interface (hello.thrift)

If you recall our definition, you will remember that the following service HelloWorldwill be a cross-language service; in our case, the cross languages are PHP and Python, and our hello.thrift The file is our single definition file, which also has the datatypes that are accepted as arguments, that is, a string with a variable placeholder of a name.

First, we define the "contract" between PHP and Python. Create a file named hello.thrift.
namespace php App.Thriftnamespace py hello_serviceservice HelloWorld { string sayHello(1: string name)}
After that, we can now be able to generate the boilerplate for both programming languages. Our Python code will be the server, and our PHP code will be the client. The following is made possible by the definition file implemented above.

Run the following in the terminal
thrift -r --gen php hello.thriftthrift -r --gen py hello.thrift

The Python Server

import syssys.path.append('gen-py')from hello_service import HelloWorldfrom thrift.transport import TSocketfrom thrift.transport import TTransportfrom thrift.protocol import TBinaryProtocolfrom thrift.server import TServerclass HelloWorldHandler:    def sayHello(self, name):        return f"Hello {name}, from the Python Thrift Server!"if __name__ == '__main__':    handler = HelloWorldHandler()    processor = HelloWorld.Processor(handler)    transport = TSocket.TServerSocket(port=9090)    tfactory = TTransport.TBufferedTransportFactory()    pfactory = TBinaryProtocol.TBinaryProtocolFactory()    server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)    print("Starting the Python server on port 9090...")    server.serve()
This Python script sets up a persistent RPC (Remote Procedure Call) server that listens for requests from other languages, such as PHP. It begins by adding the generated gen-py directory to the system path so it can import the HelloWorld service definition created by the Thrift compiler. The HelloWorldHandler class contains the actual "brain" of the service, where the sayHello method is defined to return a greeting string. To make this logic accessible over a network, the script initializes a Processor to map incoming requests to the handler, a ServerSocket to listen on port 9090, and a Binary Protocol to handle efficient data encoding. Finally, it starts a TSimpleServer, which enters an infinite loop (server.serve()), waiting to receive, decode, and respond to calls from any Thrift-compatible client.

The PHP Client
<?phprequire_once __DIR__ . '/vendor/autoload.php';// Manually include generated files if not using a PSR-4 autoloader for gen-php$GEN_DIR = __DIR__ . '/gen-php';if (file_exists($GEN_DIR . '/HelloWorldClient.php')) {    require_once $GEN_DIR . '/HelloWorldClient.php';    require_once$GEN_DIR . '/Types.php';} else {    // If it did create a subfolder, it usually matches the service name    require_once $GEN_DIR . '/App/Thrift/HelloWorldClient.php'; //   require_once$GEN_DIR . '/App/Thrift/Types.php';}use Thrift\Transport\TSocket;use Thrift\Transport\TBufferedTransport;use Thrift\Protocol\TBinaryProtocol;use App\Thrift\HelloWorldClient;try {    $socket = new TSocket('localhost', 9090);$transport = new TBufferedTransport($socket);$protocol = new TBinaryProtocol($transport);$client = new HelloWorldClient($protocol);$transport->open();    // Call the Python method as if it were a local PHP method    echo $client->sayHello("PHP Developer");$transport->close();} catch (\Exception $e) {    echo "Error: " .$e->getMessage();}
This is the most important part. Even though the actual logic for sayHello is written in Python, you call it in PHP as if it were a local function. When you run $client->sayHello(), the client:
1. Encodes "PHP Developer" into binary.
2. Sends it over the socket to port 9090.
3. Waits for the Python server to respond.
4. Decodes the binary response back into a PHP string.
The PHP client then sends a request to the server and receives a response. Running it will yield
Hello PHP Developer, from the Python Thrift Server!%

Conclusion

Compared to the first approach of using the Process Symfony Component, this is a far better and more efficient approach for not only combining PHP with Python, but also for building services in different programming languages that communicate with each other. Having the sub-process execution model (like the Symfony Process Component) in a structured RPC framework like Apache Thrift marks a shift from basic scripting to professional software architecture. While the process-based approach is quick for simple tasks, it creates significant overhead by spinning up the Python interpreter for every single request.

Share This Article

Get new tutorials in your inbox

No spam. Unsubscribe any time.

Also follow us on Google Search

Add as a Preferred Source on Google

Comments

0

Please log in or register to post a comment.

No comments yet — be the first to comment.

Keep Learning

More Articles
Await You

Browse the full collection of tutorials, guides and deep-dives — all free, all practical.