Channels ▼

Jocelyn Paine

Dr. Dobb's Bloggers

How to Call SWI-Prolog from PHP 5

February 10, 2010

I have scripts on my Web site that call SWI-Prolog from PHP 5. They run: a game for teaching Prolog, where the players are "intelligent" lorries that survive by buying and selling their freight, and buying petrol with the profit thereby earned; a science-fiction plot generator; and a generator of examples in category theory. A reader saw these last week and asked me why his Prolog-from-PHP script didn't work; so I'm going to explain how to code ones that do. My examples should work, mutatis mutandis, with other Prologs and other languages; and if you program in SWI-Prolog, they'll give you tested code that will get you started straight away.

Contents

Introduction: a simple script

I'll start by showing how to call an SWI-Prolog program from PHP 5 in four different ways. The program itself is as simple as can be, merely defining one predicate named test/0 that writes a sentence to standard output. For most other languages, that's analogous to a subroutine or procedure that does the same. This is the predicate, which writes four lines, newline being escaped as "\n":

test :-
  write( 'Prolog \nwas called \nfrom PHP \nsuccessfully.' ).

Next, please follow the link Calling SWI-Prolog from PHP to run the script on my site that calls it. In case the server should be down, here is the business portion of the script's output. It's a table that lists four different sets of PHP statements that invoke a run-SWI-Prolog command stored in the PHP variable $cmd. The statements are on the left and their output is on the right:

  system( $cmd );<br />
Prolog <br />was called <br />from PHP <br />successfully. <br />
  $output = exec( $cmd );<br />  echo $output;<br />
successfully. <br />
  exec( $cmd, $output );<br />  print_r( $output );<br />
Array<br />(<br />    [0] => Prolog<br />    [1] => was called<br />    [2] => from PHP<br />    [3] => successfully.<br />)<br /> <br />
  $output = shell_exec( $cmd );<br />  echo $output;<br />
Prolog <br />was called <br />from PHP <br />successfully. <br />

And now I'll show the script. Because the one linked above that generated the table has a lot of HTML markup in it, I'm going to show a shortened version, that generates the output you'll see by following the link Calling SWI-Prolog from PHP (short). The full script is in the Appendix. Here is the short one:

<HTML>
<HEAD>
<TITLE>Calling SWI-Prolog from PHP (short)</TITLE>
</HEAD>

<H1>Calling SWI-Prolog from PHP (short)</H1>

<?
  $cmd = "nice -n15 /software/bin/pl -f /home/popx/cgi-bin/test.pl -g test,halt";
?>

<P>
<PRE>
<?
  system( $cmd );
  echo "\n";

  $output = exec( $cmd );
  echo $output;
  echo "\n";

  exec( $cmd, $output );
  print_r( $output );
  echo "\n";

  $output = shell_exec( $cmd );
  echo $output;
  echo "\n";
?>
</PRE>
</P>

</BODY>
</HTML>

I'll assume that you know enough PHP to understand that the script outputs the HTML markup verbatim, and that it executes the statements between "<?" and "?>" tags, sending their output to the server's output stream.

The first of these statements sets the variable $cmd to a Linux command that runs SWI-Prolog on my code. This is the command; all I've done above is to put it in a string by enclosing with quotes:

nice -n15 /software/bin/pl -f /home/popx/cgi-bin/test.pl -g test,halt

Be nice with "nice"

A point of etiquette is worth noting next. I could have written this command:

/software/bin/pl -f /home/popx/cgi-bin/test.pl -g test,halt
However, the "nice" reduces its priority, and hence interference with the server's other users. On a multi-user machine — such as mine, which belongs to the excellent hosting company Mythic Beasts — this is polite.

The SWI-Prolog command line

The rest of the command runs the SWI-Prolog executable from its home in /software/bin/pl . It passes it the file in which I defined test/0 , using SWI's -f option to read it from /home/popx/cgi-bin/test.pl . The -f , and other SWI command-line options, are described in the section on Command-line options in the SWI-Prolog reference manual.

The command finishes with another option, flagged by -g . The point is that so far, we've told Prolog which source file to load, but not which predicate from that file to run. That's what -g does. It passes the compound goal:

test,halt
which calls test/0 and then executes the built-in halt/0 to end the program.

So that is the command to run Prolog. The rest of the script invokes it in four different ways, by calling the PHP functions "system", "exec" (in two ways), and "shell_exec". I'll explain these next, and then discuss their merits.

Calling SWI-Prolog with PHP 5's "system", "exec", and "shell_exec" functions

The "system" function runs the command and sends all its output to standard output. More information can be found from the PHP manual entries for these functions, linked from here.

The one-argument variant of the "exec" function runs the command and returns only the final line of its output as a string.

The two-argument variant of "exec" puts an array into its second argument. Each line is one line of the command's output. In my script, I displayed this array with the built-in print_r.

Finally, the "shell_exec" function runs the command and returns all its output as a string. This function is identical to PHP's backticks operator.

One thing that puzzled me was the difference between shell_exec and the one-argument form of "exec". Others have wondered the same, as a Google search on "difference exec shell_exec" will show. On PHPBuilder.com, contributor "dalecosp" gives code fragments that demonstrate the functions — and also "passthru", used for sending back binary data such as images. He says that shell_exec knows how to find built-in commands such as "ping", implying that "exec" doesn't. However, I tried invoking Linux "ls" with "exec", and it worked.

My search also turned up complaints about bugs in one function or the other. I should say that I have had no problems with PHP 5's "system" or "shell_exec" under Linux, and that I've not used the other functions except in these examples.

Which function should one use?

Given these ways of invoking Prolog, when are they appropriate? The "system" function is useful when you simply want to squirt all your program's output back at the Web browser. I do this in my science-fiction plot generator, as you can see from the source I've put up of its script.

The one-argument "exec" doesn't seem particularly useful, and I included it only to show how it differs from the two-argument variant. The latter is useful for the same reason that shell_exec is, namely that it allows one to manipulate the output rather than just flinging it all into a browser. Such manipulation, for preserving the Prolog program's state between transactions in a session, and for splitting off different chunks of output to display in different regions of the page, is what I'll discuss next, with reference to my Traveller game for teaching Prolog.

Traveller, a game that saves its state

Traveller is about an "intelligent" lorry that drives round a road network on the game board, buying and selling goods at shops along the way. Because the lorry must also buy petrol, and because goods cost money and take up space, which limits carrying capacity, and because different shops charge and pay different prices, brains are needed to earn enough money to survive. Students can thus compete with the environment, and with one another, by trying to program sufficiently clever brains.

The game was originally part of my Logic Programming Tutor, a front-end for Prolog with which I taught my students. There, users advanced lorries from one square to the next by typing an appropriate Prolog goal. On the Web, however, I've connected this goal to the "Next" button. Pressing the button moves a lorry one square, depicting its location in yellow, and also causes it to take whatever buying or selling decision its brain may dictate.

The game needs to preserve state between button presses. Amongst the components of this state are the goods in the lorry, the lorry's position, how much fuel it has, and its cash. So how do I do this without making Prolog save it to a temporary file or a database?

Answer: by making Prolog send the state, as well as the game's output, to standard output. If our script can capture standard output in a PHP variable, we can then split the two apart, emit the game output, and save the rest in PHP's $_SESSION variable.

Session-handling in PHP 5: the $_SESSION array

So what is $_SESSION? It's a built-in PHP array. Like any other PHP array, but unlike arrays in most languages, its indices can be integers or strings. Unlike other PHP arrays, PHP preserves its contents from one Web transaction to the next, making it terribly easy to implement session handling.

I'm not going to explain session handling in detail, but a small example may help. It's one you can run from my page Running a PHP Session. And you can get its source from the section on PHP Sessions: Using PHP's isset Function in Tizag's tutorial PHP Sessions - Why Use Them?, where I copied it from.

It displays a counter that increments every time you reload the page. Tizag accomplish this by storing the counter as $_SESSION['views']. The page tests whether this contains a value, and if so, increments it. This is the code:

<?php
session_start();
if ( isset( $_SESSION['views'] ) )
    $_SESSION['views'] = $_SESSION['views'] + 1;
else
    $_SESSION['views'] = 1;

echo "views = ". $_SESSION['views'];
?>
For more info on sessions and $_SESSION, Google "session handling in PHP $_session".

How Traveller saves its state

Returning to the idea of making Prolog write state, as well as game output, to its standard output, this is what my Traveller script, listed here, does. You will see that it contains the following code fragment:

$output = shell_exec($cmd);

// Build the Prolog command and, using shell_exec, get
// the output as a string.

list($english_state,$board,$prolog_state) = split ("==========", $output, 3);
// Split output into an English description of the new
// state, a table displaying the board, and the new
// state.

$_SESSION['game_state'] = $prolog_state;
// Store the latter.

There are some statements before this fragment that look the game state up in $_SESSION['game_state'], then build a command to invoke Prolog so that it loads the game program and passes it the state as argument to a goal.

This command is held, as before, in $cmd. The script calls shell_exec($cmd), assigning its output to $output. By looking for the separator "==========" which the game inserts into its output, the script splits this output into three parts, which it assigns to $english_state, $board, and $prolog_state.

The first two variables, $english_state and $board, both get displayed to the user. One lists the lorry's contents, cash, and so on. The other is the game board, complete with road map, shop names, and yellow square marking the lorry's position. These, the script sends back to the browser. There is some static HTML between them, which I wrote verbatim into the script. As I said earlier, capturing program output in a string enables us to reorder parts of it in this way.

The third variable, $prolog_state, gets assigned back to $_SESSION['game_state']. And that completes one cycle, ready for the next run of the script.

A simpler example of saved state

Lest Traveller be too complicated, I'll present a simpler script which you can run as is. It may help you get started with Prolog and sessions, because if you can't run it, something is probably wrong with your setup; but if you can, then you can build more complicated variants by gradually replacing parts of the script and program.

The script emits similar output to the one shown in my section on Session-handling in PHP 5: the $_SESSION array. The difference is that it's Prolog that generates the output, and Prolog that increments the view counter. To try the script, please go to the link Running a PHP Session with Prolog.

Next, here is the Prolog predicate it runs. The predicate, in file test2.pl , takes one argument, ViewsSoFar. It displays this plus a small message, then writes an "==========" separator, then increments ViewsSoFar into ViewsNow, which it writes. These pieces of output either side of the "==========" correspond to the game output and game state from Traveller:

test( ViewsSoFar ) :-
  write( 'Prolog \nwas called \nfrom PHP \nsuccessfully.
\n' ),
  format( 'Views = ~w.~n', [ViewsSoFar] ),
  ViewsNow is ViewsSoFar + 1,
  write( '==========\n' ),
  format( '~w~n', [ViewsNow] ).

And here is my script:

<HTML>
<HEAD>
<TITLE>Running a PHP Session with Prolog</TITLE>
</HEAD>

<BODY>

[ <A href="/index.html">Jocelyn Paine's Home Page</A>
| <A href="/dobbs/prolog_from_php.html"><CITE>How to Call SWI-Prolog from
PHP</CITE></A>
]

<CENTER>
<H1>Running a PHP Session with Prolog</H1>
</CENTER>

<?
session_start();

if ( isset( $_SESSION['views'] ) ) {

    $views_so_far = $_SESSION['views'];

    $goal = "test($views_so_far)";

    $cmd = "nice -n15 /software/bin/pl -f /home/popx/cgi-bin/test2.pl -g \"$goal,halt\"";

    $output = shell_exec( $cmd );

    list($english_state,$prolog_state) = split ("==========", $output, 2);

    echo $english_state;

    $_SESSION['views'] = $prolog_state;
}
else
    $_SESSION['views'] = 1;

?>
</P>

</BODY>
</HTML>

Notice that the script echoes $english_state. This is the output to the browser, as in Traveller. Whilst $prolog_state gets assigned to $_SESSION['views'], updating it and ending the cycle.

Running a pre-compiled SWI-Prolog program

We can increase speed by, in effect, pre-compiling programs in order to avoid reading their source each time the script is called. The SWI-Prolog reference manual explains this in its section on Generating Runtime Applications. The feature is actually more general than pre-compilation, because one can load a program from its source file, let it do introductory calculations such as asserting new predicates, and then save it as a runtime application. Obviously, this avoids not only the time taken to read and compile, but also the time needed for these calculations.

The SWI-Prolog HTTP server libraries

Because my Traveller script starts SWI-Prolog and loads my program into it on every transaction, as well as reading and writing the game state, it is probably less efficient than letting SWI-Prolog run all the time and act as its own Web server. This can in fact be done, and is explained in the SWI-Prolog manual's section about The HTTP server libraries. I haven't used them, because I am running Prolog on a commercial hosting company's machine. But for those who do have the privileges, it's worth trying.

The HTTP server libraries get discussed from time to time on the SWI-Prolog mailing list. Indeed, SWI's author Jan Wielemaker has just sent us a mail recommending his SWI-Prolog how-to on Web services, How to create a web-service easily? It explains how to find information about JSON/XML responses, not yet covered by the how-to.

Appendix: the full script that showed four ways of invoking Prolog

This is the script referred to in the Introduction:

<HTML>
<HEAD>
<TITLE>Calling SWI-Prolog from PHP</TITLE>
</HEAD>

<BODY>

[ <A href="/index.html">Jocelyn Paine's Home Page</A>
| <A href="/dobbs/prolog_from_php.html">How to Call SWI-Prolog from
PHP</A>
]

<CENTER>
<H1>Calling SWI-Prolog from PHP</H1>
</CENTER>

<?
  $cmd = "nice -n15 /software/bin/pl -f /home/popx/cgi-bin/test.pl -g test,halt";
?>

<P>
<TABLE BORDER>
<TR>
<TH ALIGN=left>
<PRE>
  system( $cmd );
</PRE>
</TH>
<TD ALIGN=left>
<PRE>
<?
  system( $cmd );
?>
</PRE>
</TD>
</TR>
<TR>
<TH ALIGN=left>
<PRE>
  $output = exec( $cmd );
  echo output;
</PRE>
</TH>
<TD ALIGN=left>
<PRE>
<?
  $output = exec( $cmd );
  echo output;
?>
</PRE>
</TD>
</TR>
<TR>
<TH ALIGN=left>
<PRE>
  exec( $cmd, $output );
  print_r( $output );
</PRE>
</TH>
<TD ALIGN=left>
<PRE>
<?
  exec( $cmd, $output );
  print_r( $output );
?>
</PRE>
</TD>
</TR>
<TR>
<TH ALIGN=left>
<PRE>
  $output = shell_exec( $cmd );
  echo $output;
</PRE>
</TH>
<TD ALIGN=left>
<PRE>
<?
  $output = shell_exec( $cmd );
  echo $output;
?>
</PRE>
</TD>
</TR>
</TABLE>
</P>

</BODY>
</HTML>

Links

www.swi-prolog.org/ — SWI-Prolog.

www.php.net/ — PHP.

www.swi-prolog.org/pldoc/refman/ — SWI-Prolog reference manual.

www.php.net/manual/en/index.php — PHP 5 reference manual.

www.j-paine.org/cgi-bin/spin.php — My science-fiction plot generator.

www.j-paine.org/spin_script.html — Source listing of its script.

www.j-paine.org/cgi-bin/traveller.php — My Traveller game.

www.j-paine.org/traveller_script.html — Source listing of its script.

www.j-paine.org/cgi-bin/webcats/webcats.php — My interactive category-theory demonstrations.

www.j-paine.org/cgi-bin/demo_php_calls.php — My page that demonstrates the PHP "system", "exec", and "shell_exec" functions, listed in the Appendix.

www.j-paine.org/cgi-bin/short_demo_php_calls.php — Short version of the above, listed in the Introduction.

www.swi-prolog.org/pldoc/doc_for?object=section%282%2c%20%272.4%27%2c%20swi%28%27%2fdoc%2fManual%2fcmdline.html%27%29%29 — SWI-Prolog reference manual section on the command line.

php.net/manual/en/function.system.php — PHP reference manual section on the "system" function.

php.net/manual/en/function.exec.php — PHP reference manual section on the "exec" function.

php.net/manual/en/function.shell-exec.php — PHP reference manual section on the "shell_exec" function.

www.php.net/manual/en/language.operators.execution.php — PHP reference manual section on the backticks operator.

www.google.com/search?hl=en&rlz=1B3GGGL_enGB219GB237&q=difference+exec++shell_exec&lr=&aq=f&aqi=&oq= — Google search for "difference exec shell_exec".

www.phpbuilder.com/board/archive/index.php/t-10260187.html — Contributor "dalecosp"'s code fragments demonstrating "system", "exec", "passthru", and "shell_exec".

php.net/manual/en/function.passthru.php — PHP reference manual section on the "passthru" function.

www.php.net/manual/en/reserved.variables.session.php — PHP reference manual section on the $_SESSION array variable.

php.net/manual/en/language.types.array.php — PHP reference manual section on arrays.

www.j-paine.org/cgi-bin/demo_session.php — My page that demonstrates PHP sessions, discussed in the section on Session-handling in PHP 5: the $_SESSION array.

www.tizag.com/phpT/phpsessions.php — Tizag's tutorial on PHP sessions PHP Sessions - Why Use Them?.

www.google.com/search?hl=en&rlz=1B3GGGL_enGB219GB237&q=session+handling+in+PHP+%24_session&lr=&aq=f&aqi=&oq= — Google search for "session handling in PHP $_session".

www.j-paine.org/cgi-bin/demo_session_with_prolog.php — My page that demonstrates PHP sessions with Prolog: the simple one from the section on A simpler example of saved state.

www.swi-prolog.org/pldoc/doc_for?object=section%281%2c%20%2710%27%2c%20swi%28%27%2fdoc%2fManual%2fruntime.html%27%29%29 — SWI-Prolog reference manual section on generating runtime applications.

www.swi-prolog.org/pldoc/doc_for?object=section%282%2c%20%273%27%2c%20swi%28%27%2fdoc%2fpackages%2fhttp.html%27%29%29 — SWI-Prolog reference manual section on the HTTP server libraries.

www.swi-prolog.org/Mailinglist.html — SWI-Prolog mailing list.

www.swi-prolog.org/howto/http/ — SWI-Prolog how-to on Web services, How to create a web-service easily?

mailbox.iai.uni-bonn.de/mailman/public/swi-prolog/2010/003218.html — SWI-Prolog mail about the HTTP server libraries. Explains how to find information on JSON/XML responses.

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Dr. Dobb's TV