3.3. Installing the library

When you have verified the necessary preconditions as described in the previous paragraphs it is time to install the library. The "installing" part is nothing more than copying the files in the distribution to a place in the directory structure where you script can find the library files. On a Unix system it is common to install PHP libraries under "/usr/share/php/". On a windows system there is really no standard path for installing PHP libraries so you have to decide your self.

The important thing here is that the path to the library is included in the PHP search path, i.e. it is in one of the paths that PHP searches when it tries to resolve a "require_once" or "include" statements. Furthermore, the included examples and demo applications (included in the pro version) assumes that the library is installed under the directory "jpgraph/".

As an example the following commands will install a specific version of the library on a Unix server. If we assume that you have downloaded the library to the "/tmp/" directory and are already standing in this directory the following commands will setup the library to be used

root:/tmp> tar xzf jpgraph-2.5.tar.gz
root:/tmp> cp -r jpgraph-2.5 /usr/shar/php/
root:/tmp> ln -s /usr/shar/php/jpgraph-2.5 /usr/shar/php/jpgraph

The last line makes a symbolic link from "jpgraph" to the actual version of the library. This way you can try out different versions of the library without having to make any changes in your scripts. You just point out a different version of the library in the symbolic link.

3.3.1. Configuring JpGraph/PHP on a development server

Setting up your php.ini file

Tip

To find the location of your php.ini file create and run a script with the single line <?php phpinfo(); ?> . The look at the output for a line saying "php.ini file used" and you will see which php.ini file is used.

Setting the memory limits

In many default configuration the allowed memory for PHP is not enough for complex graph script since they (as many other image manipulation programs) can require a lot of memory. On a development server there should be at least 32MB memory allowed for the HTTP/PHP process. To verify this do the following

  1. Open php.ini for editing.

  2. Locate the line saying

    memory_limit = xx

    where "xx" is some number. Now make sure that you have at least 32MB allowed by making sure the line reads

    memory_limit = 32M

    Note that fore very large images this might not be enough. Consider the following example.

    Assume you need to create an 1200x1024 image in true color. Just the plain image in itself will require 1200x1020x4 bytes, which is roughly 4.7MB RAM during internal processing the library can need up to three times that amount of memory so this means that just for the image the library needs around of ~15MB of RAM. If we then take the memory needed to load PHP as well as the entire JpGraph library and dynamically execute and parse the library it can easily consume another ~15MB RAM. If the image is very complex and requires a huge number of objects to be created (a typical example is a large Gantt chart) it might be necessary to double the allowed memory to 64MB RAM.

Setting maximum allowed run time

By default many installations have very short maximum run time for the PHP scripts. Common figures are 10s. For normal interactive use involving plain text processing this is usually adequate. However, producing large and complex images might take considerable time (as do all images processing). For this reason the maximum time limit for PHP should be increased to a minimum of 20s (depending on the complexity of your images as well as any associated data processing it might be necessary to allow up to 30-40s).

The allowed running time is controlled by the php.ini setting

max_execution_time = xx

where "xx" is some number. Recommended setting is therefore

max_execution_time = 30

Disabling output buffer

The next part of the php.ini file that might need changing is the output buffer. In short this should be disabled and we will shortly explain why. To check this do the following

  1. Open php.ini for editing

  2. Locate the line saying

    output_buffering = xx

    where "xx" is some number. Make sure that this line is commented out, i.e. it reads

    ; output_buffering = xx

This reason we want this to be commented out is that during development we want to be able to see the potential error messages produced by the library and having the output buffering enabled will actually prevent this. Fully understanding why this is the case is good first step into the added complexity of producing images with PHP compared with just outputting text. Understanding this requires us to understand a few basic principles about the HTTP protocol. Especially how MIME encodings of data works.

The following explanation is slightly simplified since a full description of the HTTP protocol would bring us a bit to far in this manual

  1. A client (e.g. browser) requests data from the server by issuing a GET (or possible a POST) command to the server. This is what happens when you enter a URI i the address bar in the browser.

  2. The server replies with a data stream (or an error if the requested data wasn't available). This data stream is prepended with header (MIME header) that tells the client (e.g. the browser) how to interpret the data that follows. The most common type (and the default type if no header is sent by a faulty server) is "text/html" . This tells the client to interpret the data as plain text with embedded HTML encoding.

    When the data is to be interpreted as an image the header will instead be one of the image headers, for example "image/png" or "image/jpeg". When the client receives this header it will Interpret all the following data as an image encoded in the indicated format.

    The important thing to keep in mind here is that each server reply can have one and only one MIME type. This is the key to further understanding the specific issues with dynamic image generation. This explains why if a PHP script running on the server sends a header first indicating that the following data it sends should be interpreted by the client as an image it cannot send both image data and some text.

We are now in a position to explain how output buffering would make debugging more difficult.

Normally all output from a PHP script is sequentially, i.e. the header must first be sent and then the data. If no header is sent or plain text is sent without a header the client will interpret this as "text/html". One purpose with "output_buffer" it to circumvent this to allow a certain amount of output to be put in a buffer for a while and later when some processing has determined what header should be sent the data is prepended with the correct header and the rest of the data is then sent.

What could now happen is the following (not unlikely scenario):

  1. The scripts starts executing and the image starts to be build.

  2. Your script has some minor issues which produces some warnings from PHP. These warning does not get sent directly back to the client (the browser) to allow you to act on these warnings instead they will be put into the output buffer. When later the scripts starts outputting the proper image header and the image data it gets added to the output buffer where your previous textual PHP warning already are stored.

  3. Your client now receives the header that indicates that the following data should be interpreted as an image but since that image data is mixed with the textual warning messages it will fail to decode the data (since it is not proper image data) and will typical just show the image as a square with a red-cross (FireFox) or some message along the lines of "Cannot decode image". This is all depending on how a certain client handles a corrupt image.

The above scenario makes it impossible to debug your script since it will give no clue to what caused or where in your script these warnings were generated. The way to counteract this scenario is to disable output buffering. In this way the warning will be sent back to the client as soon as they are generated by PHP and will allow you to act on them.

Enabling adequate error checking

The final part of the php.ini file that should be adjusted (and this is not only for the JpGraph library) is the error level. To ensure maximum interoperability of the developed scripts they should all run completely silent no matter what error levels are set on the server. This means that development of all scripts should always be done with maximum error checking enabled. The JpGraph library can safely run completely silent even when all error checking is enabled.

The error checking should therefore be specified as

error_reporting = E_ALL | E_STRICT

to enable the highest degree of PHP error checking

Tip

In addition to the above setting it is a good idea to also to makes sure that the following options are set

zend.ze1_compatibility_mode = Off

Zend engine 1 compatibility might cause problems with the library

implicit_flush = On

This can reduce the performance and shouldn't be used on a production server but will make all outputs sent back to the client as soon as possible and will aid in debugging.

allow_call_time_pass_reference = Off

This is just a general good idea since call time pass references is deprecated in PHP 5.0 and higher

display_errors = On

This makes sure all error are displayed

display_startup_errors = On

This makes sure that any initial errors thrown by PHP will be reported

Setting default timezone

Starting with PHP 5.2 a warning will now be generated unless a default time zone is explicitly specified in php.ini. To set this find the line date.timezone in the [Date]section and set this to valid zone. For example to specify GMT+1 one could specify

date.timezone = Europe/Paris

Note: There should be no citation signs around the time zone.

Caution

In order to use the LED module (See Section 17.1) the PHP installation must have multi-byte strings enabled so that the function mb_strlen() is available. This is normally enabled at compile time for PHP by specifying the options --enable-mbstring --enable-mbregex when configuring the compile options.

Caution

In order to use the PDF417 barcode module (See Chapter 25) it is necessary for the PHP installation to support the function bcmod(). This is enabled when compiling PHP by making sure that the option --enable-bcmath is given when configuring PHP at compile time.

Setting up your jpg-config.inc.php

Apart from the standard configuration described in Section 3.4 and Section 3.5 there is only one important configuration that is specific for a development server and that is the localization setting for error messages.

As of version 3.0.0 there are three localization options

  1. English error messages ("en")

  2. German error messages ("de")

  3. Production error messages ("prod"). This is not really a localization but a different set of error messages which does not give detailed error messages but a generic message suitable for a production server where the end user is not helped by detailed graph script errors. Instead a generic message is shown together with an error code that corresponds to the detailed error. ("prod")

In order to specify the error message localization the following define in jpg-config.inc.php must be set the

define('DEFAULT_ERR_LOCALE','en');

The possible options are

  1. "en", English locale

  2. "de", German locale

  3. "prod", The production version of the error messages.

Tip

In addition to specifying the locale in the jpg-config.inc.php file it can also be specified dynamically in each script by calling

1
JpGraphError::SetErrLocale($aLocale);

3.3.2. Configuring JpGraph/PHP on a production server

Setting up your php.ini file

Apart from what is applicable to a development server as described in Section 3.3.1 the following changes should be considered in a production environment.

Setting the memory limits

The one thing to keep in mind here is that each active connection will spawn a unique PHP instance (HTTP process). This means that the memory limit set per PHP process can cause a very high memory demand on a busy server with many simultaneous connections. For this reason it is important that during system test (before going into production) the actual needed memory limit is determined.

For a busy server it is not uncommon to dimension it so it can handle 100 simultaneous connections. If the limit of r each PHP process is set to 32MB this means that the server needs at least ~3.2GB memory just to handle the PHP processes (if they are all using there maximum allowed memory).

Setting maximum allowed run time

The same principle applies to a the allowed run time. For a production server with high load and many simultaneous users it might be necessary to increase the maximum allowed execution time just to be sure no process is terminated due to it reaching its maximum allowed run time. When that happens the PHP process will be killed an no output sent back to the client (e.g. the browser).

Disabling output buffer

The output buffer should be disabled on the production server as well since enabling this will slow down the PHP and put a higher demand on the memory requirements.

Enabling adequate error checking

On a production server it is not a good idea to display all PHP error messages to the end user so the display of error messages should be disabled and the error messages should only be logged to a file.

Tip

On a production server it is also a good to idea to have the following settings:

display_errors = Off

This makes sure that now PHP errors are displayed

display_startup_errors = Off

This makes sure that any initial errors thrown by PHP is not displayed to the end user

log_errors = On

error_log = <name-of-log-file>

This makes sure all server PHP errors are logged to a specified file

Setting up your jpg-config.inc.php

On a production server it is best not to show detailed error messages to an end user. Instead it is better to have a generic error message that indicates a server problem and give an error code which can be decoded by looking it up in the table in Appendix H Using a generic error message is achieved by setting the following define:

define('DEFAULT_ERR_LOCALE','prod');

3.3.3. Adjusting PHP include path

As was mentioned before the library should be installed somewhere in the PHP include path. There are two ways of configuring the include path:

  1. setting the include path in php.ini

    include_path = <file-path>

  2. adjusting the include path directly in the code by using the PHP command php_ini_set() at the top of the script

The library examples assume that the library is available under a directory called "jpgraph/" . This will allow the scripts to include the library files by, for example, writing "include" or "require_once" statements such as

require_once( 'jpgraph/jpgraph.php')

3.3.4. Using Apache2 alias configuration during development

Note

This section only discusses alias setting using the Apache HTTP server so this section can be skipped at first time reading the manual without loss of continuation.

Note

More detailed information on the alias directive is also available in the official Apache documentation at http://httpd.apache.org/docs/2.2/mod/mod_alias.html

When accessing examples and test code through a regular bowser during development the scripts must be available in document root (or somewhere beneath that root) the root is traditionally named /htdocs. Having a development code/repository directly under this root directory is not a good idea. For example, write access to a document root (even on a development server) should be restricted, in addition the paths given to a test team should be the same whatever version is currently under test so storing different versions with different names under the root is also a poor setup.

A much better and easy, approach is to use the powerful concept of alias in Apache. This is a way of mapping a URL to a specific directory on the server. For example if Eclipse-PDT is used as an IDE to develop PHP it is mandatory to have a workspace setup where all the working files resides. Assuming that a local developer has his workspace directly in his home directory, say ~joe/workspace, we could configure an alias so that the workspace is accessible by the alias "http://localhost/ws/" by adding the following configuration in the Apache setup file(s)

1
2
3
4
5
Alias /ws /home/joe/worksapce
<Directory /home/joe/workspace>
  Order allow,deny
  Allow from all
</Directory>

In this particular setup we use very liberal settings, allowing basically everyone with server access to access the directory. Using this approach makes it very easy to use the same test setup but allow testing of different branches/versions of the code.

Depending on the system these configurations can reside in different places. However, a very common structure is to keep all these small configuration files under /etc/apache/conf.d/ The main Apache configuration then reads all the files (regardless of there name) that are stored under this directory.

As a final example we show a further slightly more complex example (which actually shows how most of our developers have there systems setup for PHP5 development). This example adds options to do directory listing and allows the server to follow symbolic links. More information on available argument for the directive option is available in the official Apache documentation http://httpd.apache.org/docs/2.2/mod/core.html#options

Example 3.5. Alias configuration for a development server running Apache with Eclipse-PDT

1
2
3
4
5
6
7
8
9
10
# Configuration for Eclipse workspace
#
<IfModule mod_php5.c>
      Alias /ws/ /home/joe/workspace/
      <Directory /home/joe/workspace/>
             Options         +Indexes +Multiviews +FollowSymLinks
             order allow,deny
             allow from all
      </Directory>
</IfModule>