class ref name: phpfile
category-group: files
layer: 10

The PHP file object (phpfile_o) starts with a single text file that is used as a "template" to generate other files. In this regard it differs from most other file group objects since it does not focus on a single file in the file system. This class has member functions for generating new files.

In order for this object to perform, the CLI PHP must be avaiable to Z Directory programs. CLI PHP is bundled with the normal PHP processor used by web servers to process PHP pages. You need to tell the Z Directory libraries where it is. This needs to be done only once, and should be done when you install the Z Directory.

The intended usage of this object is to process a file repeatedly, where the file is used as a template - basically just like a web server would use a .php file to repeatedly generate a .html file. The original design of this object was to provide customized letters for mass mailings. However, this facility can be used not just as a document processor, but also as a "back door" to run any PHP program. E-mail is certainly not the only application this methodology is good for. The generated files can be fed to web servers, auto-responders, fax machines and printers. There are probably 1,000 other uses we haven't thought of and would very much like your feedback as to how you have emploeyed this object.

To use the PHP file object, PHP must be installed on the local computer. Basically, the PHP executable program is used as an external subsystem. This is not violate Z Directory conventions or principles. Many other objects rely on external systems, such as the database components (they need access to an SQL-based relational database); layer-0 registry functions (they rely on a [Microsoft] registry); or the email address object, which relies on a MTA (mail server) to transport e-mail.

If for any reason the location of the PHP SAPI is not known at program run time, it can be set via the set_php_homedir() function. If you don't know where it is, you can [in Microsoft environments] open a command (or powershell) window and type:

  which php
If which is not available to you, or for whatever reason you can't find it, check the PHPRC environment variable:
  set | grep PHP
If that doesn't work (maybe you don't have a Microsoft version of grep), look for a directory such as "C:/php" or "C:/php5". Worst case, you can use Windows Explorer to run a search on the hard drive where your OS is installed (almost always C:) for the file "php.ini".

When you have it, you can place this line somewhere near the start of your program (assuming it's in C:\\php5"):

  int ie;
  directory_o d ("C:\\php5");
  phpfile_o::set_php_homedir (d, &ie);
  if (ie) { /* .. process error-failure */ }

Given that PHP CLI is installed, configured, and available, it's time to use it from your c++ program. There are 3 logical components to this scheme:

  1. a form (or "template") letter, containing embedded PHP code;
  2. the application program that generates files frmo the form letter document;
  3. the set of data applied to the form letter document.
An example of a debt collection letter will be used here to illustrate how to put it all together (this may be depressing choice, but should easily make the point of how and why this object can be used). Typically, debt collectors send out letters demanding payment. The nature of the contents of these letters is usually tied to how much is owed and how long the debt is overdue. The following example uses these 2 variables in addition to the recipient's info to determine which text to put into the letter:
    $ie = 0;
    $bizname = zphp_dbag_value ("business name",     $ie);
    $fname   = zphp_dbag_value ("person name first", $ie);
    $lname   = zphp_dbag_value ("person name last",  $ie);
    $sex     = zphp_dbag_value ("person sex",        $ie);
    $married = zphp_dbag_value ("person is_married", $ie);
    $numdays = zphp_dbag_value ("debt time",         $ie);
    $amount  = zphp_dbag_value ("debt amount",       $ie);
    if ($sex == 'F')
        if ($married == 'YES')
            echo ("Mrs. ");
            echo ("Ms. ");
       echo ("Mr. ");
    echo ("$lname");
Your debt of <?PHP echo("$amount"); ?> is overdue.
    if ($numdays > 180)
       echo ("Looks like you have no intention of paying this.\n");
    else if ($numdays > 90)
       echo ("I mean, this is WAY overdue.\n");
    else if ($numdays > 30)
       echo ("Send payment IMMEDIATELY to avoid legal action.\n");
Please BE ADVISED that in addition to the full force of the government
and the law, we have a band of ARMED THUGS that are know where you
live and are going to come to your place in the middle of the night
and mess you up.
    if ($fname == 'Gary' && $lname == 'Dale'
            && $bizname == 'CENTRIC CONSULTING')
    echo ("\nPS: You suck. Rot in hell, you bloodsucking leach.\n");
Notice that in addition to this file, a databag is used. The PHP function zphp_dbag_value() supplies the databag values. Even with some PHP functions you cannot escape Z Directory coding standards and conventions. The second parameter ("$ie" above) is an output variable that is set to 0 if the item was found, or 1 if not (or, if something besides 0 or 1, a [strange] error occurred). Alternatively, you can get data this way:
    $fname = $_SERVER['person-name-first'];
    $lname = $_SERVER['person-name-last'];
    $ndays = $_SERVER['debt-time'];
That dashes ('-') in the variable names above are used to separate components in the path to a databag value. This is because from the C++ side, we do a traversal of the databag and push each item into the environment, via z_setenv(). Spaces are not normally allowed inside environment variable names, so we convert them to something else. The default is the dash. You can configure the separator to something else, such as '/' or pipe ('|'), if you prefer (you can even use a long, multi-character string like "HEY,YOU!" or "XXX", but be careful it's not soemthing that's part of a databag variable. We recommend you leave it alone).

You may be wondering where the PHP function zphp_dbag_value() comes from. This is supplied with the Z Directory install, and is temporarily appended to your template file during processing.

You may be wondering where does the databag come from, or perhaps you're wondering what the heck is a "databag" . You supply it, in the application. Note that the data need not come from a databag - it can come from, say, a file, or a database. Or perhaps there is even no data at all in your application. But, if you do want to pass data from the C++ program to the PHP processor that handles the template file, the only direct way to do it in this context is via a single recursive databag (actually, you can build your own plumbing, such as shared memory, files, or the registry). Be advised to keep the databag relatively small, eg, if it has 100,000 elements in it, the CPU usage / processing time may turn out to be huge. This is because every element in the data bag is accessed and made into an environment variable prior to file processing.

The templete file in this case is a plain text file. Note that you don't want to use HTML lexemes such as "<P/>" or "<BR>" (If your goal is to generate HTML files, however, you would put these constructs in your file).

Finally, here is the source code that invokes the PHP processing, including a sample data-bag:

#include "z_phpfile.h"

const char debtor_TP[] = "debtor \ ( \ business ( name \"CENTRIC CONSULTING\" ) person ( name ( first Gary last Dale ) ) debt ( time 3700 amount \"$250,000\" ) )";
int main() { file_o fout ("my_letter.txt"); // set up output file rec_dbag_o mydata (debtor_TP); // set up a data-bag phpfile_o momoney ("collection.txt"); // set up template file momoney.process(fout, mydata); // the meat! send_to_printer(fout); // app code - print it next (downstream) return 0; }
Thus, as can be seen in this example, with 3 variables and 1 line of code, you have done form processing. But.. OK! before you yell - yes, there are many more lines of PHP code, and that is where the application logic resides.

The generated output will be saved to the file specified by "fout". You need not specify a file name - if there is none, the PHP subsystem will assign one, by generating a temporary file.

PHP file object brige diagram

member functions (primary)

SIGNATURE: phpfile_o ()
SYNOPSIS: creates a a new file object, completely devoid of name, path or contents.

SIGNATURE: phpfile_o (const phpfile_o &rhs)
SYNOPSIS: creates a a new file object, based on the file represented by file object "rhs". The file's address will be copied.

operator = (phpfile_o)
SIGNATURE: const phpfile_o &operator = (const phpfile_o &rhs)
The current file object is set to that of "rhs". This is not a "deep copy" operation, as the file object is made to simply point to whatever "rhs" is pointing to (if anything). The resuls is that the current file object will have a file address that is equal to that of "rhs" (they will point to the same file).
To copy the file who's file name is provided by the variable "rhs", use one of the "deep copy" functions, such as copy_contents() , copy_from() , or copy_to() .

SIGNATURE: ~phpfile_o ()
destroys the class object instance. If the instance has flags z_Remove_On_Exit and z_Bound_To_File both set, the file will be deleted when the instance goes out of scope (eg when the destructor is invoked). any open file handle in this object to the file will be closed.

SIGNATURE: phpfile_o (const file_o &f)
basically equivalent to "phpfile_o (const phpfile_o &f)" constructor. This creates a new PHP file object and sets the address to that specified in "f" (which should be set!).

SIGNATURE: int set_templatefile (const file_o &f, int *pi = NULL)
This function copies the file given by 'f' to the current object. The source file 'f' must exist and be readable, else this fucntion call will fail and return an error (-1). It is basically equivalent to the constructor with a file object (or descendent of file object) as an argument.
The input file, 'f', is intended to be a "template", either a PHP file or text file containing PHP, to be [repeatedly] processed, generating post-PHP output. This function should be called prior to invoking the member function process().

  • pi: an [optional] error indicator [output] variable. values:
    0: no error, file was succesfully copied;
    zErr_NotFound: file 'f' was not found
    0: the file is succesfully set as a template file (eg, copied);
    -1: the file does not exist (or other error).

    SIGNATURE: int process (file_o &fo, const rec_dbag_o &bag, const flag_o *myflags = NULL, int *pi = NULL)
    SYNOPSIS: generates a new file from the [curr object's] template

    • fo: output file. Output from PHP SAPI is copied into the address specified by 'fo'. It is an error to not set the output file address prior to invoking this function.
    • bag: recursive data-bag containing data for the PHP processor
    • myflags: pointer to a flag object. This is currently unused and reserved for future options for process(). THe pointer is optional, but one must be provided if the output error codes are desired (c++ mechanics).
    • pi: an [optional] error indicator [output] variable. values:
      0: successful PHP processing; output file created
      1: output file address not set
      2: could not exec the PHP processor
      3: could not remove pre-existing (previous) output file
      zErr_NotInitialized: could not find/set up PHP directory
      zErr_Create_Failed: could not create/copy a temp. file
    This function takes a pre-loaded template text file, which can [optionally] contain PHP source code, and invokes the local PHP SAPI (there should be one installed-available on the same host computer where the program is run). Prior to invoking the PHP processor, all the data found in the databag 'bag' is pushed to the environment. This makes the "external" data in 'bag' available to the PHP SAPI, and hence the PHP code in the template file. The data can be retrieved in the same manner as a web server CGI POST form:
      $fname = $_SERVER['name-first'];
      $lname = $_SERVER['name-last'];

    0: successfully processed the template file & made a new file
    -1: error ocurred (see value of "pie" variable for reasons why)

    SIGNATURE: int set_php_homedir (const directory_o &d, int *pi)
    sets the directory address where the PHP processor (aka "SAPI") resides. This directory must exist and contain the PHP SAPI (eg, "php.exe") in order for this call to succeed. If the Z Directory has been installed on the current host computer, an explicit call to this function should not be necessary.

    • d: directory address object pointing to where "php.exe" resides. This object must be set in order for this function to succeed.
    • pi: a [mandatory] error indicator [output] variable. values:
      0: all ok, success
      zErr_NotFound: invalid directory
      zErr_Resource_Missing: directory does not have "php.exe"
      1: could not make a registry entry (#1) for the Z Directory on the current computer
      2: could not make a registry entry (#2) for the Z Directory on the current computer (HKLM/SOFTWARE/Vettrasoft/ZDirectory)
    TRAITS: this is a static member function

    SIGNATURE: int set_dbag_separator (const string_o &s, int *pi)
    configuration function to set the character(s) that separate items in a databag path. The default separator is a single dash (eg, hyphen, or '-').

  • s: string containing the separator to be used. All calls to phpfile_o::process() after invoking this function will use the new separator found in 's'. This string cannot contain any whitespace (tabs, spaces, or newline-endline characters).
  • TRAITS: this is a static member function. it is currently *not implemented*

    Usage of the PHP file object as given by the main example on this page is only the tip of the Z Directory iceberg. You can integrate its form processing capabilities using data-bags with e-mail sending capability, as well as extracting the data from a database via such ready-to-use "businss" objects such as person_o, worker_o, and business_o.

    There appears to be a bug in the unsetting of environment variables in Microsoft OS contexts. As an emergency stop-gap measure, all environment variables are set to the string "[zNOT_SET]" (without the double-quotes) prior to being unset. If the variable is not cleared, it may have this value (macro zEMsg_PHPVAR_NOTSET, in z_phpfile.h). Your PHP code should check for this, eg:

    $biz_name = $_SERVER['full_name'];
    if ($biz_name == '[zNOT_SET]')
        $biz_name = '';