Contents

1. What is ZZEE PHPExe?

ZZEE PHPExe compiles PHP, HTML, Javascript, Flash and other web files into Windows GUI programs. You can rapidly develop Windows GUI applications by employing the familiar PHP web paradigm. You can use the same code for online and Windows applications with little or no modification.

Examples of compiled applications include ZZEE PHPExe itself and ZZEE Text Utility, available as both online and Windows application. There are other samples as well.

1.1. System requirements

ZZEE PHPExe and applications compiled with it run on Windows 7, Vista, Windows XP, Windows Server 2003 and Windows 2000. For Windows 2000 Internet Explorer 5.5 or higher is needed.

2. Changes history

2.1. Version 2.6.1

2.2. Version 2.6.0

2.3. Version 2.5.2

2.4. Version 2.5.1

2.5. Version 2.5.0

2.6. Version 2.4.0

2.7. Version 2.3.0

2.8. Version 2.2.0

2.9. Version 2.1.0

2.10. Version 2.0.0

This version is a step towards better compatibility with lots of legacy code, including various frameworks (such as Zend framework) and applications. Many significant changes are made, making it much easier to compile legacy PHP code and use standard PHP tricks.

2.11. Version 1.0.0

First public release

3. Benefits

4. Features

5. How it works

When your compiled exe starts, the internal webbrowser loads the "http://127.0.0.1/" URL (the main address of the internal webserver), and the webserver sends a default index page from the Main > Source directory directory to the webbrowser. By default this is "index.php" or "index.html", but can be any file of your choice, which you can define in Webserver > General Settings > Directory index files. What happens next depends on your application's design.

6. Samples

There are several sample applications available that you can download and compile to see compatibility of ZZEE PHPExe. They cover various APIs, frameworks and technologies, including Google Youtube API, Flash, Zend Framework, Wiki, PHP sessions, etc. The samples are located on our website: ZZEE PHPExe samples.

6.1. Hello World application

Create a directory "phpSource" in the "c:\" drive, so its full path is "c:\phpSource". Put there a file "index.php" with the following contents:

<h1>Hello World!</h1>
<hr/>
<?php
echo phpinfo();
?>

Create a new directory "phpCompiled" in the "c:\" drive, so its full path is "c:\phpCompiled". Run ZZEE PHPExe, and put c:\phpSource into the Source directory box, then put c:\phpCompiled into the Output directory box, then myapp.exe into the Exe file name box. Then select Project > Compile & Run from the menu. Enjoy!

Note: such application is provided in the "samples" folder of the program installation directory. Put c:\pathtoZZEEPHPExe\samples\helloworld into Main > Source directory, replacing pathtoZZEEPHPExe by the actual path.

7. PHP specifics

7.1. Compiled internal namespace

From the programmer's standpoint the main difference between ZZEE PHPExe and a standard PHP setup is that PHP scripts and other web files are not in the file system for ZZEE PHPExe. They are contained in a special compiled internal namespace. All files from Main > Source directory of your project go to the www directory of the compiled namespace, so "/www" is the document root directory of the internal webserver.

Starting from version 2.0.0, you can read (but not write to) files and get directory contents of the compiled internal namespace. You can read compiled-in files by prepending file paths with the "zzee://" URL wrapper.

To read compiled-in files it is recommended using absolute paths, i.e. those which start with "/www". Given that the wrapper is "zzee://", then any absolute filepath should have three slashes after the colon, like "zzee:///www/myscript.php".

For example, you have a user_scripts subdirectory inside your Main > Source directory, and you want to run file "myScript.php", depending on its presence there:

$urlWrapper = "zzee://";
$docRoot = $_SERVER['DOCUMENT_ROOT']; // this will always return "/www"

// Verify that file 'myScript.php' exists in 'user_scripts' dir, 
// and if it is exists, include it:
if (file_exists($urlWrapper . $docRoot . '/user_scripts/' . 'myScript.php') {
    // Note, we can include/require PHP scripts both with and without "zzee://" wrapper
    include_once ($docRoot . '/user_scripts/' . 'myScript.php');
}

A path with two slashes after colon like "zzee://file.txt" is a relative path, and all relative internal paths are resolved against a PHP script being run via HTTP. For example, for HTTP request of "http://127.0.0.1/dir/script.php", the script being run will have an absolute path of "/www/dir/script.php" in the internal namespace, and if this script tries to read a file "zzee://file.txt", then the absolute internal path of the file is resolved as "/www/dir/file.txt".

With the "zzee://" wrapper, you can use any file/directory PHP function to read or browse compiled-in files, including fopen, fread, file_get_contents, file_exists, filetype, opendir, readdir, etc. Below is the example of getting directory contents of the "/www" directory:

header("Content-type: text/plain");
$dir = "zzee:///www/";
if ($dh = opendir($dir)) {
    while (($file = readdir($dh)) !== false) {
        echo "filename: $file, filetype: ". filetype($dir . $file) . "\n";
    }
    closedir($dh);
}

Note, you can not change current directory with chdir() in the internal compiled namespace, chdir() works only for filesystem.

7.1.1. Security warning

If your application reads some files from the filesystem and their path is input by users somehow, be careful to check that they have not entered "zzee://" at the beginning of the file path, because this way they can read compiled-in files that you may want to keep secret.

7.2. Including PHP files with include or require

Including or requiring PHP files works both with and without the "zzee://" URL wrapper, as PHPExe understands that including PHP files should be performed only for files located in the compiled-in namespace, and looks for files there. So it does not matter whether a PHP file name or path is prepended with "zzee://".

Note, if you check the File Encoding > Encode PHP files checkbox, then it is recommended that you include all PHP libraries at the very top of the PHP script and not inside any function or class. In particular, autoloading may not work. If you use autoloading (like in Zend framework), then do not check that checkbox in your project settings.

If you want to encode your PHP files with bcompiler, then make sure that all of your PHP libraries are included without any conditions, not inside any function or class.

Below are examples of valid include/require paths, note, "zzee://" part is optional:

require_once 'libs/audioLib.php'; // relative path
require_once 'zzee://libs/audioLib.php'; // relative path with zzee://
require_once '/www/libs/audioLib.php'; // absolute path
require_once 'zzee:///www/libs/audioLib.php'; // absolute path with zzee://

7.3. file_exists function

Starting from version 2.6 PHPExe will return TRUE for PHP function file_exists() if a PHP file (that is, a file with its name ending in ".php") is being checked and it really exists in the compiled namespace.

This is done to cope with a typical construct used in so many frameworks like:

if (file_exists($f)) { include_once($f); }

This should help compiling many of the frameworks with no or few changes as possible.

Note, if you want to turn off this feature you need to run this javascript code:

<script type="text/javascript">
external.doPhpFileExists = false;
</script>

7.4. __FILE__ constant

Starting from version 2.0.0, this constant returns an absolute script path in the internal namespace like "/www/script.php" (whether File Encoding > Encode PHP files is checked or not).

So, for example, you can use __FILE__ to get the internal directory path where the script being executed is located:

$file = "zzee://" . dirname(__FILE__) . '/file.xml';
$doc = new DOMDocument();
$doc->load($file);

7.5. Reading and writing data files

Data files are various non-PHP files that you want to read or write in your application, such as database, XML, ini, templates, text, etc. Some of them can be compiled into the internal namespace, some may reside in the filesystem of the computer where your program runs. You can read (but not write) all compiled-in files, see section compiled internal namespace for details.

To deal with files located in the filesystem, you can use usual PHP filesystem functions. When your compiled application starts, its current working directory is set to the directory where its exe is located. You can get this directory with $_SERVER['ZZEE_PHPEXE_SERVER_ROOT'] variable (will return a UTF-8 encoded value) or getcwd() PHP function at run time.

This example reads a text file from the internal namespace and copies it to a disk file:

$fileSource = "zzee://" . dirname(__FILE__) . '/sample.txt';
$text = file_get_contents($fileSource);
$fileDest = 'sample.txt'; // file in the current working directory in the filesystem
file_put_contents($fileDest, $text);

7.6. PHP Sessions

Starting from version 2.0 PHP sessions work reliably. You don't need to change anything in the initial php.ini settings to enable PHP sessions.

7.7. Inheriting class from Exception

If you create a class, which inherits from the built-in Exception class, and check the "Encode PHP files" flag in your project, then you will get an error (this is a PHP bug, at least in the version 5.2.5 of PHP that ships with ZZEE PHPExe). So either don't inherit from this class or do not encode PHP script if you do.

7.8. Compatibility with caching and compiling PHP extensions

While the program is compatible with most existing PHP extensions, it is incompatible with some of the PECL PHP extensions that do caching or compilation tasks. They are Advanced PHP Debugger (php_apd.dll), Alternative PHP Cache (php_apc.dll), BLENC (php_blenc.dll), Classkit Functions (php_classkit.dll), Parsekit Functions (php_parsekit.dll), Runkit Functions (php_runkit.dll), Threads (experimental implementation of threads) (php_threads.dll). Other similar extensions can also be incompatible with the Program.

7.9. PHP extensions dependant DLLs

Some of the PHP extensions (like php_ibm_db2.dll, php_oracle.dll, php_oci8.dll, etc) need additional dlls (typically database drivers) that they rely upon, and which are not part of PHP. These DLLs are not included with ZZEE PHPExe and typically provided by installations of the appropriate software (like DB2 or Oracle).

7.10. PHP Flush() Support

PHPExe supports the "no buffering" option, so you can enable full PHP flush() support. To enable it set external.buffering Javascript variable to false. That is, execute an HTML file with this code inside it *before* running your PHP script:

<script type="text/javascript">
external.buffering = false;
window.location.href = '/path/to/your/script.php';
	// Redirect to PHP script after changing external.buffering
</script>

Also you need to set output_buffering = Off in php.ini and do flush() in your code accordingly.

Warning! This is still an experimental option, as a different threading model is used there, and it is recommended to turn it off right after you are done with your progress control. To turn it off set external.buffering to true.

7.11. Getting command line arguments to your exe

Since version 2.6.0 you can get command line arguments passed to your compiled application via the $argv PHP variable. First you need to set register_argc_argv to "on" in the php.ini section. Then you can use variable $argc to get the number of arguments and array $argv to get arguments. Note, that the first argument is always the name of the exe file, so $argc is at least 1. This behavior is different from Javascript external.argv, where exe name is not present.

7.12. Current working directory

When your compiled application starts, its current working directory is always set to the directory where its exe is located. You can get this directory with $_SERVER['ZZEE_PHPEXE_SERVER_ROOT'] variable (will return a UTF-8 encoded value) or getcwd() PHP function at run time. To get the directory where a caller assigned your exe to be run, you need to use PHP variable $_SERVER['ZZEE_PHPEXE_FIRST_WORKING_DIRECTORY'], this variable is added in PHPExe v 2.6.0.

To change the directory use PHP function chdir().

Note, you can not change current directory with chdir() in the internal compiled namespace, chdir() works only for filesystem.

8. Javascript specifics

8.1. onunload, onbeforeunload and onunloadzzeephpexe events

When user navigates to another URL, the "onunload" and "onbeforeunload" events work as you expect them, however the "onunload" shall not be relied upon when the main application window is closed via the "x" button at the title bar, via Alt+F4 or via window.close(). Use onbeforeunload or onunloadzzeephpexe events for this case.

Starting from version 2.3 the "onbeforeunload" event is fired for the main window when the window is being closed when user clicks the "x" button on the the window's title bar or via shortcut like Alt+F4, and works exactly like in Internet Explorer. In particular, if you assign a string value to window.event.returnValue or return any non-empty string value from that function, it will trigger a dialog box with some predefined wording and your phrase in the middle of it asking user if to close the window. For more information see Microsoft website. However there is a more flexible solution in "onunloadzzeephpexe" event, which is described below.

The "onunload" event is also fired for the main window, but may terminate unexpectedly and we do not recommended using it, there is no guarantee it will complete. We recommend using either "onbeforeunload" or "onunloadzzeephpexe" events.

When a popup window is closed via clicking on 'x' or via Alt+F4, only the "unload" event is supported.

The "onunloadzzeephpexe" event fires only for the main window, and doesn't fire for any popups. This event should be assigned to the "window" object (like the "onbeforeunload" event handler). If the function returns false, then it means that the main window will not be closed. If it returns anything else (for example, true or 0 (zero)), the window is closed. The advantages of this event against the "onbeforeunload" event are that with the "onunloadzzeephpexe" you can silently decide whether to close the application, or if you want to ask the user, you can use your own wording in the dialog. Note, if the "onunloadzzeephpexe" is fired, then "onbeforeunload" event is not triggered. The following examples show how this event works.

// Example 1:
function onClose () {
	return confirm("Are you sure you want to quit now?");
	// If user clicks OK, the application will close
	// If user clicks Cancel, the application will stay
}
window.onunloadzzeephpexe = onClose;

// Example 2:
window.onunloadzzeephpexe = function () {
	// See if the application can be closed
	if (canClose) { return true; }
	else { 
		// Do something to inform the user that the application can't be closed
		return false;
	}
}

8.2. Popup windows

You can create pop-up windows using Javascript window.open() like you do with standard webbrowsers. You can pass top, left, width and height on creation to define popup window size and position.

8.2.1. Compatible popup windows

Compatible popups behavior is what you expect from popup windows. If you open another window using window.open() with the same target name, an existing popup with the same name will be re-used, instead of a new window. Also a variable holding popup like myWnd = window.open(...) can be used to manipulate the popup from another window. The only downside is that compatible popup windows are less secure, and a hacker can get HTML and Javascript (not PHP) source of your exe.

8.2.2. Not compatible popup windows

They are more secure than "compatible" windows, but you can not keep the returned window object reference in a variable. Or need to declare this variable (using the var keyword) inside a function only, like:

function myfunc()
{
    var newWindow = window.open(url, "_blank", "width=300, height=200");
    // ...
}

And the variable shall not be kept longer than the actual pop-up window created exists. Or do not keep the variable at all, like:

window.open(url, "_blank", "width=300, height=200");

Also when you open another window using window.open() with the same target name, a new window will be created, and existing window with the same name won't be re-used.

8.2.3. Owned popup windows

Owned popups do not have their own icon in the taskbar, they are minimized and restored along with the main window, and they are always on top of the main window.

8.2.4. Resizing popup windows

Resizing windows with javascript resizeTo() and resizeBy() doesn't work in this version of ZZEE PHPExe. You need to pass the size of the pop-up window in the window.open() like in the example above. If you need to specify the size at run time, you can dynamically build the parameters' string, like:

window.open(url, "_blank", "width=" + neededWidth + ",height=" + neededHeight);

8.3. AJAX (XMLHttpRequest)

You can use AJAX in your applications just like in a standard webbrowser. Starting from version 2.1 there are no limitations, in particular you can get HTTP response status and response headers via getResponseHeader().

8.4. Using Flash

You can use Flash just like in a standard webbrowser. Here is an example that runs a Flash animation:

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="400" height="267" id="flash">
<param name="movie" value="/flash/2_clock.swf">
<param name="quality" value="high">
<param name="bgcolor" value="#FFFFFF">
<embed src="/flash/2_clock.swf" quality="high" bgcolor="#ffffff" width="400" height="267" name="flash" align="" type="application/x-shockwave-flash"></embed>
</object>

Note, that the flash MIME type (application/x-shockwave-flash), as well as other MIME types which a flash applet may use like flv video (video/x-flv) must be set as cacheable at Webbrowser > File Caching. You need to mention all needed MIME types in the "Cacheable MIME types" box, and set "Allow file caching" to "Allow only for MIME types specified below". If you have problems with a flash applet not able to load needed files, set "Allow file caching" to "...any type on NEEDFILE..." temporarily to determine if the problem lies in caching of flash related files.

8.5. ZZEE PHPExe specific Javascript extensions

ZZEE PHPExe provides some Javascript extensions, so you can perform things not normally available in Javascript. Note: these extensions are available regardless of version of Internet Explorer installed on user's computer.

8.5.1. external.ServerRoot

This property returns a full path to the directory where compiled exe is located on the user's computer. Note, the according PHP variable is $_SERVER['ZZEE_PHPEXE_SERVER_ROOT'].

<script type="text/javascript">
var dir = external.ServerRoot;
alert(dir);
</script>

8.5.2. Constants for file and folder dialogs and ShellExecute

// Windows Comm dialog flags for file open/save.
// More information is available here:
// http://msdn.microsoft.com/en-us/library/ms646839.aspx
var OFN_READONLY = 0x00000001;
var OFN_OVERWRITEPROMPT = 0x00000002;
var OFN_HIDEREADONLY = 0x00000004;
var OFN_NOCHANGEDIR = 0x00000008;
var OFN_SHOWHELP = 0x00000010;
var OFN_ENABLEHOOK = 0x00000020;
var OFN_ENABLETEMPLATE = 0x00000040;
var OFN_ENABLETEMPLATEHANDLE = 0x00000080;
var OFN_NOVALIDATE = 0x00000100;
var OFN_ALLOWMULTISELECT = 0x00000200;
var OFN_EXTENSIONDIFFERENT = 0x00000400;
var OFN_PATHMUSTEXIST = 0x00000800;
var OFN_FILEMUSTEXIST = 0x00001000;
var OFN_CREATEPROMPT = 0x00002000;
var OFN_SHAREAWARE = 0x00004000;
var OFN_NOREADONLYRETURN = 0x00008000;
var OFN_NOTESTFILECREATE = 0x00010000;
var OFN_NONETWORKBUTTON = 0x00020000;
var OFN_NOLONGNAMES = 0x00040000;
var OFN_EXPLORER = 0x00080000;
var OFN_NODEREFERENCELINKS = 0x00100000;
var OFN_LONGNAMES = 0x00200000;
var OFN_ENABLEINCLUDENOTIFY = 0x00400000;
var OFN_ENABLESIZING = 0x00800000;
var OFN_DONTADDTORECENT = 0x02000000;
var OFN_FORCESHOWHIDDEN = 0x10000000;


// Windows shell SHBrowseForFolder flags
// More information is available here:
// http://msdn.microsoft.com/en-us/library/bb773205.aspx
var BIF_RETURNONLYFSDIRS = 0x0001;
var BIF_DONTGOBELOWDOMAIN = 0x0002;
var BIF_STATUSTEXT = 0x0004;
var BIF_RETURNFSANCESTORS = 0x0008;
var BIF_EDITBOX = 0x0010;
var BIF_VALIDATE = 0x0020;
var BIF_NEWDIALOGSTYLE = 0x0040;
var BIF_BROWSEINCLUDEURLS = 0x0080;
var BIF_UAHINT = 0x0100;
var BIF_NONEWFOLDERBUTTON = 0x0200;
var BIF_NOTRANSLATETARGETS = 0x0400;
var BIF_BROWSEFORCOMPUTER = 0x1000;
var BIF_BROWSEFORPRINTER = 0x2000;
var BIF_BROWSEINCLUDEFILES = 0x4000;
var BIF_SHAREABLE = 0x8000;
var BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);


// Windows ShowWindow constants.
// Also used in other functions like ShellExecute.
// More information is available here:
// http://msdn.microsoft.com/en-us/library/ms633548.aspx
var SW_HIDE = 0;
var SW_SHOWNORMAL = 1;
var SW_NORMAL = 1;
var SW_SHOWMINIMIZED = 2;
var SW_SHOWMAXIMIZED = 3;
var SW_MAXIMIZE = 3;
var SW_SHOWNOACTIVATE = 4;
var SW_SHOW = 5;
var SW_MINIMIZE = 6;
var SW_SHOWMINNOACTIVE = 7;
var SW_SHOWNA = 8;
var SW_RESTORE = 9;
var SW_SHOWDEFAULT = 10;
var SW_FORCEMINIMIZE = 11;

8.5.3. external.FileOpenSaveDialog

This method returns an object that implements a standard Windows File Open/Save dialog.

<script type="text/javascript">
var dialog = external.FileOpenSaveDialog();
</script>

Its properties and methods are:

Name Description
string defaultExtensionPropertySets a default extension for the dialog.
string extensionFilterPropertySets which extensions you want the dialog to show in its "Files of type" box. You need to use the pipe | to separate file type descriptions and extensions. This property must end in the double pipe.
Example: "XLS files|*.xls|All files|*.*||" .
int flagsPropertySets various flags determining behavior of the dialog. Each flag is a bitmask, you need to use a bitwise OR operator to combine flags. For the list of flags please see Constants for file and folder dialogs and ShellExecute.
Example: OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST
string initialDirPropertySets an initial directory for the dialog.
string initialFilePropertySets an initial file for the dialog.
bool open()MethodShows the dialog to open a file. Returns true if a filename is provided. Returns false if the user cancels the dialog.
int returnFileNameOffsetPropertyReturns an index from the beginning of the returnFullPath to the file name. You can use this property to get a file name only (without a path).
string returnFullPathPropertyReturns a full path of the selected file, or a directory path in case of multiple files selected
bool save()MethodShows the dialog to save a file. Returns true if a filename is provided. Returns false if the user cancels the dialog.
bool splitMultiSelect(array MultipleFileNames)Method If the dialog flags property specifies OFN_ALLOWMULTISELECT, then multiple files can be selected. In this case you need to use splitMultiSelect to get these multiple filenames. Parameter array MultipleFileNames will receive these multiple filenames if more than one file have been selected. In this case returnFullPath will receive the full path to the directory containing these files.
Returns true if more than one file have been selected. Returns false if the user selected just one file.
Please see an example below.
string titlePropertySets dialog title.

Example of using the dialog:

// Open a single file
function openFile()
{
    var dialog = external.FileOpenSaveDialog();
    dialog.flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
    dialog.extensionFilter = 'XLS files|*.xls|All files|*.*||';
    dialog.defaultExtension = 'xls';
    dialog.title = "Open XLS file";
    dialog.initialDir = "d:\\";
    if (dialog.open()) { 
        // Now the full path to the file returned is dialog.returnFullPath
        var filePath = dialog.returnFullPath;
        // We can extract just the file name as well
        var fileNameOnly = filePath.substr(dialog.returnFileNameOffset);
        alert(fileNameOnly); 
    } else {
        // User has cancelled the dialog
    }
}

// Open multipe files
function openMultiFiles()
{
    var dialog = external.FileOpenSaveDialog();
    dialog.flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
    dialog.extensionFilter = 'XLS files|*.xls|All files|*.*||';
    dialog.defaultExtension = 'xls';
    dialog.title = "Open multiple files";
    dialog.initialDir = "C:\\Documents and Settings";
    if (dialog.open()) { 
        // Initialize array "files" to receive those file names
        var files = [];
        if ( dialog.splitMultiSelect(files) ) {
            // User has selected multiple files:
            alert("Multiple files are selected:\n" + files.join("\n") + "\nIn this directory:\n" + dialog.returnFullPath);
        } else {
            // User has selected just one file:
            alert("A single file is selected:\n" + dialog.returnFullPath);
        }
    } else {
        // User has cancelled the dialog
    }
}

// Save a file
function saveFile()
{
    var dialog = external.FileOpenSaveDialog();
    dialog.flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_CREATEPROMPT;
    dialog.extensionFilter = 'XLS files|*.xls|All files|*.*||';
    dialog.defaultExtension = 'xls';
    dialog.title = "Save XLS file";
    dialog.initialDir = "d:\\";
    if (dialog.save()) { 
        // Now the full path to the file to be saved returned is dialog.returnFullPath
        alert(dialog.returnFullPath); 
    } else {
        // User has cancelled the dialog
    }
}

8.5.4. external.FolderOpenDialog

Returns an object that implements a standard Windows Folder Selection dialog.

<script type="text/javascript">
var dialog = external.FolderOpenDialog();
</script>

Its properties and methods are:

Name Description
int flagsPropertySets various flags determining behavior of the dialog. Each flag is a bitmask, you need to use a bitwise OR operator to combine flags. For the list of flags please see Constants for file and folder dialogs and ShellExecute.
Example: BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE
string initialDirPropertySets an initial directory for the dialog.
bool open()MethodShows the dialog. Returns true if the directory path is provided. Returns false if the user cancels the dialog.
string returnFullPathPropertyReturns a full path of the selected directory
string titlePropertySets dialog title.

Example of using the dialog:

function openFolder()
{
    var dialog = external.FolderOpenDialog();
    dialog.flags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
    dialog.title = "Select a folder";
    dialog.initialDir = "d:\\";
    if (dialog.open()) { 
        // Now the full path to the file returned is dialog.returnFullPath
        alert(dialog.returnFullPath);
    } else {
        // User has cancelled the dialog
    }
}

8.5.5. external.HWND

This property returns an integer Window handle of the main browser window.

    var hwnd = external.HWND;

8.5.6. external.ShellExecute

This method is a wrapper for the Windows ShellExecute() call.

bool external.ShellExecute(int hwnd, string operation, string file, string parameters, string directory, int showCmd)

Parameters:

NameDescription
int hwndA handle of the parent window
string operationOperation to perform. It can be "open", "print", "explore".
string fileFile or folder name to open or print
string parametersIf file is an executable file, then this variable is the parameters to be passed. In all other cases pass an empty string there.
string directoryDefault directory
int showCmdOne of the ShowWindow commands, see Constants for file and folder dialogs and ShellExecute

Example:

var file = "C:\\WINNT\\Media\\The Microsoft Sound.wav";
if (! external.ShellExecute(external.HWND, 'open', file, '', '', SW_SHOWNORMAL) ) {
    alert(file + ' failed');
}

8.5.7. external.argv

This method fills in an array of the parameters passed to the compiled application.

Syntax: external.argv(array parameters)
There is no return value from this method.
Example:

var parameters = [];
external.argv(parameters);
var argc = parameters.length;
alert("Number of parameters passed to my application is " + argc);
alert("They are:\n" + parameters.join("\n"));

8.5.8. external.locked

This boolean variable defines if external URLs are allowed in the built-in webbrowser. If it is set to false, then external URLs are allowed. External URLs are those URLs that are outside the built-in webserver.

8.6. Java

Java support in ZPE is limited. Java only works if you use it via the object (not applet) tag. You can find out how to convert the applet tag into the object tag here.

Java is not able to directly use a jar file compiled into the internal namespace. It must be placed into the directory where the compiled exe is located and specified via the "file:" protocol and an absolute path like in this PHP code:

$dir = $_SERVER['ZZEE_PHPEXE_SERVER_ROOT']; 
$dir = str_replace('\\', '/', $dir);
echo "<param name=\"archive\" value=\"file:///" . $dir . "/applet.jar\">";
	// Note three slashes after 'file:'
Or it must be on an external HTTP server and specified via the HTTP protocol:
<param name="archive" value="http://example.com/applet.jar">

The code below works for both ZZEE PHPExe and ZZEE PHP GUI:
<object classid="clsid:CAFEEFAC-0014-0002-0000-ABCDEFFEDCBA"
    width="305" height="200" align="baseline" 
    codebase="http://java.sun.com/products/plugin/autodl/jinstall-1_4_2-windows-i586.cab#Version=1,4,2,0"
    name="postlet" mayscript="mayscript">
    <param name="type" value="application/x-java-applet;version=1.4.2" />
    <param name="name" value="postlet" />
    <param name="code" value="Main.class">
    <?php
        $file = 'java/postlet.cvs.0.15.12-5-08.jar';
        $phpexe = strstr(strtolower($_SERVER['SERVER_SOFTWARE']), 'zzeephpexe') !== FALSE;
        if ($phpexe) {
            $javafile = @file_get_contents('zzee://' . $file);
            if ($javafile !== false) {
                @mkdir('java');
                @file_put_contents($file, $javafile);
            }
        }
        $dir = $_SERVER['ZZEE_PHPEXE_SERVER_ROOT']; 
        $dir = str_replace('\\', '/', $dir);
    ?>
    <param name="archive" value="file:///<?php echo $dir; ?><?php echo $file; ?>">
</object>

9. Webserver specifics

9.1. HTTP errors handling

You can handle HTTP errors like in Apache, go to Webserver > Error documents, and provide assignments which scripts or HTML files will be launched in case of errors. Also you can use error log, which you can define at Webserver > Logging.

9.2. Transfer-Encoding and Content-Encoding

Neither "Transfer-Encoding: chunked" nor "Content-Encoding: gzip" are supported. These encodings are useless in ZZEE PHPExe since there is no network traffic. Encoding and then decoding will just slow down the performance of the program. So please do not encode your output with these encodings (or with any other encoding).

10. Webbrowser specifics

10.1. Cookies

ZZEE PHPExe, starting from version 2.1, handles and combines both Internet Explorer's cookie storage and program's internal cookie storage. Thus PHP scripts can always set and get cookies regadless of whether cookies are allowed in user's Internet Explorer, in particular PHP Sessions work right out-of-the-box using the default php.ini settings. Your ability to set / get cookies in Javascript depends on whether cookies are allowed in user's Internet Explorer.

10.2. Kiosk mode

You can use Set kiosk mode settings and Set normal window settings menu items from the Project submenu to turn on or off kiosk mode for the webbrowser window. These menu items set the following settings:

WhatKioskNormal
Webbrowser > Window size > Window stateMaximizeNormal
Webbrowser > Window style > TopmostOnOff
Webbrowser > Window style > Window borderNoneThin

Note: a window in kiosk mode still handles special key shortcuts like Alt+F4 or Ctrl+Esc as usual. It means the window will close on Alt+F4. If a user uses only mouse, then they won't be able to close the window and it will stay on top of the screen.

11. Development information

11.1. Compilation, debug and release versions

Before you compile your project with the release settings, you need to compile it with the debug settings. Recommended debug / release options are shown in the table below. You can use Set debug settings and Set release settings menu items from the Project submenu to turn these settings on or off.

Note: php.ini's error_reporting setting is not covered by those menu items and you need to change it manually on the PHP page.

Note: before encoding Javascript files you need to make sure that they are correct. You can use jslint.com website for it.

WhatDebugRelease
Webserver > LoggingEnable both access and error logsDisable logging
File encodingTurn off encoding for all types of filesTurn on encoding for all types of files
Internal security filterDisableEnable
PHP > php.ini settingserror_reporting = E_ALLerror_reporting = 0
Webbrowser > General settingsTurn on "Show script errors"Turn off "Show script errors"

11.2. Securing your source code

Though a determined hacker can decode PHP files from the encoded namespace (this is true for any encoder, including the one in ZZEE PHPExe), it is not easy and requires skills, knowledge and time. However, if you check "Encode PHP files" all they can get is a list of opcodes, which further makes it harder to understand or re-use your code.

We highly recommend setting checkboxes to encode and compress PHP and Javascript files on the File encoding screen for the production version of your application.

Make sure user can not enter 'zzee://' paths so they can not read files from the compiled namespace. See this section for more information.

Note, users can drag&drop links from your compiled program to Windows desktop, or to Internet Explorer and thus get your URLs. However starting from version 2.5, there is an option to prevent dragging and dropping links to compiled applications.

The following project settings influence how secure your source code is:

WhatMay expose PHP sourceMay expose HTML and Javascript source
File encoding > Internal security filter [disabling]NoYes
Webbrowser > Popup windows > Compatible popup windows [enabling]NoYes
Webbrowser > General Settings > Allow context menu [enabling]NoYes

11.3. Deployment, making installer

The compiled program doesn't require any special installation on the end user's computer and can be launched from a CD or a flash drive. However if your project uses ActiveX controls, you may need to register them on the end user's system.

If you want to provide icons and links to your program in the Start menu or on the desktop, and compile it into a self-extracting exe, we recommend Inno Setup to make installers for your applications. It is feature-rich, easy to use and free.

11.4. Determining that you are running under ZZEE PHPExe

Since you can use the same code for both online and Windows applications, sometimes it is needed to determine at runtime whether your script is running under ZZEE PHPExe.

11.4.1. In PHP

You can use $_SERVER['SERVER_SOFTWARE'] PHP variable to find out:

$WindowsApplication = strstr(strtolower($_SERVER['SERVER_SOFTWARE']), 'zzeephpexe') !== FALSE;

In this example, $WindowsApplication will be true if your script is running under ZZEE PHPExe.

11.4.2. In Javascript

You can use navigator.userAgent variable to find out:

var WindowsApplication = (navigator.userAgent.toLowerCase().indexOf('zzeephpexe') >= 0);

In this example, WindowsApplication will be true if your script is running under ZZEE PHPExe.

11.5. Database

11.5.1. Optimal Choice

If you develop a database driven application, and only a single user works with your application (and with database data), then one of the best solutions will be using SQLite, since it doesn't need installation nor a separate database process, it is absolutely free and ships with PHPExe.

Of course, the program can also connect to other databases like PostgreSQL, MySQL, MSSQL, Oracle, etc. You need to uncomment their according PHP extension dlls in the php.ini . MySQL is covered below.

11.5.2. SQLite

To enable support for version 3 of SQLite, uncomment extension=php_pdo.dll and extension=php_pdo_sqlite.dll on the Php.ini page.

Here is an example that will create a new SQLite database (myNewDb.bin), create a table in it, and perform INSERT and SELECT queries:

header("Content-type: text/plain");
$sqlitedb = 'myNewDb.bin';
try {
    // If file myNewDb.bin doesn't exist, a new database will be created,
    // if it exists, the existing database will be used.
    $dbh = new PDO("sqlite:$sqlitedb");

    // Create a table there
    $q = "CREATE TABLE t (i int, s varchar)";
    $res = $dbh->prepare($q);
    if ($res === false) {
        $e = $dbh->errorInfo();
        die ("SQLite error: " . $e[2]);
    } else {
        $res->execute();
    }

    // Fill in some data
    $q = "INSERT INTO t (i, s) VALUES (1, 'Hi there')";
    $res = $dbh->prepare($q);
    if ($res === false) {
        $e = $dbh->errorInfo();
        die ("SQLite error: " . $e[2]);
    } else {
        $res->execute();
    }

    // Get the data
    $q = "SELECT i, s FROM t";
    $res = $dbh->prepare($q);
    if ($res === false) {
        $e = $dbh->errorInfo();
        die ("SQLite error: " . $e[2]);
    } else {
        $res->execute();
    }
    while ($arr = $res->fetch(PDO::FETCH_NUM)) {
        echo join(" | ", $arr) . "\n";
    }
    $res->closeCursor();

} catch (PDOException $e) {
    die ("SQLite error: " . $e->getMessage());
}

11.5.3. MySQL

MySQL is one of the most popular databases and often used in conjunction with PHP, so many people are interested in using MySQL with PHPExe. However there are things that need to be considered.

11.5.3.1. Drawbacks of using MySQL together with exe

First of all, MySQL is not free for redistribution, and if you want to distribute MySQL along with your exe, you need to have a license from Oracle (MySQL is now owned by Oracle). MySQL licensing policy for OEMs is described on their site.

The standard MySQL server on Windows uses TCP/IP, so it will involve additional TCP/IP communication between your exe and the MySQL server. This may lead to firewall blocks and warnings - as firewalls (like the built-in Windows firewall or ZoneAlarm) may block communication between your exe and a MySQL server.

Another problem is that MySQL server will require installation and configuration on user's computer. And you need to take into account its distribution size of tens of MBs. Problems may also occur if people need or use MySQL with another products.

So to avoid TCP/IP communication, installation and configuration problems you need to have an embedded MySQL implemented as a Windows dll. At the time of this writing there is no publicly available information on the MySQL website whether such dll exists and works with PHP for Windows.

11.5.3.2. Connection to MySQL

In your project you will need to go to the php.ini section and uncomment either extension=php_pdo.dll and extension=php_pdo_mysql.dll (if you use PDO) or extension=php_mysql.dll (if you use the old MySQL driver). You can connect to a MySQL server installed on the user's computer or a MySQL server installed elsewhere. Connection is performed the very same way as you do for online PHP applications. If you connect to a server installed on user's computer, you need to use server address as '127.0.0.1' and port 3306 (3306 is a standard MySQL TCP port number, but it can be changed for a particular MySQL installation). In all examples below port number is 3306, database name is "test", username is "username", password is "password". If you use PDO, then connection is done like this:
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$dbh = new PDO($dsn, 'username', 'password');
// do something with this $dbh connection
If you use the old PHP mysql extension, then it is like this:
$dbh = mysql_connect('127.0.0.1', 'username', 'password');
if (!$dbh) {
	// exit with error
}
if (!mysql_select_db('test', $dbh)) {
	// exit with error
}
// do something with this $dbh connection

11.5.3.3. FSQL with FSQL-MySQL - a drop-in alternative to MySQL

There is a PHP library called FSQL, which can do various SQL queries in a way similar to MySQL, with basically the same SQL syntax but which uses just flat files, and requires no separate installation or configuration. FSQL is suitable for small databases. We also have developed a bridge FSQL-MySQL library, which can be an easy drop-in replacement for MySQL, implementing MySQL functions. This means that if you don't use any exotic MySQL features and use the old MySQL extension (that is, you use functions like mysql_query(), etc), then you will have to change just one line in your PHP code, a call to mysql_select_db(), as you will need to provide a path to a directory where your database will be stored instead of a MySQL database name. In fact, you still use things like mysql_connect() or mysql_query(), but this will invoke FSQL and not MySQL and will continue working the same way.

12. References