So you have a PHP project that needs to call a Python script to do some processing?
There are 4 possible ways to call a Python script from PHP:
- Call the Python script in the command line, using
shell_exec()
orexec()
. - Set the Python script as an API endpoint, and do a CURL call from PHP.
- Alternatively on an HTML page – Call the Python script, then pass the output to PHP.
- Finally, set the Python script as a socket endpoint.
Let us walk through examples of each method – Read on!
TABLE OF CONTENTS
DOWNLOAD & NOTES
Here is the download link to the example code, so you don’t have to copy-paste everything.
EXAMPLE CODE DOWNLOAD
Just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
SORRY FOR THE ADS...
But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.
Buy Me A Coffee Code Boxx eBooks
PHP CALL PYTHON
All right, let us now get into the examples of calling a Python script from PHP.
METHOD 1) COMMAND LINE
1A) PYTHON
print("Hello from Python CLI.")
This is probably one of the easiest methods, create your Python script as usual – This is just a dummy script.
1B) PHP
<?php
$script = __DIR__ . DIRECTORY_SEPARATOR . "1a-cli.py";
$result = shell_exec("python $script");
echo "PHP got the result - $result";
In PHP, use shell_exec()
or exec()
to call the Python script.
METHOD 2) CURL
2A) PYTHON
# (A) LOAD FLASK
from flask import Flask
app = Flask(__name__)
# (B) DUMMY ENDPOINT
@app.route("/")
def hello():
return "Hello from Python CURL."
# (C) GO!
if __name__ == "__main__":
app.run(host = "localhost", port = "8008")
There’s no need to be confused –
- Your “regular Apache-Mysql-PHP” stack will still run at
http://localhost:80
. - Python will deploy an “alternative” HTTP server at
http://localhost:8008
.
2B) PHP
<?php
$c = curl_init();
curl_setopt($c, CURLOPT_URL, "http://localhost:8008/");
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($c);
echo "PHP got the result - $result";
From PHP, we do a CURL call to Python at http://localhost:8008
.
METHOD 3) FETCH
3A) HTML JAVASCRIPT
/ (A) CALL PYTHON SCRIPT
function callPY () {
fetch("http://localhost:8008", {
method : "POST",
mode : "cors"
})
.then(res => res.text())
.then(res => callPHP(res))
.catch(err => console.error(err));
}
// (B) CALL PHP SCRIPT
function callPHP (res) {
// (B1) DATA FROM PYTHON
let data = new FormData();
data.append("data", res);
// (B2) FETCH PHP
fetch("http://localhost/3c-fetch.php", {
method : "POST",
body : data
})
.then(res => res.text())
.then(res => console.log(res))
.catch(err => console.error(err));
}
// (C) GO!
callPY();
This roundabout alternative involves a “third party”, HTML/Javascript – On the HTML page, we call the Python script first, then pass the result to PHP.
3B) PYTHON
# (A) LOAD FLASK
from flask import Flask, Response, request
app = Flask(__name__)
# (B) ALLOWED ORIGINS
allowed = ["http://localhost", "https://localhost"]
# (B) DUMMY ENDPOINT
@app.route("/", methods=["POST"])
def hello():
print(request.environ["HTTP_ORIGIN"])
if "HTTP_ORIGIN" in request.environ and request.environ["HTTP_ORIGIN"] in allowed:
response = Response("Hello from Python FETCH.", status=200)
response.headers.add("Access-Control-Allow-Origin", request.environ["HTTP_ORIGIN"] )
response.headers.add("Access-Control-Allow-Credentials", "true")
return response
else:
return Response("Not Allowed", status=405)
# (D) GO!
if __name__ == "__main__":
app.run(host = "localhost", port = "8008")
No need to be confused, this is the same as the previous example with minor additions:
- This will still deploy a Python HTTP server at
http://localhost:8008
. - We also output cross-origins (CORS) headers to support the
fetch()
calls… Yep,localhost
andlocalhost:8008
are considered to be “different domains”.
3C) PHP
<?php
echo "PHP received - " . $_POST["data"];
Since Javascript has “already called Python”, we process whatever output from Python as usual.
METHOD 4) SOCKET
4A) PYTHON
# (A) LOAD SOCKET MODULE
import socket
# (B) CREATE SOCKET SERVER
s = socket.socket()
s.bind(("127.0.0.1", 8008)) # bind to 127.0.0.1:8008
s.listen(1) # allow only 1 client
print("Ready")
# (C) ACCEPT CONNECTION
conn, addr = s.accept()
print("Connection from: " + str(addr))
while True:
data = conn.recv(1024).decode()
if data:
print("Received: " + str(data))
break
conn.close()
Finally, we can also set a Python endpoint using TCP
.
4B) PHP
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, "127.0.0.1", "8008");
socket_write($socket, "Hello from PHP!");
socket_close($socket);
In PHP, we connect to the Python endpoint and do the “data exchange”.
P.S. If you get “socket_create()
is an invalid function”, enable the module in php.ini
– extension=sockets
EXTRAS
That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.
PASSING PARAMETERS
- Yes, Python does accept command-line arguments.
import sys
print(sys.argv)
- In CURL/fetch, we can do POST/GET as usual.
- In sockets, we are pretty much sending/receiving data between 2 servers.
WHICH IS THE BEST METHOD?
It depends.
- If you just want something simple, the command line will do.
- If you have multiple Python scripts, it is better to set up many endpoints.
- If you are dealing with huge amounts of data, socket will be better.
LINKS & REFERENCES
- shell_exec and exec – PHP
- Python Flask
- CURL – PHP
- Fetch API – MDN
- POST Form Data with Fetch – Code Boxx
- Socket – Python | PHP
THE END
Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!