2017-08-08
Copyright © 2001-2017, by The PEAR Documentation Group
PEAR is the PHP Extension and Application Repository.
Copyright © 2001 - 2017 by the PEAR Documentation Group. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/).
Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder.
Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.
The PEAR Documentation Group consists of all the people that have contributed documentation to the PEAR manual. Representatives are listed on the front page of this manual. In case you would like to contact the group, please write to pear-doc@lists.php.net.
The documentation of the XML_RPC package has originally been written by Edd Dumbill as an independent document on his homepage and is published as part of the PEAR Manual under the following license restrictions:
Copyright © 1999,2000,2001 by Edd Dumbill, Useful Information Company
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the "XML-RPC for PHP" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This manual is written in XML using a slightly enhanced version of the DocBook 5 XML DTD, using PHP's very own DocBook rendering system PhD.
This guide explains the general structure, layout, and conventions used in the PEAR manual. The manual is divided into six main parts, they are:
This section contains a gentle introduction to what PEAR is and has to offer. Included is general information about installing and using PEAR, support options, and answers to frequently asked PEAR related questions.
General information about developing and releasing PEAR-Packages.
This section contains documentation about the core PEAR classes. These core components are the base to every PEAR class and understanding the basics will allow general use of the PEAR library. Topics include the PEAR base, PEAR administration, error handling through use of the PEAR_Error object, and system commands. These classes are generally shipped with every source PHP distribution.
There are many PEAR packages with the number growing every day. Most every package is documented and most are not installed by default. For information on installing PEAR classes on your system, be sure to read the Installation chapter.
Each documented class contains basic documentation about the packages publically available functions. There may also be additional information like an introduction, list of available constants, and example uses of the given package. Additional information may also exist within the package source files themselves.
The description of each function may contain some or all of the following parts:
Synopsis
Shows the structure and prototype of the function.
Description
Gives a description about what the function does.
Parameter
Describes each parameter for the function along with the parameter types and names. Both required and optional parameters are listed.
Returns
Describes the returned value, if the function doesn't fail.
Throws
Describes the returned PEAR_Error objects, if the function fails.
Note
Additional notes and information about the function. An example note would be if the function can be called statically.
See
Links to other related functions or manual entries.
Example
An example use of the function or class.
Contains information for writing your own PEAR Packages.
The following is a list of people that are helping to maintain this documentation. If you would like to contact one of them, please write to pear-doc@lists.php.net.
Lorenzo Alberton
Gregory Beaver
David Coallier
Daniel Convissor
David Costa
Thomas V.V. Cox
Michael Gauthier
Christophe Gesché
Ken Guest
Martin Jansen
Alan Knowles
Clay Loveless
Alexander Merz
Stefan Neufeind
Jon Parise
Tobias Schlitt
Stephan Schmidt
Mika Tuupola
Michael Wallner
Christian Weiske
Mark Wiesemann
(In alphabetic order.)
2009-06-26
This chapter describes basic concepts that are important for all users of Pyrus, the PEAR Installer, or PEAR packages to understand.
The smallest unit that can be managed by Pyrus or the PEAR Installer is
a package
. A package is a collection of files
that are organized and defined by a meta-information file called
package.xml.
A package also contains meta-information about the collected files, such as the name of the package, the channel that the package is from, the version of the package, information on the developers who created the package, and any external dependencies the package has on other packages or installation requirements (such as minimum PHP version).
Packages can exist as a collection of files on disk, or can be placed into an archive in phar, tar, or zip format and then later installed on another system.
A package's API is the publicly documented entry points to the library or program within the package. For example, the classes or methods that are to be used with a PEAR package, or the commands for a command-line program are all elements of an API. Another example might be the templating language.
Any element of the program that is not intended to be used by the outside world, or is not documented, is not considered part of the API, and thus should not be relied upon in your programs as it may change in any future release.
Versioning and stability are separate entities for packages. The stability of a package determines how likely the package is to contain bugs or to have changes to its API. A distinction is made between the version/stability of the API and the version/stability of the code.
This simple chart shows the meaning of a package's stability:
Stability type | Stability | Description |
---|---|---|
Release | devel |
The package is under development and will change dramatically, both adding new features, changing the design, and fixing many bugs. It may not function at all and be more of a proof-of-concept. There may be little to no documentation or unit tests. Use at your own risk. |
Release | alpha |
The package is ready for testing by hard core users. Features are still being developed, but the program should work. Subsequent releases may have major changes. |
Release | beta |
The package is nearly ready for public release and usage in production. Documentation is complete, unit tests are complete, and the API is frozen (will not change) unless major problems are found. |
Release | stable |
The package is ready for use in production. Documentation is complete, unit tests are complete, and the API is frozen completely and will not change. |
API | devel |
The API will change dramatically between releases, and cannot be relied upon. |
API | alpha |
The API is starting to stabilize, but may still have major changes. |
API | beta |
The API will only have changes if major bugs are found. |
API | stable |
The API will not change and can be relied upon. |
There are a few conventions that should be followed when deciding which stability to use for your package. In general, the API stability should be equal to or better than the release stability.
Here is a helpful chart of stabilities:
Release Stability | API Stability | Scenario |
---|---|---|
devel |
devel |
This package is brand new, no documentation, no tests, more of a proof-of-concept. It may not work at all. |
alpha |
alpha |
This package has been developed extensively, and is ready for testing by the outside world, has some documentation or full documentation and tests, but the API or design is subject to dramatic change if necessary. Bugs are likely |
alpha |
beta |
This package has a relatively certain API, but may contain bugs, and the API may change, but most changes will be small. |
beta |
beta |
This package is approaching release, the code is stabilizing as well as the API |
beta |
stable |
This package is in the release candidate stage, has full documentation and tests, as well as a frozen API. Bugs may still be present in the code. |
stable |
stable |
This package is ready for use in production, has full documentation and tests, and the API can be relied upon as frozen. |
Versioning and stability are separate entities for
packages.
The version of a package is a numeric string like 1.2.3
that
is incremented every time a new version of the package is released.
The stability of a package determines how likely the package is to contain bugs
or to have changes to its API.
In addition, PEAR makes a distinction between the API version and the package version.
There are a few conventions that should be followed when deciding which version
number or stability to use for your package. Version numbers should always
contain three decimals such as 1.2.3
. This is because of
the way that PHP's version_compare() function calculates
the difference between versions:
<?php
var_dump(version_compare("1.0", "1.0.0")); // int(-1)
?>
The example above shows that in fact version 1.0
is
considered to be a different version from version 1.0.0
,
a distinction that is confusing at best for users. Use of 3 decimals for
every version will ensure that both your users and PHP will not be confused
by which of two versions is the same or newer.
Package versions can be abstractly referred to as X.Y.Z
.
For version 1.2.3
, X
is 1
,
Y
is 2
and Z
is
3
.
Generally speaking, it is best if X
is used to refer to
major API changes, sweeping addition of new features, or any break of
backwards compatibility. The Y
component should be reserved
for small to large feature additions, but should never be used for
breaks of backwards compatibility. The Z
component should
only be used for bugfixes.
These three questions can be used to determine how to increase the version number:
X
,
set Y
= Z
= 0
Y
, set
Z
= 0
Z
Here is a typical life cycle for a package:
Release Notes | Release Stability | API Stability | Release Version | API Version |
---|---|---|---|---|
Initial release | devel or alpha |
devel or alpha |
0.1.0 |
0.1.0 |
Bugs fixed | devel or alpha |
devel or alpha |
0.1.1 |
0.1.0 |
More bugs fixed | devel or alpha |
devel or alpha |
0.1.2 |
0.1.0 |
API changed, more bugs fixed | devel or alpha |
devel or alpha |
0.2.0 |
0.2.0 |
API changed, more bugs fixed | devel or alpha |
devel or alpha |
0.3.0 |
0.3.0 |
API changed, documentation started, tests expanding | alpha |
alpha |
0.4.0 |
0.4.0 |
API stabilizing, documentation nearly finished, tests expanding | alpha |
beta |
0.5.0 |
0.4.1 |
API stabilizing, code stabilizing, documentation nearly finished, tests expanding | beta |
beta |
0.5.1 |
0.4.1 |
API problem fixed, code stabilizing, documentation nearly finished, tests expanding | beta |
beta |
0.6.0 |
0.5.0 |
API stabilized, code stabilizing, documentation nearly finished, tests expanding | beta |
stable |
0.6.1 |
1.0.0 |
API stabilized, code stabilizing, documentation finished, tests expanding | beta |
stable |
1.0.0RC1 |
1.0.0 |
API stabilized, code stabilizing, documentation finished, tests full coverage | beta |
stable |
1.0.0RC2 |
1.0.0 |
code ready for use in production | stable |
stable |
1.0.0 |
1.0.0 |
bugs fixed | stable |
stable |
1.0.1 |
1.0.0 |
bugs fixed | stable |
stable |
1.0.2 |
1.0.0 |
new features added, bugs fixed | stable |
stable |
1.1.0 |
1.1.0 |
bugs fixed, package enters maintenance mode to develop next generation | stable |
stable |
1.1.1 |
1.1.0 |
Note that the PEAR coding standards require packages to be renamed when they
break backwards compatibility. Thus, a PEAR package can never reach
version 2.0.0
.
PEAR and PEAR2 calls its developers maintainers
, and
classifies maintainers by their level of contribution to a package, or
their role
. In addition, each maintainer must choose a
handle which can be used to refer
to them in package.xml and in bug reports. A handle is an alphanumeric
word with all lower-cased letters such as cellog
or tony2001
. If you are developing for pear.php.net,
pear2.php.net or pecl.php.net, this handle is the account name you use
to log into the website.
Developer roles are lead
, developer
,
contributor
and helper
. Only lead
maintainers have the permission to release a package. Both lead and
developer maintainers may create and modify package roadmaps, and
all developers can directly log into pear.php.net and change the status of
bugs for the packages that they maintain.
For channels outside of pear.php.net, pecl.php.net, and pear2.php.net, these handles are still required, and have no special meaning beyond what the channel defines them to be. However, both the Pyrus simple channel server and other external channel server implementations require the lead developer of a package to perform releases.
There are several different ways of referring to a package that Pyrus and the
PEAR Installer understand. Three ways are concrete, in that they refer to
deterministic entities such as a package.xml file on disk, or a URI
(Uniform Resource Identifier) such as http://pear.php.ne/get/PEAR-1.8.1.tgz
that refers to a file on a remote server.
Package type | Local/Remote | Example |
---|---|---|
package.xml file |
Local | php pyrus.phar install /path/to/package.xml |
Package release archive | Local | php pyrus.phar install /path/to/PackageName-1.2.3.tgz |
Static url | Remote | php pyrus.phar install http://example.com/PackageName-1.2.3.tgz |
Abstract package | Remote | php pyrus.phar install PackageName |
The first three ways of specifying a package are concrete: the package name
always refers to one and only one package. Abstract packages are more
flexible, and there are several ways of requesting a package. Note that
if the channel is not explicitly requested (as it is in the last example),
Pyrus or the PEAR Installer prepends the default channel. The default channel
is set by the default_channel
configuration variable, and is
set to one of pear.php.net
(pear command), pecl.php.net
(pecl command), or pear2.php.net
(Pyrus) unless explicitly changed
by a call to the config-set
(pear/pecl command) or set (pyrus)
command. A request for
PackageName
will be interpreted by the PEAR Installer or Pyrus
to be a request for package PackageName from the default channel, as if the user had
requested pear.php.net/PackageName
(pear command),
pecl.php.net/PackageName
(pecl command) or
pear2.php.net/PackageName
(pyrus).
Example | Description |
---|---|
PackageName-1.2.3 |
This forces Pyrus to download version 1.2.3 of
package PackageName , but will will attempt to download
a release in one of the following file formats, in this order:
|
PackageName-alpha |
This causes Pyrus to download the latest version of
package PackageName that is alpha
stability or better (the hierarchy of stabilities is devel ,
alpha , beta , stable ).
After finding a matching version, it will attempt to download a release
in one of the following file formats, in this order:
|
PackageName |
This causes Pyrus to download the latest version of
package PackageName that is preferred_state
(configuration variable)
stability or better (the hierarchy of stabilities is devel ,
alpha , beta , stable ).
After finding a matching version, it will attempt to download a release
in one of the following file formats, in this order:
preferred_state configuration variable can be seen
with the config-show
command.
|
PackageName#groupname |
This causes Pyrus to download and install a release of Pyrus as specified
above, and it also causes Pyrus to download and install the contents
of the groupname dependency group (dependency groups
are documented here).
Note that PackageName in PackageName#groupname
can be any of the above examples, such as PackageName-1.2.3#groupname .
|
channelname/PackageName |
This causes Pyrus to download and install a release of
PackageName from the channel channelname .
Another syntax that can be used is channel://channelname/PackageName
which is useful if there is a sub-directory of the current working
directory named channelname and a file or directory
within it named PackageName . Note that
in the example above, PackageName can be any of the
previous syntaces such as channelname/PackageName-1.2.3#groupname .
|
Note that the uninstall
, run-scripts
,
and other commands that operate on installed packages
only support a simple
package name as in PackageName
or
channelname/PackageName
. Any fancy stuff like
pear2.php.net/PackageName-1.2.3#group
is ignored.
A PEAR Channel is a web site that distributes package archives for remote installation by users of Pyrus or the PEAR Installer. In addition to providing the package archives for download, a PEAR Channel must also provide some meta-information that the installer can use to locate the package releases and determine which is the best release to download.
Channels have a file that defines the capabilities of the channel named channel.xml located in its document root (for instance, pear.php.net's channel.xml) and some meta-information in REST format.
A channel can also provide a public frontend for users to browse the contents of the channel online, such as PEAR's public frontend.
Pyrus and The PEAR Installer categorize file types by their role
.
A file role is equivalent to the web's concept of MIME type
,
a concept that allows web browsers to determine how a file should be displayed
or processed. A file role allows Pyrus and the PEAR Installer to determine
where a file should be installed, the conditions under which the role can be
used, and even whether the file should be installed at all. A file may only
have one role in a package.
Generally speaking, each file role has its own installation location. For
example, php
files (files whose file role is
php
) are installed into the location specified by the
php_dir
configuration variable, data
files (files whose file role is data
) are installed into
the location specified by the data_dir
configuration
variable. Some file roles do not have a direct mapping of role name
to configuration variable, such as Pyrus's customcommand
file role. This file role is installed into the location specified by the
data_dir
configuration variable.
File roles also control how package.xml attributes are handled. The
php
file role installs files into the exact
relative path as specified in package.xml. The data
file role always installs files into a subdirectory containing the package name
for PEAR packages, and both the channel and package name for packages designed
to be installed by Pyrus.
Here is an example of the same file path in package.mxl as php
role and as data
role. All examples assume this is a
package named PackageName
in the pear2.php.net
channel.
<contents> <dir name="\"> <dir name="base"> <file name="foo" role="php"> </dir> </dir> </contents>
This installs as base/foo
in the location specified
by php_dir
.
<contents> <dir name="\"> <dir name="base"> <file name="foo" role="data"> </dir> </dir> </contents>
For PEAR Installer packages, this installs as PackageName/base/foo
in the location specified by php_dir
. For Pyrus packages,
this installs as pear2.php.net/PackageName/base/foo
.
The baseinstalldir
(base installation directory) attribute
is a tool that can be used to install a file into a different directory than
its location in the source repository.
As an example, the path in the subversion repository to the file
PEAR2\Foo.php
is at
Foo.php
. To inform the installer to install this
package into the PEAR2
directory, we would use a baseinstalldir
attribute:
<contents> <dir name="\"> <file name="Foo.php" role="php" baseinstalldir="PEAR2"> </dir> </contents>
The attribute can also be used on <dir>
tags to apply
the base installation directory to all files within the directory:
<contents> <dir name="\" baseinstalldir="PEAR2"> <file name="Foo.php" role="php"> </dir> </contents>
The baseinstalldir
role can also be used to inform
the PEAR Installer or Pyrus to strip all relative paths by using
/
as the base installation directory. Here is an
example from the PEAR package:
<dir name="scripts" baseinstalldir="/"> <file name="pear.bat" role="script"/>
This file would be installed as scripts/pear.bat
, but
the baseinstalldir
attribute of /
informs the installer to instead install it to pear.bat
.
Each file role reacts differently to the baseinstalldir
attribute. Packages designed to be installed by the PEAR Installer also
handle them differently from packages designed for installation by Pyrus.
The php
, script
and www
file roles react the
same way as documented above. In packages designed for the PEAR Installer,
the other file roles do not honor the
baseinstalldir
attribute, meaning they ignore it. For
example:
<contents> <dir name="\" baseinstalldir="PEAR2"> <file name="Foo.dat" role="data"> </dir> </contents>
installs Foo.dat
into the PackageName/Foo.dat
directory. The same XML in a package designed for installation by Pyrus
will install the file into pear2.php.net/PackageName/PEAR2/Foo.dat
.
Pyrus allows special handling of files through tasks. File tasks can perform
any action necessary both when installing a package and when creating a
package. Both Pyrus and the PEAR Installer ship with 4 built-in tasks,
replace
, windowseol
,
unixeol
, and postinstallscript
.
The built-in tasks are documented here. The documentation also describes how to create custom tasks for the PEAR Installer. Custom tasks for Pyrus are documented here.
Tasks are specifically designed to allow customization of an installation, and
particularly modification of a specific file's contents. The
unixeol
task, for instance, transforms line endings to
UNIX \n
and is useful for shell scripts that must have
the proper line endings. The replace
task can be used
to update the version of a package directly in the source code, or to
automatically set up the path to a PEAR Installation in a shell script.
Tasks are defined using the XML namespace
http://pear.php.net/dtd/tasks-1.0
. Most often, this
is declared using tasks
as the namespace prefix, as in
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
.
More than one task can be used for a single file, as shown by this example from
the PEAR package:
<file name="pearcmd.php" role="php"> <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" /> <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" /> <tasks:replace from="@pear_version@" to="version" type="package-info" /> <tasks:replace from="@include_path@" to="php_dir" type="pear-config" /> </file>
This chapter describes how to install Pyrus, the package manager for PEAR2 and PHP 5.3+.
Installing Pyrus is much simpler than installing PEAR. Installing PEAR requires downloading a separate installation and installing PEAR before you can use it to install any packages.
To install pyrus, you must follow two simple steps:
That's it, pyrus has no external dependencies. Using Pyrus is also simple, one simply passes it to the command-line interface (CLI) like so:
php pyrus.phar install packagename
The first time pyrus is run on your system, it will ask where you would like to install packages. After this, one can simply use it.
To match PEAR, it is also possible to create a convenience script for accessing pyrus. Here is a sample script for unix systems:
#!/bin/bash /usr/local/bin/php -dphar.readonly=0 /home/username/pyrus.phar $*
If your system has open_basedir enabled, the script should instead look like:
#!/bin/bash /usr/local/bin/php -dphar.readonly=0 -dopen_basedir= -dsafe_mode= /home/username/pyrus.phar $*
Windows users can create a batch file similar to the unix script named pyrus.bat:
@ECHO OFF C:\php5\php -dphar.readonly=0 -dopen_basedir= -dsafe_mode= C:\php5\pyrus.phar %*
Place the shell script (on unix) or batch file (on windows) in your path, and then you can run commands like so:
pyrus install packagename
2006-11-25
This chapter describes how to install the PEAR package manager.
Before you install PEAR, we recommend you understand what PEAR is and why you should install it.
The base installation that comes with the PHP distribution
contains all the stuff that is needed to run the PEAR installation tools etc.
If you have a recent installation of PHP, you can relax: The PEAR base
installation is already there - unless you have compiled your PHP
with the ./configure
flag
--without-pear
.
The packages that do not come with PHP can be installed with the PEAR package manager. The manager handles installation similar to Debian's "apt-get" package management utility, as well as others such as "yum". Again: If you are running a recent version of PHP (> 4.3.0) you can skip the next section. But if you are running PHP 4.2.* or earlier, you need to manually install the manager.
Apart from installing packages, the PEAR package manager also handles some other tasks: It can create new packages on your machine, manage a registry of installed packages, check dependencies and it can interact with services on pear.php.net, and other PEAR compatible channel servers to get information about available packages.
After you have downloaded and installed PHP, you have to manually
execute the batch file located in e.g.
c:\php\go-pear.bat
.
Alternatively, download
https://pear.php.net/go-pear.phar
with your browser and save the output to a local
file named go-pear.phar
. You can then run
php go-pear.phar
in a Windows Command Prompt to start the installation.
The setup will ask you some questions and afterwards the PEAR Package Manager will be installed in the path, which you have specified during installation.
Finally you have to add that installation path to your
PATH
environment. Either do this manually (Start >
Control Panel > System > Environment) or run (double-click) the
newly generated PEAR_ENV.reg
that's now found in the
PHP source directory.
After that you can access the PEAR Package Manager
by running the command pear
in a Windows Command
Prompt.
After changing
php.ini
, you will need to restart your web server.
When using PHP, the PEAR Package Manager is already
installed unless one has used the ./configure
option --without-pear
.
If one uses a version of PHP that is supplied by Unix/Linux/BSD distributors it may be necessary to manually install PEAR. Users should consult the documentation for the respective distribution in this case.
If you want to re-install the Package Manager, you can use the following provisional way:
$ wget http://pear.php.net/go-pear.phar
$ php go-pear.phar
Please note, you may need to install the wget package via your Unix/Linux/BSD package manager. On Debian and Ubuntu this is done via:
$ sudo apt-get install wget
Alternatively, download the go-pear.phar file via your browser.
If the process just exits without any output, your syslog will probably contain the following lines:
suhosin[4705]: ALERT - Include filename ('phar://go-pear.phar/index.php') is an URL that is not allowed (attacker 'REMOTE_ADDR not set', file '/root/go-pear.phar', line 1236)To work around this problem, enable the phar in
/etc/php5/conf.d/suhosin.ini
:suhosin.executor.include.whitelist = phar
Use curl as shown below to download the go-pear.phar file or just download the go-pear.phar file via your browser.
$ curl -O https://pear.php.net/go-pear.phar
$ php -d detect_unicode=0 go-pear.phar
You're now ready to configure PEAR for installation.
First you need to change the Installation Base.
So type 1, and then press Enter.
Enter /usr/local/pear
Press Enter.
Then, you will need to change the Binaries directory.
Type 4, and then press Enter.
Enter /usr/local/bin
Press Enter.
Once you have changed the Installation Base and the Binaries Directory, press Enter to install PEAR.
For a system-wide installation, you will need to execute the go-pear script with increased permissions. This is only recommended for advanced users.
$ curl -O https://pear.php.net/go-pear.phar $ sudo php -d detect_unicode=0 go-pear.phar
If you are running your site at a web hosting provider with no direct access to the server (via local logins, Telnet or SSH), you can use the PEAR Installer using the Web Frontend or (S)FTP.
Go to go-pear and save as
go-pear.php
.
Copy go-pear.php
to
your server and open the corresponding URL in your browser, for
example http://example.com/pear/go-pear.php
.
Do not forget to protect the pear directory if you did not do already before the installation: Make it unreadable and not executable from external (i.e. put it outside
public_html
).
Both pear and pecl tools
should be available everywhere on command line.
For that to work, pear's binary (bin
) directory
should be in your PATH
variable.
To verify it works, simply type pear. A list of commands should be shown:
$ pear
Commands:
build Build an Extension From C Source
bundle Unpacks a Pecl Package
channel-add Add a Channel
...
You should further test that PEAR is up to date:
$ pear version
PEAR Version: 1.7.2
PHP Version: 5.2.6RC4-pl0-gentoo
Zend Engine Version: 2.2.0
Running on: Linux ...
To use PEAR and PEAR compatible packages in your applications,
you normally include them into your PHP scripts using
require_once().
For this to work, PEAR's php_dir
must be a
part of PHP's include path.
First, check where PEAR installs .php
files:
$ pear config-get php_dir
/usr/share/lib/php/
This directory will contain System.php
.
Now it's time to find which configuration file is used by your PHP installation. On command line, execute:
$ php --ini
Configuration File (php.ini) Path: /etc/php/cli-php5
Loaded Configuration File: /etc/php/cli-php5/php.ini
Scan for additional .ini files in: /etc/php/cli-php5/ext-active
Additional .ini files parsed: /etc/php/cli-php5/ext-active/php_gtk2.ini,
/etc/php/cli-php5/ext-active/xdebug.ini
To see which php.ini
is used by PHP on your
web server, create a file with only <?php phpinfo(); ?>
as the contents, and save it in your local web root as
check_php.php
. Open the file in your browser as
http://localhost/check_php.php
, to find the path to
the php.ini
file your web server is using.
Now check PHP's include_path setting on command line:
$ php -c /path/to/php.ini -r 'echo get_include_path()."\n";'
To check PHP's include_path in your web server, create a file with
only <?php phpinfo(); ?>
as the contents, and
save it in your local web root as check_php.php
.
Open the file in your browser as http://localhost/check_php.php
,
to verify the include_path your web server is using.
In every case, PEAR's php_dir
should be in the
include path. If not, add it in your system's php.ini
.
Now that this is done, try including a file. Create a new
check_pear.php
file with the following contents:
<?php
require_once 'System.php';
var_dump(class_exists('System', false));
?>
System.php
is shipped with every PEAR installation
and thus should be on your computer, too.
Open the file with the browser from your web server, and also try
it on command line. The only output should be
bool(true)
A message like:
Warning: require_once(System.php): failed to open stream:
No such file or directory in /path/to/check_pear.php on line 2
means that your include path is not correct. (So go and fix it!)
A completely white page in your browser hints two things:
Your server is configured to not display any errors to the
user/browser (display_errors
Off)
There was an error including System.php
, and
you should check you server's error log.
That's it! Now go on and install some packages.
After changing php.ini
, you need to restart
your web server.
Few people also reported they had to restart the whole machine physically, probably due to PATH changes not propagating correctly. Before wasting hours and after you tried everything else, just try that.
Newer Linux distributions use multiple php.ini
files; mostly one for the web server
(e.g. /etc/php/apache2-php5/
)
and one for command line
(like /etc/php/cli-php5/
).
Make sure you edit the right ones.
On Windows, recent versions of PHP use php.ini
from their own directory (where php.exe
is).
You still might have an old php.ini
in your windows
or system(32)
directory that fools you.
You cannot get away with using absolute paths in your own
require_once() statements as an
altervative to fixing your include_path
,
because all the other files that are then required by your scripts
are all coded for relative pathing based on
include_path
.
php.ini
To get PEAR working properly, you need to adjust PHP's include_path. After you found php.ini, open it in an editor.
Search for the line include_path
.
Now that you found it, you probably will see a semicolon
;
at the beginning. This means the line is a comment.
Add a new line below it.
In this line, write:
include_path="."
Depending on your operating system, add a
:
(Unix/Linux/FreeBSD/Mac OS X)
or a ;
(Windows) after the dot.
Add PEAR's php_dir
after it.
(The directory System.php
is located in!)
The result should look like that:
; Unix include_path=".:/usr/local/php/pear/"
or
; Windows include_path=".;C:\php\pear\"
Prerequisites
The following description requires the latest version of the PEAR package manager to be installed.
This chapter shows you how to use the PEAR command line installer.
Always remember that PEAR has a help command:
$ pear help .. list of commands follows ..
Command examples will have a dollar sign
$
before each line that is typed. This helps to distinguish input from the output of the PEAR installer.
$
is just an example for a command prompt. Depending on your distribution or personal preferences, your shell might display a username, often combined with the current path:joe:~/some/path>
Help for a command is just as easy to get:
$ pear help <command> $ pear help install .. help for install follows ..
PEAR has a number of configuration options that you can change. Getting an overview about them is as easy as issuing a
$ pear config-show
The default settings suffice for novice users, there is rarely a need to change them when getting started. You can go on with installing packages.
Reading single values is accomplished by using
config-get
. The following command will show you where
all the .php files of your installed pear packages reside.
$ pear config-get php_dir /usr/share/pear
Changing a value is as easy as retrieving it:
$ pear config-set preferred_state beta config-set succeeded
When changing a config setting, it does not immediately get applied to already installed packages. This is especially true when changing variables like
data_dir
. Use$ pear upgrade --force
to reinstall all packages in such cases.
Variable Name | Description | Default Value |
---|---|---|
bin_dir | Directory where executables are installed | /usr/bin |
doc_dir | Directory where documentation is installed | /usr/lib/php/docs |
ext_dir | Directory where loadable extensions are installed | ./ |
php_dir | Directory where PHP files are installed (like PEAR files) | /usr/lib/php |
cache_dir | PEAR installer cache directory, and used by XMLRPC | /tmp/pear/cache |
data_dir | Directory where data files are installed | /usr/lib/php/data |
php_bin | The PHP CLI or CGI binary for executing scripts | /usr/bin/php |
test_dir | Directory where regression tests are installed | /usr/lib/php/tests |
cache_ttk | Number of seconds that the local cache is used, and not updated (Time To Kill) | 3600 |
preferred_state | Preferred package state: stable, beta, alpha, devel, or snapshot | stable |
umask | umask used when creating files (Unix-like systems only) | 22 |
verbose | Debug log level: 0-3 where 3 is full debug mode. | 1 |
http_proxy | The optional HTTP proxy address (host:port) used when downloading packages | |
remote_config | Remote configuration file, used to mirror a local installation on a remote server through ftp. (PEAR 1.4+) | |
auto_discover | Auto-discover new channels from command line or dependencies | 0 |
default_channel | Default channel (PEAR 1.4+) | pear.php.net (pecl.php.net if using the pecl command) |
preferred_mirror | Preferred channel mirror (PEAR 1.4+) | pear.php.net (pecl.php.net if using the pecl command) |
master_server | PEAR server [deprecated in PEAR 1.4+] | pear.php.net |
password | PEAR password (used by maintainers) | |
sig_bin | Signature handling program | /sw/bin/gpg |
sig_keydir | Signature key directory | /etc/pearkeys |
sig_keyid | The key used for signing | |
sig_type | Package signature type (only gpg) | gpg |
username | PEAR username (used by maintainers) |
After getting PEAR working on your machine (see Installation) you most likely want to install some packages. This guide shows people new to the PEAR command line installer how to get started.
The general command to install a PEAR package named "foo" is
$ pear install foo
Typing this and pressing return, the package will be downloaded and installed on your computer. It does not matter if you write the package name in lowercase, UPPERCASE or MixedCase - the installer will find the package by lowercasing the name.
When a package is already installed, you will get the following message:
$ pear install foo Ignoring installed package pear/foo Nothing to install
This happens even if there is a newer version of the package! The correct command to upgrade to the lastest version is
$ pear upgrade foo upgrade ok: channel://pear.php.net/Foo-1.2.3
If the package already has the lastest version, you will get a message similar to the following:
Ignoring installed package pear/Foo Nothing to upgrade
In the case you deleted some file and really really want to re-install the package, you have two choices:
Uninstall the package, and reinstall it afterwards
Force the installation
Forcing an command should only be done when you absolutely know what you are doing - you might in some circumstances break PEAR otherwise. Forcing something should always be the last option.
$ pear install -f foo $ pear upgrade -f foo
Now and then, you will get error messages like
Failed to download pear/foo within preferred state "stable", latest release is version 0.1.2, stability "beta", use "channel://pear.php.net/foo-0.1.2" to install Cannot initialize 'channel://pear.php.net/foo', invalid or missing package file Package "channel://pear.php.net/foo" is not valid install failed
Reason for this is that PEAR by default installs stable packages only. When a package is in state devel, alpha or beta it will refuse to install them. You can easily persuade it by adding either the version number or the stability you are willing to accept:
$ pear install Foo-beta $ pear install Foo-alpha
You can also install a specific version, or upgrade to a specific version regardless of the state:
$ pear install Foo-1.2.3 $ pear upgrade Foo-1.2.3
If you don't care about stability and don't want to specify the
-alpha
or -beta
suffix
everytime, you can change the global stability config option:
$ pear config-set preferred_state alpha config-set succeeded
You can switch between the following stability states (sorted by stability):
stable
beta
alpha
devel
A package often requires other packages to be installed to function correctly. Such a relation is called a dependency. The PEAR installer has full support for dependencies; it can automatically install required and/or optional dependencies if you wish so.
If you try to install a package with required dependencies, you will get an error that the installation failed. Looking deeper and actually reading the messages shows you that the package needs dependencies that are not installed on your system:
$ pear install html_page2 Did not download dependencies: pear/HTML_Common, use --alldeps or --onlyreqdeps to download automatically pear/HTML_Page2 requires package "pear/HTML_Common" (version >= 1.2) No valid packages found install failed
You have several choices:
Install dependent packages by hand
Let PEAR automatically install necessary dependencies only
Let PEAR automatically install necessary and optional dependencies
The first method can be a painful and daunting process, because dependent packages itself can have dependencies.
Both other methods just require a switch to the install command,
either --onlyreqdeps
(install required dependencies
only) or --alldeps
(install all dependencies).
$ pear install --onlyreqdeps html_page2 WARNING: "pear/HTML_Common" is deprecated in favor of "pear/HTML_Common2" downloading HTML_Page2-0.5.0beta.tgz ... Starting to download HTML_Page2-0.5.0beta.tgz (15,467 bytes) ......done: 15,467 bytes downloading HTML_Common-1.2.4.tgz ... Starting to download HTML_Common-1.2.4.tgz (4,519 bytes) ...done: 4,519 bytes install ok: channel://pear.php.net/HTML_Common-1.2.4 install ok: channel://pear.php.net/HTML_Page2-0.5.0beta
You can download individual packages for e.g. offline installation on a second machine just as you would install or upgrade a package:
$ pear download Foo
After downloading, you will have a file like
Foo-1.2.3.tgz
if the latest version of
Foo was 1.2.3
. Installing it
is as easy as typing
$ pear install Foo-1.2.3.tgz
We removed this section, because, today, manually installing a package requires a deeper understanding of the way how packages are organized and what happens during the installation process. You should read the section about the
package.xml
in the Developers Guide (package.xml and package.xml 2.0), if you really want install a package without the PEAR installer.If you want to install PEAR on a remote host without shell access, you should look into Installation of a local PEAR copy on a shared host.
This passage will describe how to install the latest development version of a PEAR package from SVN.
It is NOT recommended to run a package from SVN in working environments! Because SVN versions are not regular releases, this means:
You should use a package from SVN only, if:
If you still want to install a package from SVN, you have to do the same steps like a package maintainer creating a new release of a package. If you have problems following the next steps, take a look into the Developers Section of the manual.
Get the package files from SVN like described in http://www.php.net/svn.php
The name of the module to checkout is
pear/<packagename>/trunk
, i.e.
svn checkout http://svn.php.net/repository/pear/packages/HTTP_Client/trunk HTTP_Client.
Check the package.xml
file, especially the
dir and file entries. They must match the existing files and directory
structure. If they differ, contact the package maintainer and ask for
an update of the package.xml
.
Create a valid package using the PEAR Installer pear package <path to package.xml>
If you have already installed the package: remove it to avoid version conflicts: pear uninstall <package>
Install your package archive: pear install <package-file>
Now, you have a SVN version installed!
You should upgrade to an official release of the package as early as possible. Before you install the official release, uninstall the SVN version to avoid version conflicts.
The procedure of installing PECL packages is described in the pecl manual section on the PHP website.
If you simply want to get to know the installed version of a package or see its description, issue an info command:
$ pear info PEAR About pear.php.net/PEAR-1.7.1 .. more info follows ..
You often don't know which files belong to a certain installed package. list-files is helping you here. It shows you the file's role and its full path:
$ pear list-files log Installed Files For log ======================= Type Install Path doc /usr/share/pear/docs/Log/docs/guide.txt ... php /usr/share/pear/Log/composite.php php /usr/share/pear/Log/console.php ... data /usr/share/pear/data/Log/misc/log.sql test /usr/share/pear/tests/Log/tests/composite.phpt ... php /usr/share/pear/Log.php
While we try to document each package in the manual, many packages
- with or without a manual entry - come with examples. They have the
file role "doc
" and can be located with
list-files, too:
$ pear list-files log | grep ^doc doc /usr/share/pear/docs/Log/docs/guide.txt doc /usr/share/pear/docs/Log/examples/composite.php doc /usr/share/pear/docs/Log/examples/console.php doc /usr/share/pear/docs/Log/examples/display.php doc /usr/share/pear/docs/Log/examples/error_log.php doc /usr/share/pear/docs/Log/examples/file.php doc /usr/share/pear/docs/Log/examples/firebug.php ...
list shows you all installed packages with their version and stability:
$ pear list Installed packages, channel pear.php.net: ========================================= Package Version State Archive_Tar 1.3.2 stable Auth 1.5.4 stable Cache 1.5.4 stable Console_Getargs 1.3.4 stable Console_Getopt 1.2.3 stable ...
To see all available packages on the channel server, use remote-list:
$ pear remote-list ... output follows ...
This command takes some time if there are many packages on the server
Instead of going to the PEAR website, you can use the pear installer to search for package names.
$ pear search w3c Retrieving data...0%. Matched packages, channel pear.php.net: ======================================= Package Stable/(Latest) Local Services_W3C_CSSValidator 0.1.0 (alpha) An Object Oriented Interface ...
The result list shows name, remote version and stability and the package description. Alternatively, you can use the package browser on pear.php.net.
Channels are alternative package sources. See the channel section of the manual for more information.
What you need to know now: By using channels, you can install packages that are not part of PEAR. Prominent PHP projects like Horde and PHPUnit distribute their software through PEAR-compatible channels.
Before you can use a channel, your pear installation needs to know about it. This process is called "channel discovery".
Once you know the project's channel url, just type:
$ pear channel-discover pear.phpunit.de Adding Channel "pear.phpunit.de" succeeded
Downloading a channel.xml file, you can add a channel just using that file using channel-add:
$ pear channel-add my-channel.xml ...
Using list-channels, you can get an overview of known channels:
$ pear list-channels Registered Channels: ==================== Channel Summary components.ez.no eZ Enterprise components demochanserv.bogo Simple demo channel server gnope.org PHP-Gtk2 applications pear.chiaraquartet.net Chiara Testing Channel pear.phing.info Channel for Phing build tool releases pear.php.net PHP Extension and Application Repository pear.phpunit.de PHPUnit channel server pear.symfony-project.com symfony project PEAR channel pearified.com PEAR-Compatible Extension and Application Repository pecl.php.net PHP Extension Community Library __uri Pseudo-channel for static packages
More insight about a channel can be gotten with channel-info. It prints out a description, the channel's shortcut name (alias) as well as mirror information.
$ pear channel-info pear.php.net Channel pear.php.net Information: ================================= Name and Server pear.php.net Alias pear Summary PHP Extension and Application Repository ...
When installing or doing anything else with a package that is not in your default channel, you need to specify the channel by full name or alias:
$ pear install gnope/Dev_Inspector ... installation of package "Dev_Inspector" in channel "gnope" $ pear list -c pear.phpunit.de Installed packages, channel pear.phpunit.de: ============================================ Package Version State PHPUnit 3.2.5 stable
General rule is that when you would specify a package name, use
$channel/$packagename
now. All other functions with
channel support have a -c
option to specify the
channel name or alias.
PEAR compatible channels can be password protected. You could use this to e.g. distribute custom proprietary software to your clients, and don't want to publicy publish those packages anywhere.
Password protection is done via a HTTP Basic Authentication
(.htaccess
and .htpassword
on
Apache). When trying to discover such a password protected channel, you
will get a message like this:
$ pear channel-discover pear.company.com Discovery of channel "pear.company.com" failed (channel-add: Cannot open "http://pear.company.com/channel.xml" (File http://pear.company.com:80/channel.xml not valid (received: HTTP/1.1 401 Authorization Required )))
In this case, download the channel.xml
file
manually, for example with your browser or wget. A
channel.xml
is always in the root directory of the
channel server. After that, discover the channel with the saved
file:
$ pear channel-add /path/to/saved/channel.xml Adding Channel "pear.company.com" succeeded
Now the PEAR manager needs to know about the channel's username and password. We tell him by using set-config in connection with the channel option:
$ pear config-set -c pear.company.com username johndoe config-set succeeded $ pear config-set -c pear.company.com password secret config-set succeeded
Now test if we did everything right by showing available packages on the channel:
$ pear list-all -c pear.company.com Retrieving data...0% All packages [Channel pear.company.com]: ========================== Package Latest Local comp/WorldDominator 0.8.1 Tool to dominate the world
If you changed your creditentials, you should issue a pear clear-cache command to make sure that the installer does not use cached data.
Here's a list of commands available to the pear command line tool. Many of these commands may require root access to the server.
Command | Description |
---|---|
build | Build the extension from source |
bundle | Download and unpack a PECL extension |
channel-add | Add a Channel (PEAR 1.4+) |
channel-alias | Specify an alias to a channel name (PEAR 1.4+) |
channel-delete | Remove a channel from the list (PEAR 1.4+) |
channel-discover | Initialize a channel from its server name (PEAR 1.4+) |
channel-info | Retrieve information on a channel (PEAR 1.4+) |
channel-login | Connects and authenticates to remote channel server ( PEAR 1.8+) |
channel-logout | Logs out from the remote channel server ( PEAR 1.8+) |
channel-update | Update an existing channel (PEAR 1.4+) |
clear-cache | Clear the REST/XML-RPC cache |
config-create | Create a default configuration file (PEAR 1.4+) |
config-get | Echo a specific configuration setting |
config-help | Show information about a setting |
config-set | Set a specific configuration setting value |
config-show | Show all configuration setting values |
convert | Convert a package.xml 1.0 format to package.xml 2.0 format (PEAR 1.4+) |
cvsdiff | Execute and display a "cvs diff -u " on
all files within the package |
cvstag | Set CVS Release Tag |
download | Download a package but not install it |
download-all | Downloads every available package |
info | Display information about a package |
install | Install a package, will report with success or failure |
list | List installed packages |
list-all | List all packages, packaged and/or available |
list-channels | List available channels (PEAR 1.4+) |
list-files | List files in an installed package (PEAR 1.4+) |
list-upgrades | List available upgrades for the installed packages |
login | Connects and authenticates to remote server [Deprecated in favor of channel-login] |
logout | Logs out from the remote server [Deprecated in favor of channel-logout] |
makerpm | Builds a RPM spec file from a PEAR package |
package | Build a package |
package-dependencies | Show package dependencies |
package-validate | Validate package consistency |
pickle | Build PECL Package |
remote-info | Information about remote packages |
remote-list | List remote packages |
run-scripts | Run post-install scripts bundled with a package (PEAR 1.4+) |
run-tests | Run regression tests |
search | Search the remote package database |
shell-test | Do a shell script test |
sign | Sign a package distribution file |
svntag | Set a SVN release tag |
uninstall | Uninstall and remove a package |
update-channels | Update the channel list (PEAR 1.4+) |
upgrade | Upgrade a package to the current version (see also: preferred_state) |
upgrade-all | Upgrade all packages (see also: list-upgrades) |
The PEAR installer can be used for tracking project dependencies with a simple package definition file, without having to describe the entire project in that file.
For example, this can be done to ensure that a specified set of PEAR packages are installed on a development environment, into a test environment, and finally into a production environment, all with minimum fuss.
Another reason for using PEAR to track dependencies is to reduce the amount of work required when trying to do this work manually... checking to see if a new release adds required functionality, closes security issues, or includes bug fixes can take time. Add a few more packages and interdependencies to the mix, and the amount of work grows exponentially.
The PEAR installer can take care of all of this for you. You just need to tell it what you want.
The logical steps for creating a package definition file, typically named package.xml, that describes the dependencies required by your project are:
List what you need - Include in this details such as what channels the packages are to be installed from and whether you only want very specific versions of those packages to be installed, or if you want to specify what the minimum versions of the packages should be.
Create a package.xml including these packages as its manifest.
Test that it works
Assuming you want to replicate a subset of PEAR-installable packages that are on one machine and you wish to install that same set on another machine, this is one suggested way to get the required details: Use the output of "$ pear list -a" to determine the names of the packages, which versions are installed and what channels they were installed from.
For example (edited for brevity):
Installed packages, channel doc.php.net:
========================================
Package Version State
PhD 0.4.6 beta
Installed packages, channel pear.php.net:
=========================================
Package Version State
PEAR 1.8.1 stable
PEAR_Size 0.1.9 alpha
PHP_Beautifier 0.1.14 beta
PHP_CodeSniffer 1.2.0RC1 beta
PHP_CompatInfo 1.9.0 stable
PHP_UML 0.5.2 alpha
PhpDocumentor 1.4.2 stable
Testing_Selenium 0.4.3 beta
Installed packages, channel pear.phpunit.de:
============================================
Package Version State
PHPUnit 3.3.16 stable
phpcpd 1.1.1 stable
The package.xml can be generated using the pfm tool or in your favourite text editor. It's your call really.
The pfm tool can be installed via:
$pear install PEAR_PackageFileManager_Cli
pfm is a commandline tool that analyses the contents of a project directory and creates a package.xml describing what it has found. Because we don't want to install any project specific files we're going to cheat in order to keep the generated package.xml as small as possible.
Begin by creating an empty directory and then moving into it:
kguest:~$ cd MyProject-packages
/home/kguest/MyProject-packages
then create an empty php file:
kguest:~/MyProject-packages$ touch empty.php
Download and save this package.xml into this new directory; then run pfm.
kguest:~/MyProject-packages$ wget package.xml
kguest:~/MyProject-packages$ pfm
PEAR Package File Manager Command Line Tool
Please enter the location of your package [.]*:<press enter>
Enter the base install directory
1) /dev/null
Please choose an option: 1
PEAR Package File Manager Command Line Tool
1. Package name [MyProject_Packages]
2. Channel/URI [URI: file:///MyProject-packages/package.xml]
3. Summary [this is a wrapper package for install...]
4. Description [<none>]
5. Maintainers
6. Version [Release: 0.0.1 API: 0.0.1]
7. Stability [Release: alpha API: alpha]
8. License [LGPL]
9. Notes [<none>]
10. Dependencies
11. Tasks
12. Regenerate contents
13. Echo package file to stdout
14. Save & Quit
15. Quit without saving (ctrl-c)
Please choose an option from the menu: 10
Edit Dependencies
1. Return to main menu
2. Add new dependency
3. Clear all dependencies
4. Change PHP >= 5.2.1
5. Change PEAR Installer >= 1.8.0
Dependencies:
Please choose an option from the menu: 2
Dependency type [pkg] (pkg,ext,php,prog,os,sapi,zend)*: pkg
Dependency name*: PHP_CodeSniffer
Is the dependency (o)ptional or (r)equired [r] (o,r)*: r
Package type (c)hannel or (u)ri [c] (c,u)*: c
Dependency channel [pear.php.net]*:
Minimum version: 1.2.0RC1
Maximum version:
Edit Dependencies
1. Return to main menu
2. Add new dependency
3. Clear all dependencies
4. Change PHP >= 5.2.1
5. Change PEAR Installer >= 1.8.0
Dependencies:
Required Package dependency "PHP_CodeSniffer" - pear.php.net
Repeat this as required until all packages that you want to be managed have been added; then return to the main menu, save and quit. Naturally, you should also use pfm to change the name of the project described by package.xml to something of your choosing.
Download the package.xml file from above. Use this as a template and refer to the tag reference for version 2 of package.xml in the developers section for details of how to add dependencies to the file.
The best way to test that your meta package works is to uninstall at least one of the packages that is a dependency of it, then [re]install your meta package.
$ pear uninstall PHP_CodeSniffer
$ pear install -f package.xml
Installing the meta package here should result in the dependency, PHP_CodeSniffer, being installed also.
Check this using pear list.
$ pear list
This documentation is organized with progressive complexity in mind. If you are new to PEAR, you should read the PEAR Concepts section of the manual first, and then return to learn more about Pyrus.
If you are migrating from using the PEAR installer, it will be helpful to read the section on differences from PEAR.
To get started with information on the features available in Pyrus, start with the Pyrus commands section to familiarize yourself with the commands available for users of Pyrus, then read about the configuration options available in Pyrus.
If you wish to extend Pyrus's functionality, read the section on plugins. Finally, if you are inspired to distribute your own software using Pyrus, you can either apply for a PEAR developer account (information here) or read the section on releasing packages through your own channel here.
PEAR, PEAR2 and PECL developers should read the documentation on how to use Pyrus to manage your existing or new packages here.
In botany (the science of plants), Pyrus
is the genus of
the pear family, and thus includes all of the pear-producing trees and
shrubs under its taxonomical umbrella.
In PHP (the hypertext programming language), Pyrus
is
the next-generation PEAR Installer, a revolutionary package management
and distribution system that extends the functionality of the PEAR Installer's
already advanced functionality. Pyrus is also friendlier to projects
outside of PEAR that wish to take advantage of the strengths of PEAR through
channels.
Pyrus represents the cumulative effort of years of work, experience with the PEAR Installer since its inception in 1999, and feedback from users like you. We are very proud to present the best installation tool for PHP.
2009-06-25
Commands available for the Pyrus command-line script.
The install command is used to install packages and accepts a list of package names to install as arguments. Unlike upgrade, the install command will only install new packages.
Install recognizes several different ways of specifying a package:
Package type | Local/Remote | Example |
---|---|---|
package.xml file |
Local | php pyrus.phar install /path/to/package.xml |
Package release archive | Local | php pyrus.phar install /path/to/PackageName-1.2.3.tgz |
Static url | Remote | php pyrus.phar install http://example.com/PackageName-1.2.3.tgz |
Abstract package | Remote | php pyrus.phar install PackageName |
Abstract Packages are documented here.
By default, required package dependencies are also installed. To also
automatically install optional dependencies (not dependency groups, but
dependencies specified using the <optional> tag in package.xml),
pass the -o
or --optionaldeps
option
to the install command:
php pyrus.phar install -o PackageName
Plugins (documented here) must be explicitly
installed with the -p
or --plugin
option.
Plugins are installed into the location specified by the plugins_dir
configuration variable, which defaults to the same location that the user
configuration file is located ($HOME/.pear
in unix,
<My Documents>\pear
on windows).
php pyrus.phar install -p PEAR2_Pyrus_Developer
Developers packaging RPMs or other forms of distribution for OS vendors
should use the -r
or --packagingroot
option to install a package into a subdirectory for creating the RPM.
php pyrus.phar install -r /home/myuser/package PackageName
For the example above, if the default Pyrus installation is in
/usr/local/lib/pear
, all files will be installed
into /home/myuser/package/usr/local/lib/pear
. Another
way of understanding this is that the packagingroot option instructs
Pyrus to treat /home/myuser/package
as if it were the
root directory /
.
Developers packaging RPMs or other forms of distribution for OS vendors
should use the -r
or --packagingroot
option to install a package into a subdirectory for creating the RPM.
php pyrus.phar install -r /home/myuser/package PackageName
The --force
or -f
option can be used
to force installation if there are errors. For instance, this can be used
to override problems in dependency validation, or to force installation of a
package that is not stable enough. As such, it should be used sparingly if
at all. Better is to request a specific version for installation.
The upgrade command is almost identical to the install command. The only difference is that upgrade will also upgrade an existing package as well as install a new one.
The --packagingroot
option is also unavailable,
--packagingroot
should only be used for installing new files.
The uninstall command removes a package and accepts a list of installed packages to remove as arguments.
Plugins (documented here) must be explicitly
uninstalled with the -p
or --plugin
option.
Plugins are installed into the location specified by the plugins_dir
configuration variable, which defaults to the same location that the user
configuration file is located ($HOME/.pear
in unix,
<My Documents>\pear
on windows).
php pyrus.phar uninstall -p PEAR2_Pyrus_Developer
The --force
or -f
option can be used
to force uninstallation if there are errors. For instance, this can be used
to override problems in dependency validation. As such, it should be used
sparingly if at all. Better is to uninstall the packages tha depend on the
package being uninstalled.
The run-scripts
command is used to execute
post-install scripts of a package. It accepts a list
of abstract package names.
This command downloads a remote package to the current directory. It will accept any package name, abstract or concrete, and save the resulting file in the current directory.
Add a channel to the registry by its local channel.xml
file.
Use channel-discover to
add a channel by its name.
This command searches for a channel.xml first at a secure https
and then unsecured http
address.
Examples:
php pyrus.phar channel-discover pear.example.com
This tries to locate https://pear.example.com/channel.xml
and if that fails, http://pear.example.com/channel.xml
,
then adds the channel to the registry.
Use channel-add to add a local channel.xml to the registry.
Remote a channel from the registry. Note that the default channels cannot be removed from the registry.
Default channels in Pyrus:
__uri
pear.php.net
pear2.php.net
pecl.php.net
doc.php.net
This command is used to display a list of information on a package, such as the maintainers, the stability and version or versions available for installation (for remote packages).
If passed description
or notes
,
it displays the complete text of these fields. If passed files
,
it lists the files in the package and their on-disk location for installed
packages. For example:
php pyrus.phar info PackageName description
The command will accept any concrete or abstract package names as its argument. Note that if there is an ambiguity between an installed package and the remote package, Pyrus will assume that information is being requested on the installed package.
The --forceremote
or -r
option is used
to instruct Pyrus to resolve any ambiguity between an installed package and
a remote package to assume that information is requested on the remote package.
This command lists all installed packages in all channels, organized by the installation location.
List all known channels alphabetically.
The remote-list command lists all remote packages in a channel, organized by
category. This command present an alphabetized list of all packages with the
latest release and package summary. Installed packages are marked with an
asterisk (*
).
If the --basic
or -b
option is specified,
only package name, latest release, and latest stable release are listed.
This command lists all packages that have upgrades available within the
current preferred_state
or the installed package's stability,
whichever is less stable. This command does not list releases that are
incompatible with the current PHP version.
If package X
is installed with version 0.3.0
,
stability alpha
, and preferred_state
is stable
, Pyrus will list the newest version available
that satisfies stability of alpha
, beta
or stable
. If package Y
is also
installed, with version 1.2.3
, stability stable
,
only the latest stable release will be listed. In both cases, if the latest
available version that satisfies these requirements is not found, none will
be listed.
Another example: If package X
is installed with version
1.0.0
, stability stable
, and the
preferred_state
is alpha
, Pyrus
will list the newest version available
that satisfies stability of alpha
, beta
or stable
.
The upgrade-registry command is used to convert a registry from the old PEAR Installer format into the new Pyrus format, and then optionally remove the old registry.
The command creates registries in Sqlite3 and XML formats in the path passed
as the argument to upgrade-registry
. Note that Pyrus
registries are stored in the parent directory as the installed PHP files,
whereas PEAR registries are stored in the same directory as the installed
PHP files, so it is necessary for Pyrus to have write access to the parent
directory.
The --removeold
or -r
option instructs
Pyrus to remove the old PEAR registry when finished upgrading.
The config-show
command lists all system and user
configuration variables. Documentation on currently supported
configuration variables and how Pyrus organizes configuration can be found
here.
The mypear
command sets up the path or paths in which Pyrus
will look for installed packages. If multiple paths are specified, they
should be separated by PATH_SEPARATOR, which is
:
on unix systems, and ;
on Windows-based
systems. If multiple paths are specified, only the first path will be
used for installing packages. The other paths are only used to validate
package dependencies.
mypear
is a convenient alternative to using the
set
command to
set the my_pear_path
configuration variable.
The run-phpt
command is used to execute tests in the
PHPT
format. If the xdebug
extension
is present, the command can also be used to generate code coverage. This
coverage can then be used to construct a coverage report and even to
intelligently detect both modified tests and tests that are affected by
changes to the source code in between test runs.
The command takes as arguments a list of paths containing tests to execute,
unless the --modified
option is specified, then it takes
as arguments the path to the tests directory and the path to the source
directory. If the --modified
option is not specified,
and no arguments are passed, the command searches for tests in the current
working directory.
The --modified
or -m
option, if present,
implies both the --recursive
and --coverage
options, and is used to generate a coverage database and to use that database
to detect modifications in the tests and the source. These modified tests are
then executed.
The command places a file named pear2coverage.db
in the
tests directory, which is an Sqlite3 database containing the coverage information.
The coverage can be viewed as a web-based report by taking the
pear2coverage.phar.php
file installed with the developer
tools and placing it in a web server directory, and then browsing to it.
The web server must have the phar
and sqlite3
extensions enabled in order to function properly.
To illustrate how powerful this option is, imagine a hypothetical directory structure as follows:
src/ File1.php File2.php File3.php tests/ test1.phpt test2.phpt test3.phpt test4.phpt
Here are the source files:
File1.php:
<?php
class File1
{
var $a = 1;
function __construct($a = 1)
{
$this->a = $a;
}
function setInternalThing($thing)
{
$this->internal = $thing;
$this->internal->initialize($this);
}
}
?>
File2.php:
<?php
class File2
{
function initialize(File1 $parent)
{
$parent->a = 2;
}
}
?>
File3.php:
<?php
class File3 extends File2
{
function initialize(File1 $parent)
{
$parent->a = 3;
}
}
?>
test1.phpt:
--TEST--
test 1
--FILE--
<?php
function __autoload($class) { include __DIR__ . '/../src/' . $class . '.php'; }
$test = new File1(6);
if ($test->a != 6) {
echo '$a is not 6, it is ' . $test->a, "\n";
}
?>
===DONE===
--EXPECT--
===DONE===
test2.phpt:
--TEST--
test 2
--FILE--
<?php
function __autoload($class) { include __DIR__ . '/../src/' . $class . '.php'; }
$test2 = new File2;
$test = new File1;
$test->setInternalThing($test2);
if ($test->a != 2) {
echo '$a is not 2, it is ' . $test->a, "\n";
}
?>
===DONE===
--EXPECT--
===DONE===
test3.phpt:
--TEST--
test 3
--FILE--
<?php
function __autoload($class) { include __DIR__ . '/../src/' . $class . '.php'; }
$test2 = new File2;
$test = new stdClass;
$test2->initialize($test);
if ($test->a != 2) {
echo '$a is not 2, it is ' . $test->a, "\n";
}
?>
===DONE===
--EXPECT--
===DONE===
test4.phpt:
--TEST--
test 4
--FILE--
<?php
function __autoload($class) { include __DIR__ . '/../src/' . $class . '.php'; }
$test3 = new File3;
$test = new File1;
$test->setInternalThing($test3);
if ($test->a != 3) {
echo '$a is not 3, it is ' . $test->a, "\n";
}
?>
===DONE===
--EXPECT--
===DONE===
If a modification is made to File3.php
, the
run-phpt command will detect that only test4.phpt
uses
this file, and will run that test. If a modification is made to
File2.php
, test2.phpt
,
test3.phpt
and test4.phpt
will all be
executed, even though test4.phpt
does not directly
use the File2
class, because File3
extends File2
and so the file is loaded. If a modification
is made to File1.php
, test1.phpt
,
test2.phpt
and test4.phpt
will all
be executed. Finally, if any of the phpt test files are executed, or any
external files that they include are modified, they will be run again. If a
new test, test5.phpt
is added, the run-phpt command will
also detect the test and run it.
This allows extremely efficient development, as surgically running only tests that are affected by source code changes allows assurance that even the most remote file dependencies are validated, and irrelevant tests are not executed unnecessarily. By relying upon the coverage report, it is also easy to catch subtle logic bugs preventing code blocks from being executed, resulting in far more robust code much faster. Pyrus itself was developed using this technique.
The --recursive
or -r
option causes Pyrus
to recursively traverse directories specified
the --coverage
or -x
option causes
Pyrus to record coverage using the xdebug
extension's
code coverage capabilities.
The generate-pear2
command is used to create a bare
skeleton for a new PEAR2 package. It creates everything needed except the
source code.
Two arguments are accepted, package
and optionally
channel
.
The package
argument is the name of the package to create
a skeleton for. This is used as the directory name and as the package name
used within files related to creating a PEAR2 package.
If MyPackage
is passed, a directory will be created in
the current working directory titled MyPackage
. All the
required packaging files will be within this directory, and you can place
your code within the MyPackage/src/MyPackage/Main.php
file.
If no channel is specified
PEAR2_MyPackage
will be the name of your package ifMyPackage
is passed as the package name.
The channel
argument defaults to pear2.php.net.
The generate-pecl
command is used to create a bare
skeleton for a new PECL package or PHP extension as it will reside in
Subversion. This is designed to provide all of the functionality of the
ext_skel command but also generates a package.xml and other files that can
be used to automatically update for a release.
One argument is accepted, extension
.
This command automatically creates class definitions as well as ZEND_ARG_INFO for parameters to provide useful reflection to your extension's users.
The extension
argument is the name of the package to create
a skeleton for. This is used as the directory name and as the extension name
used within files related to creating a PECL package.
The --proto
or -p
option specifies a
file containing function and method prototypes to create in your new extension.
As an example, here are some supported protos:
int myfunc(string firstarg, unicode secondarg, array thirdarg,
object fourtharg [, double optionalarg1
[, float optionalarg2 [, callback optionalarg3 [, text optionalarg4]]]])
void Myclass::myfunc(array|object arg1, bool arg2, class arg3,
resource arg4, mixed arg5 [, ... varargs])
static int Myclass::staticfunc()
protected string Myclass::otherguy([mixed optionalarg])
static protected object Myclass::factory(text path)
A proto begins with either the return type or access modifiers
static
and one of public
,
protected
and private
followed
by the return type. Next, the name of the function, or name of the
class::method is specified, followed by an argument list. Optional
methods are enclosed in [brackets]
and whitespace
is important, so follow the conventions as in the above examples.
Each argument consists of a type followed by an argument name. The types are
informed by parameter parsing as supported by PHP's internal
zend_parse_parameters(). This is thoroughly documented
in the file README.PARAMETER_PARSING
inside PHP's
source code. Note that some of the parameter parsing choices only work
in PHP 6, in particular the unicode-related options.
The following types are supported:
array
(maps to 'a'
in parameter parsing)
array|object
(maps to 'A'
in parameter parsing)
bool
(maps to 'b'
in parameter parsing)
boolean
(maps to 'b'
in parameter parsing)
callback
(maps to 'f'
in parameter parsing)
class
(Maps to 'C'
in parameter parsing)
double
(maps to 'd'
in parameter parsing)
float
(maps to 'd'
in parameter parsing)
handle
(maps to 'r'
in parameter parsing)
int
(Maps to 'L'
in parameter parsing)
long
(Maps to 'L'
in parameter parsing)
mixed
(maps to 'z'
in parameter parsing)
object
(maps to 'o'
in parameter parsing)
resource
(maps to 'r'
in parameter parsing)
string
(maps to 's'
in parameter parsing)
text
(Maps to 'T'
in parameter parsing)
unicode
(maps to 'u'
in parameter parsing)
void
, use only for the return value of a function that returns nothing
...
(varags: maps to '*'
in parameter parsing)
However, if the parameter is not optional, if maps to '+'
is used.
Themake
command is available through the developer tools. If you do not have the developer tools installed, simply runphp pyrus.phar makeand Pyrus will ask if you would like to install the developer tools. If you assent with the wordyes
, Pyrus will automatically download them and install them for you.
This command creates a package.xml file from a standard PEAR2 directory layout, and then optionally creates a package release.
This command looks for these standard files:
CREDITS
README
RELEASE-X.Y.Z
(where X.Y.Z is the release version)API-X.Y.Z
(where X.Y.Z is the API version)and for a standard directory layout of
Sub-directory | Description |
---|---|
src/ |
PHP files |
data/ |
Data files |
test/ |
Test files |
doc/ |
Documentation files |
examples/ |
Example files (documentation) |
scripts/ |
Executable files, scripts |
www/ |
Web files |
customrole/ |
Custom installer role XML definition files |
customtask/ |
Custom installer task XML definition files |
customcommand/ |
Custom command XML definition files |
The CREDITS
file must have this format:
;; comments ignored Maintainer One [handle1] <email@example.com> (role) Maintainer Two [handle2] <email@example.com> (role)
Where role is one of lead
, developer
,
contributor
, or helper
.
The first line of README
is used as the summary of the
package, the rest is used as the description.
RELEASE-X.Y.Z
is used as the release notes. Pyrus will
scan all release notes (such as RELEASE-1.0.0
and
RELEASE-1.2.3
) and use the most recent version number
(1.2.3
in our example) as the version, and the contents
of the file (RELEASE-1.2.3
in our example) as the release
notes.
API-X.Y.Z
is used as notes about the
API and the version
X.Y.Z
is used as the API version.
if --packagexmlsetup
or -s
is specified,
it should be passed the name of a file in the package base directory that is
used to fine-tune the generated package.xml. This file should work with variable
$package
for modifying the package.xml, and
$compatible
for the compatible
one (if present). If --packagexmlsetup is not specified, and
packagexmlsetup.php
exists in the package base directory, it will be used.
Here is an example packagexmlsetup.php
:
<?php
$package->dependencies['required']->package['pear2.php.net/PEAR2_Autoload']->save();
$package->dependencies['required']->package['pear2.php.net/PEAR2_Exception']->save();
$package->dependencies['required']->package['pear2.php.net/PEAR2_MultiErrors']->save();
$compatible->dependencies['required']->package['pear2.php.net/PEAR2_Autoload']->save();
$compatible->dependencies['required']->package['pear2.php.net/PEAR2_Exception']->save();
$compatible->dependencies['required']->package['pear2.php.net/PEAR2_MultiErrors']->save();
?>
If --nocompatible
or -n
option
is passed in, Pyrus will not generate a package.xml that is compatible
with the PEAR Installer.
This option instruct the make
command to pass off the
finished package.xml to the
package
command. It accepts a comma-delimited list of file formats, and can be any
of phar
, tar
, tgz
or zip
.
This option is identical to the option for the
--stub
option of the package
command, and is ignored if --package
or -p
is not also specified.
This option is identical to the option for the
--extrasetup
option of the package
command, and is ignored if --package
or -p
is not also specified.
Thepackage
command is available through the developer tools. If you do not have the developer tools installed, simply runphp pyrus.phar packageand Pyrus will ask if you would like to install the developer tools. If you assent with the wordyes
, Pyrus will automatically download them and install them for you.
The package command bundles up the files of a package and its package.xml into an archive for distribution. It accepts a single optional argument, the path to a package.xml.
If the optional path is not provided, the command
searches for a file named package.xml
in the current
directory. If this file is an older version 1.0, it also searches for
package2.xml
in the current directory, and uses that
as the package file. This allows creating packages that are compatible
with ancient versions of PEAR older than version 1.4.0
If package.xml
exists and is
a PEAR2 package (requires pear installer version equal to or newer than
2.0.0a1
), the command will also look for a file
in the current directory named package_compatible.xml
.
This file should be a package.xml customized for use by the PEAR Installer,
and is automatically generated by the make
command unless explicitly prevented. This file makes it possible for the
PEAR Installer to install packages generated for Pyrus.
The created archive can be in any or all of the following formats:
The formats are requested by passing their name as an option like so:
php pyrus.phar package --phar --tar --tgz --zip
.
The phar
file format is a format that can be used to
create self-installing archives, or even archives that are self-contained
commands. pyrus.phar
, for instance, is a complete
application that runs directly from its archive. Due to security
considerations, the ability to create executable phar archives is disabled
by default. To create a phar archive, the phar.readonly
php.ini setting must be disabled like so:
php -dphar.readonly=0 pyrus.phar package --phar
The tar
and tgz
file formats are the
formats familiar to users of PEAR. tgz
is a gzipped tar
archive, and requires the zlib
extension be enabled in
order to create it.
The zip
file format is most familiar to Windows users.
All packages designed for Pyrus automatically have PEAR2/Autoload.php
,
PEAR2/Exception.php
, PEAR2/MultiErrors.php
,
and PEAR2/MultiErrors/Exception.php
bundled so that
these dependencies are available when a package is extracted without use of
Pyrus to install it. Packages designed for installation by the PEAR Installer
and not for Pyrus do not bundle any dependencies.
Pyrus supports creating archives that have a mandatory signature using
OpenSSL
certificates such as those available from
CACert. Signed archives
cannot be processed without the presence of the openssl
PHP extension. In addition, a bug in the phar
extension's
verification of openssl signatures requires PHP version 5.3.1
or newer. Creation of signatures only requires PHP version 5.3.0
or newer.
To create a package signature, set the
openssl_cert
configuration variable to the path of your PKCS#12 certificate (generally
saved with a file extension of p12
), and the
handle
configuration variable to your handle in package.xml.
The --stub
or -s
option specifies
a PHP script to use as the stub to a phar archive, and is ignored if
--phar
is not passed to the command. The stub is a
short executable script that is executed as a boot strap when a phar
archive is passed to PHP like
php pyrus.phar
. All stubs
must end with the __HALT_COMPILER();
directive to be a
valid stub.
If the --stub
option is not explicitly specified and
a phar archive is being created, Pyrus will look for a file named
stub.php
in the same directory as the package.xml, and
will use that as the stub for the phar archive if found.
Here is pyrus's stub:
<?php
if (version_compare(phpversion(), '5.3.0', '<')) {
if (substr(phpversion(), 0, 5) != '5.3.0') {
// this small hack is because of running RCs of 5.3.0
echo "Pyrus requires PHP 5.3.0 or newer.\n";
exit -1;
}
}
foreach (array('phar', 'spl', 'pcre', 'simplexml') as $ext) {
if (!extension_loaded($ext)) {
echo 'Extension ', $ext, " is required\n";
exit -1;
}
}
try {
Phar::mapPhar();
} catch (Exception $e) {
echo "Cannot process Pyrus phar:\n";
echo $e->getMessage(), "\n";
exit -1;
}
function pyrus_autoload($class)
{
$class = str_replace('_', '\\', $class);
if (file_exists('phar://' . __FILE__ . '/php/' . implode('/', explode('\\', $class)) . '.php')) {
include 'phar://' . __FILE__ . '/php/' . implode('/', explode('\\', $class)) . '.php';
}
}
spl_autoload_register("pyrus_autoload");
$frontend = new \PEAR2\Pyrus\ScriptFrontend\Commands;
@array_shift($_SERVER['argv']);
$frontend->run($_SERVER['argv']);
__HALT_COMPILER();
The --extrasetup
or -e
option
is used to specify the path to a PHP script that is used to include
files in the created packages that are not in the package.xml list of
files. This is used to create archives that are designed to be friendly to
users simply "trying before they buy" the archive, or to provide
support files needed to create self-installing archives.
If the --extrasetup
option is not explicitly specified,
then Pyrus looks for a file named extrasetup.php
in the
same directory as the package.xml, and will use this if found.
The extrasetup script should create a variable named $extrafiles
that contains an associative array mapping relative path within the archive
to an absolute path of a file on disk.
Here is Pyrus's extrasetup, which demonstrates bundling of the required dependencies PEAR2_HTTP_Request and PEAR2_Console_Commandline:
<?php
/**
* This file generates the pyrus.phar file and PEAR2 package for Pyrus.
*/
$rp = __DIR__ . '/../HTTP_Request/src/HTTP';
$cc = __DIR__ . '/../sandbox/Console_CommandLine/src/Console';
$extrafiles = array(
'php/PEAR2/HTTP/Request.php' => $rp . '/Request.php',
'php/PEAR2/HTTP/Request/Adapter.php' => $rp . '/Request/Adapter.php',
'php/PEAR2/HTTP/Request/Adapter/Curl.php' => $rp . '/Request/Adapter/Curl.php',
'php/PEAR2/HTTP/Request/Adapter/Http.php' => $rp . '/Request/Adapter/Http.php',
'php/PEAR2/HTTP/Request/Adapter/Phpsocket.php' => $rp . '/Request/Adapter/Phpsocket.php',
'php/PEAR2/HTTP/Request/Adapter/Phpstream.php' => $rp . '/Request/Adapter/Phpstream.php',
'php/PEAR2/HTTP/Request/Exception.php' => $rp . '/Request/Exception.php',
'php/PEAR2/HTTP/Request/Headers.php' => $rp . '/Request/Headers.php',
'php/PEAR2/HTTP/Request/Listener.php' => $rp . '/Request/Listener.php',
'php/PEAR2/HTTP/Request/Response.php' => $rp . '/Request/Response.php',
'php/PEAR2/HTTP/Request/Uri.php' => $rp . '/Request/Uri.php',
'php/PEAR2/Console/CommandLine.php' => $cc . '/CommandLine.php',
'php/PEAR2/Console/CommandLine/Result.php' => $cc . '/CommandLine/Result.php',
'php/PEAR2/Console/CommandLine/Renderer.php' => $cc . '/CommandLine/Renderer.php',
'php/PEAR2/Console/CommandLine/Outputter.php' => $cc . '/CommandLine/Outputter.php',
'php/PEAR2/Console/CommandLine/Option.php' => $cc . '/CommandLine/Option.php',
'php/PEAR2/Console/CommandLine/MessageProvider.php' => $cc . '/CommandLine/MessageProvider.php',
'php/PEAR2/Console/CommandLine/Exception.php' => $cc . '/CommandLine/Exception.php',
'php/PEAR2/Console/CommandLine/Element.php' => $cc . '/CommandLine/Element.php',
'php/PEAR2/Console/CommandLine/Command.php' => $cc . '/CommandLine/Command.php',
'php/PEAR2/Console/CommandLine/Argument.php' => $cc . '/CommandLine/Argument.php',
'php/PEAR2/Console/CommandLine/Action.php' => $cc . '/CommandLine/Action.php',
'php/PEAR2/Console/CommandLine/Renderer/Default.php' => $cc . '/CommandLine/Renderer/Default.php',
'php/PEAR2/Console/CommandLine/Outputter/Default.php' => $cc . '/CommandLine/Outputter/Default.php',
'php/PEAR2/Console/CommandLine/MessageProvider/Default.php' => $cc . '/CommandLine/MessageProvider/Default.php',
'php/PEAR2/Console/CommandLine/Action/Callback.php' => $cc . '/CommandLine/Action/Callback.php',
'php/PEAR2/Console/CommandLine/Action/Counter.php' => $cc . '/CommandLine/Action/Counter.php',
'php/PEAR2/Console/CommandLine/Action/Help.php' => $cc . '/CommandLine/Action/Help.php',
'php/PEAR2/Console/CommandLine/Action/StoreFloat.php' => $cc . '/CommandLine/Action/StoreFloat.php',
'php/PEAR2/Console/CommandLine/Action/StoreInt.php' => $cc . '/CommandLine/Action/StoreInt.php',
'php/PEAR2/Console/CommandLine/Action/StoreString.php' => $cc . '/CommandLine/Action/StoreString.php',
'php/PEAR2/Console/CommandLine/Action/StoreTrue.php' => $cc . '/CommandLine/Action/StoreTrue.php',
'php/PEAR2/Console/CommandLine/Action/Version.php' => $cc . '/CommandLine/Action/Version.php',
);
The pickle command is designed to make the creation of an extension for PECL developers very easy. It scans a SVN checkout of a PECL package directory, generates package.xml, and packages a release in one step.
The pickle command looks for these standard files:
and for a standard directory layout of
/ Extension source files data/ Data files tests/ Test files doc/ Documentation files examples/ Example files (documentation)
The CREDITS file must have this format for the pickle command to recognize it:
;; extensionname
Maintainer One [handle1] <email@example.com> (role)
Maintainer Two [handle2] <email@example.com> (role)
Where role is one of lead, developer, contributor, helper. The first line of README is used as the summary of the package. RELEASE-X.Y.Z is used as the release notes.
X.Y.Z
in the filename RELEASE-X.Y.Z
is
also used to automatically calculate the stability, and
X.Y.Z
in the filename API-X.Y.Z
is used
to calculate the API stability. The formula is relatively simple: if X is
0
, the stability is set to alpha
.
Otherwise, if X is 1
or greater, the following methods are
used to calculate the stability.
If Z contains a
as in the version 1.0.0a1
the stability is set to alpha
If Z contains b
as in the version 1.0.0b1
the stability is set to beta
If Z contains RC
as in the version 1.0.0RC1
the stability is set to beta
Otherwise, stability is set to stable
.
The pickle command has 4 arguments:
php pyrus.phar pickle extname channelname /path/to/packagedir extensions...
/path/to/packagedir
specifies the location in which to
package up the release. If not present, it defaults to the current working
directory. The pickle command usually should be executed from that directory,
this argument is present to allow batch creation of package.xml files for
multiple extensions with a single shell script.
If package.xml
does not exist in the location packaging
will take place, then the first argument is required. The first argument
is the name of the package, which is usually the same name as the extension,
and the second argument is the channel name. If not specified, the package
is assumed to be in the pecl.php.net
channel.
The final argument, extensions
is a list of file extensions
that should be considered source files. By default, the file extensions
recognized as source files are:
If --donotpackage
or -n
is specified,
then the pickle command will only generate package.xml, and will not
package up a release. The package
command can be used to build the release after reviewing package.xml.
This command builds an installed PECL extension. It accepts as its arguments a list of installed php extension packages to build. It performs an automatic version of:
phpize --clean phpize ./configure make make install
In the source directory of the package. It also will prompt the user
if any <configureoption>
tags exist in package.xml
(see the documentation for <configureoption>
here)
2009-06-26
Pyrus has two kinds of configuration files, system configuration files, which
are stored directly in the installation location, and user configuration files,
which are stored in the home directory of the user, or in the Windows
equivalent, My Documents
.
Each installation is tightly bound to the configuration that defines where files should be installed, which PHP executable to use, and which php.ini is used to manage that installation.
On the other hand, user-specific preferences are tightly bound to each user, by placing them in the user's home directory. These values are universal to all installations and include things like the amount of information to display, the preferred mirror to use for a channel, the openssl certificate to use for package signing, and so on.
This way, multiple PEAR installations can be managed very safely without ambiguity over what configuration values should be used.
The user configuration file is always stored in the user's personal directory,
the home directory on unix, and My Documents
on Windows.
For a unix user with username user
, the user configuration
file is stored in /home/user/.pear/pearconfig.xml
. For
a Windows user, the configuration is stored in
My Documents\pear\pearconfig.xml
. The file is saved in
XML format and can be hand-edited if necessary.
Unlike the system configuration file, the user configuration file is always saved when an operation that writes to disk is called, such as installing a package. On startup, Pyrus uses the configuration file's existence to determine whether it is being executed for the first time, and if so, prompts the user to initialize a few default settings such as the PEAR path.
There are two kinds of user configuration variables, installation-wide variables
such as verbose
, and channel-specific variables such as
openssl_cert
. Channel-specific variables can have different
values for different channels. This allows setting a different certificate
for each channel, for instance. The channel name is used as a tag, with
non-XML friendly characters translated into simple mnemonics (such as
.
becoming DOT
).
Here is a sample user configuration file (with line breaks added for readability):
<?xml version="1.0"?> <pearconfig version="1.0"> <default_channel>pear2.php.net</default_channel> <auto_discover>0</auto_discover> <http_proxy></http_proxy> <cache_dir>/home/user/testpear/cache</cache_dir> <temp_dir>/home/user/testpear/temp</temp_dir> <verbose>1</verbose> <paranoia>2</paranoia> <preferred_state>stable</preferred_state> <umask>0022</umask> <cache_ttl>3600</cache_ttl> <my_pear_path>/home/user/testpear:/usr/local/lib/php</my_pear_path> <plugins_dir>/home/user/.pear</plugins_dir> <openssl_cert> <pear2DOTphpDOTnet>/home/user/mykey.p12</pear2DOTphpDOTnet> </openssl_cert> <handle> <pear2DOTphpDOTnet>cellog</pear2DOTphpDOTnet> </handle> </pearconfig>
auto_discover
is a flag (boolean), defaulting to
0
or off. If on, this flag instructs Pyrus
to automatically discover channels of dependencies (see
channel-discover for
a more in-depth description of what channel discovery means).
For example, let's say we are installing Package foo
from channel
pear2.php.net
, and that foo
depends on package
bar
from channel pear.example.com
.
If Pyrus does not know the pear.example.com
channel, and
auto_discover
is set to 1
, it will
attempt to discover information on the channel, and after successfully
adding its information to the registry, will then successfully download
and install the dependency bar
. However, if
auto_discover
is disabled, Pyrus will simply fail
with an error explaining that nothing is known about the
pear.example.com
channel, and that it must be
added prior to installation.
cache_dir
is the location where HTTP caching of
PEAR channel REST files is cached.
the cache_ttl
configuration variable is used to determine
when to consider the PEAR Channel REST
cache to have been invalidated. It is measured in seconds, and by default
is 3600
, or 1 hour.
The default channel is the channel that should be implied when an
Abstract Package
is ambiguous. By default, it is pear2.php.net
.
As an example, when executing:
php pyrus.phar install PEAR2_HTTP_Request
The abstract package PEAR2_HTTP_Request
is ambiguous - it
does not specify a channel. Pyrus assumes, therefore, that the requested
package is from the default channel, and acts as if the user had in fact
typed:
php pyrus.phar install pear2.php.net/PEAR2_HTTP_Request
The default_channel
value is also used for all
channel-specific configuration values.
This is a channel-specific configuration value
The download_dir
is where downloaded packages are kept.
This can allow later repairing or easy cloning of an installation. There is
no penalty if the files are removed, and they may be easily removed to
conserve space.
This is a channel-specific configuration value
The handle
variable should be set to the handle you use
to identify yourself in the <maintainers>
section
of package.xml for packages in a specific channel.
For example, if your <maintainer>
entry in package.xml
is:
<lead> <name>Greg Beaver</name> <user>cellog</user> <email>cellog@php.net</email> <active>no</active> </lead>
Your handle is cellog
.
This configuration variable is used in conjunction with the openssl_cert configuration variable to implement package signing.
The http_proxy
configuration variable should be set to the
full URI of your local HTTP proxy, or left blank for none.
The my_pear_path
configuration value controls the order
in which Pyrus cascades PEAR installations. The path should have the same
syntax as include_path
, a
PATH_SEPARATOR-separated list of full paths to PEAR
installations. The my_pear_path
configuration variable
can be easily set with the mypear
command.
Only the first path is considered to be read/write, the others are only used to validate package dependencies on download.
For instance, the my_pear_path
/home/user/testpear:/usr/local/lib/php
instructs Pyrus
to install all packages into the PEAR installation at
/home/user/testpear
, and to also use the PEAR installation
in /usr/local/lib/php
to validate dependencies.
On Windows, an example my_pear_path
is
D:\customPear;C:\php5
.
This is a channel-specific configuration value
The openssl_cert
configuration variable should be set to
the full path to your personal PKCS#12 certificate, as signed by a recognized
certificate authority such as CACert.
Pyrus uses this certificate along with your
handle to implement
package signing.
Your certificate must have the email address you use in package.xml as its alternate name, otherwise Pyrus will refuse to use it. Pyrus uses the email address as stated in package.xml of the releasing maintainer of a package to verify that the package was actually created by the maintainer. This makes a man-in-the-middle attack far more difficult to execute, as well as verifying package integrity.
This is a channel-specific configuration value
This configuration variable is a legacy variable from PEAR, is not yet used in Pyrus, and may be removed before the stable release.
The plugins_dir
directory is where all Pyrus plugins are
installed. By default, it is the directory in which the user configuration
file is stored.
This is a channel-specific configuration value
This variable controls which mirror of a channel should be used to retrieve package releases and PEAR Channel REST information. By default, it is set to the main channel path.
The preferred_state
configuration variable controls the
release stability level of packages that will be installed. By default it
is stable
, which instructs Pyrus to ignore any releases
with lesser stabilities beta
, alpha
or devel
unless explicitly requested by the user.
This can be changed to allow riskier installation of newer, less-tested releases that are on the cutting edge of development.
The temp_dir
configuration directive is where all
temporary files are extracted by Pyrus.
The umask
configuration value is used to control the
default file mask used when setting file permissions. By default it is
octal value 0022
. The value is a bitmask that is used
to clear any bits. Thus, a umask
of 0000
will cause files to be installed with 0666
permissions. A
umask
of 0022
causes files to be installed
with 0644
permissions.
This is a channel-specific configuration value
This configuration variable is a legacy variable from PEAR, is not yet used in Pyrus, and may be removed before the stable release.
The verbose
setting controls how much information Pyrus
echoes as it performs its task. The higher the setting, the more information
Pyrus will spew. By default, it is set to 1
.
The paranoia
setting controls how Pyrus handles automatic upgrades
to new versions of packages. The API
version of the installed package is compared against the API version of remote
packages, and chooses a release that is compatible with the current version
based on the paranoia level. This setting does not affect upgrades of
local packages, only those retrieved from a remote PEAR channel server.
The paranoia setting is a numeric setting with levels 1
to 4
supported, anything above 4
is
automatically converted to 4
. The levels work as follows:
API version is ignored, only package stability and PHP version compatibility is used to determine which package to download for installation.
This is the default setting, and specifies that backwards compatibility must be maintained.
This is performed by checking that the
API version first digit does not change. Thus a package with
API version of 1.2.3
cannot upgrade to a new package
with API version 2.0.0
. Upgrades are allowed
to versions such as 1.2.4
or 1.3.0
.
This is a strict setting, only allowing security and other API bugfixes.
This is performed by checking that the API version's first and second
digits do not change. Thus a package with
API version of 1.2.3
cannot upgrade to a new package
with API version 2.0.0
. Upgrades are allowed
to versions such as 1.2.4
, but not to
1.3.0
.
Do not allow any API changes
This is the most paranoid setting, and prevents upgrading to any package that changes API version whatsoever.
If using pyrus.phar, the setting can also be changed with the -p
command-line option. This example sets paranoia temporarily to 1:
php pyrus.phar -p install PackageName
This example sets paranoia temporarily to 4:
php pyrus.phar -pppp install PackageName
The system configuration file is always saved in a file named .config
in the base of the PEAR installation. Thus, if php files are installed in
/usr/local/lib/pear/php
, then the configuration for
that installation is installed in /usr/local/lib/pear/.config
.
The configuration is stored in XML format, and can be modified by hand if
necessary.
The configuration will not be saved if no values are modified from the default values
Here is a sample system configuration file (with line breaks added for readability):
<?xml version="1.0"?> <pearconfig version="1.0"> <ext_dir>/usr/local/lib/php/extensions/debug-non-zts-20090115</ext_dir> <cfg_dir>/home/user/testpear/cfg</cfg_dir> <doc_dir>/home/user/testpear/mydata</doc_dir> <bin_dir>/usr/local/bin</bin_dir> <www_dir>/home/user/testpear/www</www_dir> <test_dir>/home/user/testpear/tests</test_dir> <src_dir>/home/user/testpear/src</src_dir> <php_bin>/usr/local/bin/php</php_bin> <php_ini>/usr/local/lib/php.ini</php_ini> <php_prefix></php_prefix> <php_suffix></php_suffix> </pearconfig>
executable files (files with a script
role) are installed
into bin_dir
Customizable configuration files (files with cfg
role) are
installed into this directory. These files are intended to be manipulated
by the user, and Pyrus will not overwrite them if any changes have been made.
Files with roles data
, customcommand
,
customrole
and customtask
are installed
into the data_dir
configuration variable.
In Pyrus, this is a pseudo-configuration variable: its value cannot be changed
without creating an entirely new repository. Data is always stored in the
directory data/
relative to the location of the installation.
files with the doc
role are installed into
doc_dir
.
Compiled extension files are installed into the ext_dir
,
and files with role ext
.
the php_bin
configuration variable refers to the location
of PHP that should be used for installed executable scripts.
Files with roles php
are installed
into the php_dir
configuration variable.
In Pyrus, this is a pseudo-configuration variable: its value cannot be changed
without creating an entirely new repository. PHP files are always stored in the
directory php/
relative to the location of the installation.
the php_ini
variable should be set to the location of
php.ini
that is used by this installation, and is used
to automatically enable extensions on installation.
This variable should be set to the value that --program-prefix
was set to when PHP was configured, and is used when building PECL extensions.
This variable should be set to the value that --program-suffix
was set to when PHP was configured, and is used when building PECL extensions.
Files with the src
role are installed into
src_dir
, and building of PECL packages
also happens in this directory.
Files with test
role are installed into this directory.
Files with www
role are installed into this directory.
2009-06-25
Differences between Pyrus and the PEAR Installer. This documentation is a work in progress.
Pyrus is a re-factored version of the PEAR installer, re-designed for new features available in PHP 5.3 and newer. As a result, Pyrus is more robust than PEAR as well as faster. Several of the subtle design flaws in the PEAR Installer have been fixed, and so Pyrus is more stable than the PEAR Installer for handling an existing PEAR repository.
Here is a brief summary of the differences from PEAR:
Pyrus is distributed as a single file, pyrus.phar. Because of PHP's new phar extension, Pyrus does not need to be installed, and can run directly from the file pyrus.phar.
Pyrus also simplifies the command-line options available, and provides a
far greater range of developer tools for creating, managing and
distributing packages through tools such as the simple channel server and
package.xml creation command make
.
Several security vulnerabilities in the design of PEAR were discovered due to the particular Command Pattern implementation used to detect file roles, commands, and other plugins. Pyrus fixes this by requiring that all plugins be installed into a centralized location separate from the actual PEAR installation. In addition, installation of plugins cannot happen at the same time as installation of packages, thus the enforced separation ensures a level of security that is much higher than PEAR supports, while preserving the flexibility that extending the installer provides.
Pyrus also feaures true package signing and signature verification using OpenSSL PKCS#12 and X.509 certificates. This allows users to directly verify the validity of a package, protecting from man-in-the-middle attacks and other potential disruptions of a package release. This feature requires the openssl extension, which is not enabled by default.
PEAR supports signing packages using PGP keys, but has no mechanism in place to verify the signed packages. Pyrus will refuse to install a signed package without verifying the signature even if the openssl extension is not enabled.
In addition, the new paranoia setting can be used to control how upgrades are performed to releases that change the API, helping to guarantee safe upgrades to future releases.
Because Pyrus takes advantage of PHP 5.3's built in support for XML processing, archive handling, and advanced structures through the simplexml, libxml2, phar, sqlite3, and spl extensions, Pyrus is significantly smaller than PEAR, and as a result consumes far less memory to accomplish its tasks.
Pyrus is also faster than PEAR because of its reliance on built-in features of PHP 5.3 and a more structured object-oriented design.
Pyrus has redundant registries in XML and Sqlite3 database formats, as well as support for the existing PEAR registry. Reconstruction of a corrupted registry is simple and fully supported.
In addition, all installation tasks occur within an atomic transaction, including file installation and removal, so that if an installation or uninstall command fails mid-stream, or something as drastic as a power failure occurs, the PEAR installation will be not be left in a half-installed state.
Pyrus supports cascading installations, so that a system-wide installation of core packages can be recognized. By default, include_path is used to detect PEAR installations, but a different location for a PEAR installation can be passed directly to Pyrus as its first argument.
Convention over configuration allows packages constructed with the new PEAR2 coding standards to be installed simply by extracting the archive, and then later upgraded using Pyrus without the intermediate step of using Pyrus to install the packages. For the first time, this allows a try-before-you-buy approach to be possible.
The same principle also makes bundling a PEAR2 package in another package's source repository possible, and Pyrus can be used to easily upgrade the package or revert to a previous version.
Pyrus has been developed with extensive unit testing and xdebug coverage data has been used to verify that the code is being executed. As a result, the first alpha release of Pyrus has 10% higher code coverage than the most recent stable version of the PEAR Installer.
One of the most important conceptual changes in Pyrus is how configuration
is handled. PEAR was designed to handle at most 2 installations by default,
a system and a user PEAR installation, and it excels at this. As soon as
PEAR is used on multiple installations, a separate configuration file must
be specified (as in pear -c /path/to/another/pear.conf install
blah
). This leads to what is colloquially referred to as
"config hell", where it is easy to accidentally install
things into the wrong place without realizing it. Pyrus's configuration
handling is specifically engineered to eliminate config hell, and to make
handling multiple PEAR installations simple.
PEAR stores all configuration values in a single configuration file, and allows
specifying a different configuration file for different setups. In addition,
PEAR supports automatic cascade of a system configuration file and a user
configuration file. The configuration values are used when installing applications,
and for customizing things like the path to php in the PEAR Installer's
pear
command. Configuration files are stored separate
from the PEAR installations that they represent.
Pyrus instead splits up configuration files into two separate components: one file contains user customizations such as the preferred stability of packages to install, the username and password for logging into a channel, the verbose setting and so on. Configuration variables that affect where to install files are stored in a separate configuration that is tightly bound to the PEAR installation.
Thus, a PEAR configuration setup might look like:
/etc/pear.conf
, defines
php_dir
as /usr/local/lib/php
/home/username/.pearrc
, defines
php_dir
as /home/username/pear
.
include_path
is set to /home/username/pear:/usr/local/lib/php
.
The equivalent configuration setup with Pyrus would look like:
/usr/local/lib/pear
, system configuration
stored in /usr/local/pear/.config
and php files in
/usr/local/lib/pear/php
.
/home/username/pear
, system
configuration stored in /home/username/pear/.config
and php
files in /home/username/pear/php
.
/home/username/.pear/pearconfig.xml
.
include_path
is set to /home/username/pear:/usr/local/lib/pear/php
.
By default, Pyrus uses the include_path
to locate PEAR
installations, but this is configurable with the new user configuration
variable my_pear_path
, which is a PATH_SEPARATOR
separated list of paths to PEAR installations.
In addition, an explicit path can be directly passed to Pyrus:
php pyrus.phar /home/username/pear:/usr/local/lib/pear list-packages
The above command will list the installed packages in both registries in
/home/username/pear
and in /usr/local/lib/pear/php
.
A detailed reference of Pyrus's handling of configuration files is here
Pyrus fully supports PEAR's registry format, but introduces 2 new registry formats, an sqlite3 database-based registry, and an XML file-based registry. These registries are fully redundant, and can be used to repair or reconstruct a corrupt registry.
In addition, unlike PEAR, which stores the registry in the same directory as
the PHP source files, Pyrus stores the registry in its parent directory.
Thus, PHP files stored in /usr/local/lib/php
have their
registry in /usr/local/lib
.
For backwards compatibility, an older PEAR registry is always stored in the location the PEAR Installer expects it to be stored.
Pyrus is intelligent enough to detect which registries are present, and
to use them. If only an older PEAR registry exists, Pyrus will not
automatically upgrade to the newer format. However, the
upgrade-registries
command is available to convert from
an older registry to the newer format.
Some of the benefits of the newer registry format include much speedier processing of a large registry at installation time due to Sqlite3's speedy processing. Additionally, truly safe uninstall-time resolution of dependencies is possible, something that PEAR can only do for relatively simple package dependency trees.
In addition, the XML registry consists of storing the package.xml and channel.xml files for package releases in the same location that they are packaged. This is what makes it possible to extract a package created with Pyrus and then later use Pyrus to upgrade it.
For instance, the hypothetical PEAR2_Foo
package from
channel pear2.php.net
version 1.2.3
will store its package.xml in path
.xmlregistry/packages/pear2.php.net/PEAR2_Foo/1.2.3-package.xml
inside the archive, so that when it is extracted, it lines up exactly with how
the package would look on disk when installed with the XML registry.
Pyrus no longer supports package.xml version 1.0, although it will include a package.xml version 1.0 in an archive designed to support both earlier PEAR versions and the more recent versions. It does not validate the package.xml, however, and so it is important to validate any older package.xml format using PEAR and not Pyrus.
In addition, Pyrus has introduced support for PEAR2 packages that can be extracted to disk and then later upgraded using Pyrus. To implement this feature, Pyrus transforms paths in a different way from PEAR.
For example, this entry from a package.xml:
<dir name="php" baseinstalldir="PEAR2"> <dir name="Pyrus"> <dir name="Developer"> <dir name="CoverageAnalyzer"> <dir name="SourceFile"> <file role="php" name="PerTest.php"/> </dir> </dir> </dir> </dir> </dir>
would cause PEAR to install PerTest.php
into the
relative path PEAR2/php/Pyrus/Developer/CoverageAnalyzer/SourceFile/PerTest.php
.
Pyrus, however, recognizes that php
is actually the default value of
the php_dir system
configuration variable, and strips it from the path, resulting in
PerTest.php
being installed into the path:
PEAR2/Pyrus/Developer/CoverageAnalyzer/SourceFile/PerTest.php
.
To enable this handling, one need only set the <pearinstaller>
dependency to version 2.0.0a1
or newer. Pyrus will
automatically recognize any package.xml with a <pearinstaller>
dependency on any version of the PEAR Installer as an older package.xml, and
will not perform the magic removal of configuration values from
directories.
No other changes have been made to package.xml handling, except that the
default version of package.xml used when generating a package.xml is
version 2.1, which has been supported by the PEAR Installer since
version 1.5.0
.
The PEAR Installer allowed packages to install custom commands as well as custom file roles and custom file tasks that are used in package.xml. Pyrus also allows this, but the format of plugins is very different. If you are simply a user of PEAR, you probably won't notice the difference, except that some packages that use custom file roles or tasks will not be installable by Pyrus until the maintainer releases an update that will work with both PEAR and Pyrus.
PEAR extensions are installed directly into the location where the PEAR
installer is located. Thus, if PEAR is located in
/usr/local/lib/php/PEAR
, a custom command must install
its XML information file and PHP script into
/usr/local/lib/php/PEAR/Command
,
a custom file role must install its XML information file and PHP script into
/usr/local/lib/php/PEAR/Installer/Role
and a custom file
task must install its PHP script into
/usr/local/lib/php/PEAR/Task
.
Pyrus is distributed as a phar archive, so this model is no longer physically
possible, one cannot just magically insert files into the phar archive without
considerable pain and annoyance (the phar.readonly INI setting must be disabled
by hand). Instead, Pyrus installs all plugins into a location specified
by the new plugins_dir
user configuration variable.
By default, this installs plugins into $HOME/.pear/plugins
on unix systems, and My Documents\pear\plugins
on Windows.
All plugins to Pyrus now must provide an xml file with one of the three
new file roles customcommand
, customrole
or customtask
in package.xml. Pyrus uses the information
in the XML file to locate the PHP script that will execute the plugin. In
addition, only one plugin is allowed per package, and the first one Pyrus
encounters is the one that will be used. More information on custom plugins
is provided in the Pyrus plugins section
of the manual.
For developers of existing PEAR custom roles/tasks and post-install scripts, a special kind of file role that allows configuration of your package after installation, making your work compatible with Pyrus can be accomplished. See the documentation on Custom Roles, Custom Tasks, and Post-install scripts.
PEAR's handling of PECL extensions has been somewhat dodgy, with reports of issues with phpize failing, and other problems. Pyrus attempts to fix this through two major changes to the way PECL packages are built and installed.
src/
and then built directly inside this location.
PEAR builds PECL packages by creating a temporary directory, installing all of the source files into this directory, building the extension, harvesting built files, and finally removes the temporary directory. This system works most of the time, but if there is a problem, it is impossible to debug because the sources are removed.
Pyrus solves this by splitting PECL package installation into two components,
installation and build. The installation process simply places the
source files into a sub-directory of the src_dir
configuration
variable, and thus makes it possible to debug problems or even apply patches
to the source and re-build.
In addition, because installation is separate from the actual building, PECL packages can now implement post-install scripts to handle truly complex configuration of extensions beyond what configure options can handle.
The new build
command enhances PEAR's package building by
directly calling this sequence:
phpize --clean
phpize
./configure [any options specified by <configureoptions>]
make
make install
This is the same sequence one would use to build a PECL extension by hand. In addition, proc_open() is used instead of popen(), which allows better monitoring and control of the processes in question.
Lastly, Pyrus is more cross-platform than PEAR, as it replaces a
call to find
and xargs
with native
PHP iteration over the modules directory when listing extension components
that were built.
Post-install scripts (documented here and here) are mostly the same in Pyrus with a few important differences.
The XML format in package.xml is identical, so no change need be made to
the <paramgroup>
or other tags. The script itself
should still follow the naming conventions of PEAR. The only real difference
is the naming of methods within the script.
PEAR requires that all post-install scripts implement init(),
run() and optionally postProcessPrompts().
Pyrus requires post-install scripts to implement init2(),
run2(), and optionally postProcessPrompts2().
This allows PEAR and Pyrus-based post-install scripts to co-exist in the
same package without difficulty. Note that post-install scripts must be
E_STRICT
and E_DEPRECATED
compliant,
otherwise many PHP warnings will be emitted. One way of handling this issue
is to put PHP4 non-E_STRICT-compatible code into a separate file and include it
dynamically at run-time. The same should be done for any PHP5+ non-PHP4
compatible code if the post-install script is expected to be able to run in PHP 4.
The init2() method should accept two parameters like so:
<?php
function init2($pkg, $lastversion)
{
}
?>
$pkg
is an object representing the package, and
$lastversion
is the last installed version of the
package.
These two methods should accept identical parameters to what the PEAR equivalent accepted. The only reason these are called is to allow easy differentiation between what kind of installer is calling the post-install script.
Custom commands
have changed substantially in Pyrus. PEAR commands draw their information
from Xml files located in the directory PEAR/Command
and
require all commands to be implemented in a single file. Pyrus is much
more flexible on this account, and extends the custom XML format used to
define commands. Because of this difference, it is theoretically possible
to create custom commands that will work in both the PEAR Installer and in
Pyrus, although the internal implementation of these commands will necessarily
be quite different.
The biggest difference between how a custom command is implemented in Pyrus has to do with the new plugin system (documented here). Before reading any further, it may be a good idea to familiarize yourself with the way that plugins work in Pyrus by reading the documentation on plugins, then return back to finish reading about custom commands.
Here is PEAR's version of the xml for the install
command:
<commands version="1.0"> <install> <summary>Install Package</summary> <function>doInstall</function> <shortcut>i</shortcut> <options> <force> <shortopt>f</shortopt> <doc>will overwrite newer installed packages</doc> </force> <loose> <shortopt>l</shortopt> <doc>do not check for recommended dependency version</doc> </loose> <nodeps> <shortopt>n</shortopt> <doc>ignore dependencies, install anyway</doc> </nodeps> <register-only> <shortopt>r</shortopt> <doc>do not install files, only register the package as installed</doc> </register-only> <soft> <shortopt>s</shortopt> <doc>soft install, fail silently, or upgrade if already installed</doc> </soft> <nobuild> <shortopt>B</shortopt> <doc>don't build C extensions</doc> </nobuild> <nocompress> <shortopt>Z</shortopt> <doc>request uncompressed files when downloading</doc> </nocompress> <installroot> <shortopt>R</shortopt> <doc>root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM</doc> <arg>DIR</arg> </installroot> <packagingroot> <shortopt>P</shortopt> <doc>root directory used when packaging files, like RPM packaging</doc> <arg>DIR</arg> </packagingroot> <ignore-errors> <shortopt></shortopt> <doc>force install even if there were errors</doc> </ignore-errors> <alldeps> <shortopt>a</shortopt> <doc>install all required and optional dependencies</doc> </alldeps> <onlyreqdeps> <shortopt>o</shortopt> <doc>install all required dependencies</doc> </onlyreqdeps> <offline> <shortopt>O</shortopt> <doc>do not attempt to download any urls or contact channels</doc> </offline> <pretend> <shortopt>p</shortopt> <doc>Only list the packages that would be downloaded</doc> </pretend> </options> <doc>[channel/]<package> ... Installs one or more PEAR packages. You can specify a package to install in four ways: "Package-1.0.tgz" : installs from a local file "http://example.com/Package-1.0.tgz" : installs from anywhere on the net. "package.xml" : installs the package described in package.xml. Useful for testing, or for wrapping a PEAR package in another package manager such as RPM. "Package[-version/state][.tar]" : queries your default channel's server ({config master_server}) and downloads the newest package with the preferred quality/state ({config preferred_state}). To retrieve Package version 1.1, use "Package-1.1," to retrieve Package state beta, use "Package-beta." To retrieve an uncompressed file, append .tar (make sure there is no file by the same name first) To download a package from another channel, prefix with the channel name like "channel/Package" More than one package may be specified at once. It is ok to mix these four ways of specifying packages. </doc> </install> </commands>
And the same command as implemented in Pyrus:
<?xml version="1.0" encoding="UTF-8"?> <commands version="2.0" xmlns="http://pear2.php.net/dtd/customcommand-2.0"> <command> <name>install</name> <class>PEAR2\Pyrus\ScriptFrontend\Commands</class> <function>install</function> <summary>Install a package. Use install --plugin to install plugins</summary> <shortcut>i</shortcut> <options> <option> <name>plugin</name> <shortopt>p</shortopt> <type><bool/></type> <doc>Manage plugin installation only</doc> </option> <option> <name>packagingroot</name> <shortopt>r</shortopt> <type><string/></type> <doc>Install the package in a directory in preparation for packaging with tools like RPM</doc> </option> <option> <name>optionaldeps</name> <shortopt>o</shortopt> <type><bool/></type> <doc>Automatically download and install all optional dependencies</doc> </option> <option> <name>force</name> <shortopt>f</shortopt> <type><bool/></type> <doc>Force the installation to proceed independent of errors. USE SPARINGLY.</doc> </option> </options> <arguments> <argument> <name>package</name> <multiple>1</multiple> <optional>0</optional> <doc>package.xml, local package archive, remove package archive, or abstract package.</doc> </argument> </arguments> <doc>Installs listed packages. local package.xml example: php pyrus.phar install package.xml local package archive example: php pyrus.phar install PackageName-1.2.0.tar remote package archive example: php pyrus.phar install http://www.example.com/PackageName-1.2.0.tgz Examples of an abstract package: php pyrus.phar install PackageName installs PackageName from the default channel with stability preferred_state php pyrus.phar pear/PackageName installs PackageName from the pear.php.net channel with stability preferred_state php pyrus.phar install channel://doc.php.net/PackageName installs PackageName from the doc.php.net channel with stability preferred_state php pyrus.phar install PackageName-beta installs PackageName from the default channel, beta or stable stability php pyrus.phar install PackageName-1.2.0 installs PackageName from the default channel, version 1.2.0</doc> </command> </commands>
The format for commands in Pyrus is much more fine-grained, and provides both better validation and better presentation of options and arguments as passed in from the user. In addition, Pyrus allows fine-grained specification of where a command is located, and automatically registers an autoloader to load the class implementing the command, and any dependent classes in the same location.
The details of new tags like <classprefix> and <autoloadpath> are documented in the full documentation of custom roles here.
This XML metadata file is identified by Pyrus through the use of the file role
customcommand
, which is used in the custom command's package.xml.
Here is an example from the <contents> of a package.xml:
... <contents> <dir name="data"> <file name="commands.xml" role="customcommand"/> <file name="someotherdata.csv" role="data"/> </dir> </contents> ...
Custom file roles (documented here) have changed substantially in Pyrus. Fortunately, the usage of roles in package.xml remains unchanged, and Pyrus's expectation of custom role implementation does not conflict or overlap with PEAR's at all, so custom role packages can easily be designed to work with both PEAR and Pyrus.
The biggest difference between how a custom role is implemented in Pyrus has to do with the new plugin system (documented here). Before reading any further, it may be a good idea to familiarize yourself with the way that plugins work in Pyrus by reading the documentation on plugins, then return back to finish reading about custom roles.
Pyrus modifies the XML description file for custom file roles slightly, here is the PEAR version of a role:
<role version="1.0"> <releasetypes>php</releasetypes> <releasetypes>extsrc</releasetypes> <releasetypes>extbin</releasetypes> <releasetypes>zendextsrc</releasetypes> <releasetypes>zendextbin</releasetypes> <installable>1</installable> <locationconfig>php_dir</locationconfig> <honorsbaseinstall>1</honorsbaseinstall> <unusualbaseinstall /> <phpfile>1</phpfile> <executable /> <phpextension /> <config_vars /> </role>
And the same role as implemented in Pyrus:
<role version="2.0" xmlns="http://pear2.php.net/dtd/customrole-2.0"> <name>php</name> <class>PEAR2\Pyrus\Installer\Role\Php</class> <releasetypes>php</releasetypes> <releasetypes>extsrc</releasetypes> <releasetypes>extbin</releasetypes> <releasetypes>zendextsrc</releasetypes> <releasetypes>zendextbin</releasetypes> <installable>1</installable> <locationconfig>php_dir</locationconfig> <honorsbaseinstall>1</honorsbaseinstall> <unusualbaseinstall /> <executable /> </role>
The most obvious difference is that Pyrus now includes the name of the role,
PEAR extracts the role name from the name of the file (which in the
example above was Php.xml
). In addition, the
<phpfile>
and <phpextension>
elements have been removed.
The details of new tags like <class> and <autoloadpath> are documented in the full documentation of custom roles here.
This XML metadata file is identified by Pyrus through the use of the file role
customrole
, which is used in the custom role's package.xml.
Here is an example from the <contents> of a package.xml:
... <contents> <dir name="data"> <file name="myrole.xml" role="customrole"/> <file name="someotherdata.csv" role="data"/> </dir> </contents> ...
Custom file tasks (documented here) have changed substantially in Pyrus. Fortunately, the XML format of tasks used in package.xml remains the same, and Pyrus's expectation of custom task implementation does not conflict or overlap with PEAR's at all, so custom task packages can easily be designed to work with both PEAR and Pyrus.
The biggest difference between how a custom task is implemented in Pyrus has to do with the new plugin system (documented here). Before reading any further, it may be a good idea to familiarize yourself with the way that plugins work in Pyrus by reading the documentation on plugins, then return back to finish reading about custom tasks.
In PEAR, custom tasks are detected simply by scanning the
PEAR/Tasks
directory, and no differentiation is made between
tasks built into PEAR, and external custom tasks.
Pyrus custom tasks are detected through the use a new XML metadata format. The format is simple, and looks like this:
<?xml version="1.0" encoding="UTF-8"?> <task xmlns="http://pear2.php.net/dtd/customtask-2.0" version="2.0"> <name>mytask</name> <classprefix>vendor_PackageName</classprefix> <autoloadpath/> </task>
The details of what <classprefix> and <autoloadpath> mean are revealed in the full documentation of custom tasks here.
This XML metadata file is identified by Pyrus through the use of the file role
customtask
, which is used in the custom task's package.xml.
Here is an example from the <contents> of a package.xml:
... <contents> <dir name="data"> <file name="mytask.xml" role="customtask"/> <file name="someotherdata.csv" role="data"/> </dir> </contents> ...
2009-06-16
Pyrus defines three kinds of plugins: commands, file roles, and file tasks. Commands add new commands to a frontend for Pyrus, file roles and file tasks add new features to package.xml.
Pyrus, by necessity, requires a clear separation between its plugins and its core. This is because Pyrus is distributed as a phar archive, which cannot be modified without jumping through several hoops. For this reason, plugins are installed in a special directory whose location is specified by the plugins_dir user configuration variable.
Pyrus also makes a clear separation between regular packages and plugin
packages. Plugins must be installed separate from any non-plugin packages using the
--plugin
option of the install,
upgrade, and
uninstall command. Here is
an example, installing the developer tools plugin:
php pyrus.phar install -p PEAR2_Pyrus_Developer
Pyrus defines a plugin as any package that contains one of the three
file roles that identify a plugin: customcommand
,
customrole
, or customtask
. In addition,
a plugin package can only contain one plugin. This helps users by mapping
one package to one plugin so that when they list plugin packages, it clearly
shows all of the plugins that are installed.
A plugin's definition is handled by an XML file, which must be referenced using one of the three plugin file roles in package.xml. Here is an example identifying a custom command in package.xml:
... <dir name="/"> <dir name="customcommand" baseinstalldir="/"> <file role="customcommand" name="commands.xml"/> </dir> ...
Pyrus relies upon PHP5's autoloading capabilities to automatically load a plugin
class. All plugins should include the <autoloadpath>
element to specify
a path relative to the php_dir
location for the plugin registry. For plugin classes that conform to PEAR2
standards, the autoloadpath should be an empty string:
<autoloadpath></autoloadpath>
The pyrus autoloader will automatically replace _
and
the namespace separator \
with DIRECTORY_SEPARATOR
and append .php
to determine the class name to load. To
instruct Pyrus to prepend a particular relative path, put this path in the
<autoloadpath>
element. As an example, the following
XML will prompt Pyrus to load the file
/home/user/.pear/MyPackage/customcommands/Command/Line/Obj.php
if the user's plugins_dir
is /home/user/.pear
:
<autoloadpath>MyPackage/customcommands</autoloadpath> <class>Command_Line\Obj</class>
The <class>
element is used by Pyrus to determine
which object to instantiate. If <class>
is:
<class>FooBar_Willy\Dilly</class>
Pyrus will instantiate an object of class FooBar_Willy\Dilly
.
Custom commands add new functionality to pyrus.phar. An example of a
plugin that implements custom commands is the
PEAR2_Pyrus_Developer
package, which implements the
make,
package,
pickle, and
run-phpt commands.
Custom command plugins can implement multiple commands, and are defined by
an xml file that is noted in package.xml with the customcommand
file role. The XML format is defined and validated by pyrus with the
customcommand.xsd
XSchema file.
Commands can have arguments and options. Here is an example of a command with a single argument:
php pyrus.phar install PackageName
The command is install
, and the argument is
PackageName
.
Here is an example of a command with multiple arguments:
php pyrus.phar install PackageName package.xml http://example.com/Foo.tgz
The command is install
, and the arguments are
PackageName
, package.xml
and
http://example.com/Foo.tgz
.
An option is a special argument that is preceded by 1-2 dashes (-
or
--
). Short arguments are single letters preceded by a dash,
and long arguments are words preceded by two dashes. Here is an example
of a command with both a short and a long option
php pyrus.phar package -p --tar
The command is package
, the short option is
-p
and the long option is --tar
. Short
options are aliases for long options. For the package
command,
the -p
short
option is an alias to the --phar
long option.
Options can also accept arguments in 2 formats. If an option accepts an argument, there are two ways of passing that argument. Short options consider the next argument to be their argument:
php pyrus.phar install -r /path/to/packagingroot PackageName
/path/to/packagingroot
is the argument to the short
option -r
, PackageName
is the argument
to the install
command.
Long options require an =
sign in between the option and
the argument as in:
php pyrus.phar install --packagingroot=/path/to/packagingroot PackageName
/path/to/packagingroot
is the argument to the long
option --packagingroot
, PackageName
is the argument
to the install
command.
Here is a human-readable overview of a custom command's XML definition file.
Optional tags are enclosed in [brackets]. If there is a choice of
tags, they are separated by a vertical line |
and
enclosed in parentheses like (<this>|<example>)
.
<?xml version="1.0" encoding="UTF-8"?> <commands version="2.0" xmlns="http://pear2.php.net/dtd/customcommand-2.0"> <command> <name>commandname</name> <class>CommandClass\Name</class> <function>makePackageXml</function> [ <webfunction>webCommand</webfunction>] [ <gtkfunction>gtkCommand</gtkfunction>] [ <autoloadpath>CommandClass</autoloadpath>] <summary>Short description of command purpose</summary> <shortcut>CN</shortcut> <options> [<option> <name>optionname</name> <shortopt>O</shortopt> <type>(<bool/>|<string/>|<int/>|<float/>|<counter/>| <callback>optionProcessorCallback</callback>| <set><value>value1</value><value>value2</value>...</set>)</type> [<default>defaultvalue</default>] <doc>Short description of option purpose</doc> </option>] </options> <arguments> [<argument> <name>argname</name> <multiple>0</multiple> <optional>1</optional> <doc>Short argument description</doc> </argument>] </arguments> <doc> Long description of command usage for help output </doc> </command> </commands>
A command need not require or accept any arguments or options, and can accept multiple arguments and multiple options. In addition, a custom command definition XML file may declare multiple commands.
Commands can also implement a shortcut, which is a shorter alias. The
upgrade
command, for instance, has a shortcut of
up
.
By convention, all command names of external projects should be prefixed with
the vendor name. For instance, if the vendor is Zend Framework, commands should be
prefixed with something like zf-
or with zend-
.
An install command would be zf-install
.
In addition, all shortcuts from external vendors should be upper-cased and
consist of the first letter of the vendor, and the first two letters
of the command. zf-install
would have a shortcut of
Zin
. This helps to avoid name collisions between
vendors.
This is the same method used for all plugins, and is documented here.
The three frontend command handlers are discussed in the CLI section, Web section, and the Gtk section.
Pyrus recognizes both optional and required arguments, as well as the ability to specify repeating arguments. The logic is very similar to function signatures in PHP. Each argument is placed by name into an associative array and passed to the function. Note that invalid input is not passed to custom commands, so a command can assume valid input if it is called.
Here is a sample argument definition and the way that Pyrus will handle different valid inputs for the hypothetical foo command:
<arguments> <argument> <name>argname</name> <multiple>0</multiple> <optional>0</optional> <doc>required arg</doc> </argument> <argument> <name>argname2</name> <multiple>0</multiple> <optional>1</optional> <doc>optional arg</doc> </argument> <argument> <name>argname3</name> <multiple>1</multiple> <optional>1</optional> <doc>optional arg, multiple</doc> </argument> </arguments>
php pyrus.phar foo arg1
<?php
function foo($frontend, $args, $options)
{
// $args = array('argname' => 'arg1')
}
?>
php pyrus.phar foo arg1 arg2
<?php
function foo($frontend, $args, $options)
{
// $args = array('argname' => 'arg1', 'argname2' => 'arg2')
}
?>
php pyrus.phar foo arg1 arg2 arg3
<?php
function foo($frontend, $args, $options)
{
// $args = array('argname' => 'arg1', 'argname2' => 'arg2', 'argname3' => array('arg3'))
}
?>
php pyrus.phar foo arg1 arg2 arg3 arg4
<?php
function foo($frontend, $args, $options)
{
// $args = array('argname' => 'arg1', 'argname2' => 'arg2', 'argname3' => array('arg3', 'arg4'))
}
?>
Options must define a short and a long option, a type for the option, and a short description of the option for help text.
Options fall into two categories, those that accept arguments, and those that don't.
Options that don't accept arguments are of type <bool/>
and <counter/>
.
The types recognized are:
<bool/>
<counter/>
<string/>
<int/>
<float/>
<callback></callback>
<set></set>
A <bool/>
option sets its value to TRUE if
present, and FALSE if not.
<options> <option> <name>nocompatible</name> <shortopt>n</shortopt> <type><bool/></type> <doc>Do not generate package_compatible.xml</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('nocompatible' => false)
}
?>
php pyrus.phar foo -n
<?php
function foo($frontend, $args, $options)
{
// $options = array('nocompatible' => true)
}
?>
php pyrus.phar foo --nocompatible
<?php
function foo($frontend, $args, $options)
{
// $options = array('nocompatible' => true)
}
?>
A <counter/>
option sets its value to 0
if not present, and to the number of times the option is present if it is.
This is the only option type for which multiple occurrences of the option
are allowed.
<options> <option> <name>verbose</name> <shortopt>v</shortopt> <type><counter/></type> <doc>How verbose to be with output</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('verbose' => 0)
}
?>
php pyrus.phar foo -v
<?php
function foo($frontend, $args, $options)
{
// $options = array('verbose' => 1)
}
?>
php pyrus.phar foo --verbose -vv --verbose
<?php
function foo($frontend, $args, $options)
{
// $options = array('verbose' => 4)
}
?>
A <string/>
option sets its value to the
argument passed in if present, or to NULL if not present.
<options> <option> <name>name</name> <shortopt>n</shortopt> <type><string/></type> <doc>Name of foo</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('name' => null)
}
?>
php pyrus.phar foo -n myname
<?php
function foo($frontend, $args, $options)
{
// $options = array('name' => 'myname')
}
?>
php pyrus.phar foo --name=myname
<?php
function foo($frontend, $args, $options)
{
// $options = array('name' => 'myname')
}
?>
An <int/>
option sets its value to the
integer argument passed in if present, or to NULL if not present.
<options> <option> <name>namecount</name> <shortopt>n</shortopt> <type><int/></type> <doc>Number of foo names</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('namecount' => null)
}
?>
php pyrus.phar foo -n 3
<?php
function foo($frontend, $args, $options)
{
// $options = array('namecount' => 3)
}
?>
php pyrus.phar foo --namecount=3
<?php
function foo($frontend, $args, $options)
{
// $options = array('namecount' => 3)
}
?>
A <float/>
option sets its value to the
float argument passed in if present, or to NULL if not present.
<options> <option> <name>foopercent</name> <shortopt>f</shortopt> <type><float/></type> <doc>Foo percent in decimal notation</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('foopercent' => null)
}
?>
php pyrus.phar foo -f .2
<?php
function foo($frontend, $args, $options)
{
// $options = array('foopercent' => 0.2)
}
?>
php pyrus.phar foo --foopercent=0.3
<?php
function foo($frontend, $args, $options)
{
// $options = array('foopercent' => 0.3)
}
?>
A <callback/>
option calls the specified
callback with the value passed in by the user as a string.
The callback must be a static method in the same class that
implements the command.
<options> <option> <name>foocallback</name> <shortopt>f</shortopt> <type><callback>processfoo</callback></type> <doc>Foo date</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('foocallback' => null)
}
static function processfoo($value)
{
// $value = null
return null;
}
?>
php pyrus.phar foo -f 2009-12-31
<?php
function foo($frontend, $args, $options)
{
// $options = array('foocallback' => new DateTime('2009-12-31', new DateTimeZone('UTC')))
}
static function processfoo($value)
{
// $value = '2009-12-31';
$date = new DateTime($value, new DateTimeZone('UTC'));
return $date;
}
?>
php pyrus.phar foo --foocallback=2009-12-31
<?php
function foo($frontend, $args, $options)
{
// $options = array('foocallback' => new DateTime('2009-12-31', new DateTimeZone('UTC')))
}
static function processfoo($value)
{
// $value = '2009-12-31';
$date = new DateTime($value, new DateTimeZone('UTC'));
return $date;
}
?>
A <set/>
option sets its value to NULL if not
present. Otherwise it ensures that the value passed in by the user is within
a pre-defined acceptable list of values.
<options> <option> <name>numbah</name> <shortopt>n</shortopt> <type> <set> <value>one</value> <value>two</value> <value>three</value> <value>four</value> </set> </type> <doc>Numbers less than five</doc> </option> </options>
php pyrus.phar foo
<?php
function foo($frontend, $args, $options)
{
// $options = array('numbah' => null)
}
?>
php pyrus.phar foo -n four
<?php
function foo($frontend, $args, $options)
{
// $options = array('numbah' => 'four')
}
?>
php pyrus.phar foo --numbah=one
<?php
function foo($frontend, $args, $options)
{
// $options = array('numbah' => 'one')
}
?>
A class must implement a command-line handler, which should have this method signature:
<?php
function commandhandler($frontend, $args, $options)
{
// perform command here
}
?>
The first argument is the CLI frontend, and can be used for asking the user
a question with the ask() method, or passing control
to a built-in command. $args
is an associative array of
arguments. Only required arguments are guaranteed to be present, all optional
arguments must be verified as present before using. $options
is an associative array of options. All options will be present, those not present
will be initialized to NULL.
Options should be accessed using their long names, and arguments using their names (see examples in the documentation sections for arguments and options above).
The ask() method accepts 3 arguments:
string $question
the question to ask the user. It should end in a question mark
(optional) array $choices
an array of possible answers
(optional) string $default
the default answer
Although a Web frontend has not yet been written for Pyrus, the XML command
format supports the eventual addition of a Web frontend. A separate method
should be used for the Web handling, and is named via the
<webfunction>
tag.
Although a GTK frontend has not yet been written for Pyrus, the XML command
format supports the eventual addition of a GTK frontend. A separate method
should be used for the GTK handling, and is named via the
<gtkfunction>
tag.
If you are not familiar with how file roles install, read the
introduction to file roles
first. This segment of the documentation assumes you are familiar with
concepts such as baseinstalldir
and with what a file
role means in terms of installation location.
Custom file roles can implement a single role, and are defined by
an xml file that is noted in package.xml with the customrole
file role. The XML format is defined and validated by pyrus with the
customrole-2.0.xsd
XSchema file.
Here is a human-readable custom file role xml file definition.
Optional elements are enclosed in [brackets], and if there is a choice of
values, they are delimited with a vertical bar |
and
enclosed in (parentheses).
<?xml version="1.0" encoding="UTF-8"?> <role version="2.0" xmlns="http://pear2.php.net/dtd/customrole-2.0"> <name>rolename</name> <class>MyPlugin_Rolename</class> [<validationmethod>validate</validationmethod>] [<autoloadpath>relative/path/to/MyPlugin</autoloadpath>] (<releasetype>php</releasetype>|<releasetype>extsrc</releasetype>| <releasetype>extbin</releasetype>|<releasetype>zendextsrc</releasetype>| <releasetype>zendextbin</releasetype>) <installable>(1|0)</installable> <locationconfig>config_var</locationconfig> <honorsbaseinstall>(1|0)</honorsbaseinstall> <unusualbaseinstall>(1|0)</unusualbaseinstall> <executable>(1|0)</executable> [<configvar> <name>config_name</name> <type>string</type> <default> <![CDATA[ <?php $default = 'hi'; ?> ]]> </default> <doc>Extensive, multi-line documentation</doc> [<validset>val1</validset><validset>val2</validset>...] <prompt>summary documentation</prompt> <group>Custom</group> <configtype>(system|user|channel-specific)</configtype> </configvar>]* </role>
This is the same method used for all plugins, and is documented here.
Some roles benefit from the ability to validate the files they represent
at packaging time. For instance, a customcommand
file
is checked to ensure it conforms to its XML schema, so that a custom command
with invalid XML cannot be distributed by mistake.
By convention, a validation method should be named validate
,
although the xml supports any name. The method name should be placed in
the <validationmethod
tag, and the method itself should
have the same method signature as this example from Pyrus's
customrole
validation:
<?php
function validate(\PEAR2\Pyrus\IPackage $package, array $file)
{
$parser = new \PEAR2\Pyrus\XMLParser;
$schemapath = \PEAR2\Pyrus\Main::getDataPath();
if (!file_exists(\PEAR2\Pyrus\Main::getDataPath() . '/customrole-2.0.xsd')) {
$schemapath = realpath(__DIR__ . '/../../../../data');
}
$taskschema = $schemapath . '/customrole-2.0.xsd';
try {
$taskinfo = $parser->parse($package->getFilePath($file['attribs']['name']), $taskschema);
} catch (\Exception $e) {
throw new \PEAR2\Pyrus\Installer\Role\Exception('Invalid custom role definition file,' .
' file does not conform to the schema', $e);
}
}
?>
A custom file role package-time validator should throw a
PEAR2_Pyrus_Installer_Role_Exception
exception object
with an error message describing the reason for validation failure, or simply
return on success.
The <releasetype>
element is used to tell Pyrus
which releases can contain a file with this role. For instance, the
src
role only has meaning in a PHP extension source release,
and is only allowed in extsrc
and zendextsrc
release types.
There are 5 release types that Pyrus recognizes:
php
- this is for PEAR-style packages distributing PHP code
extsrc
- this is for PHP extension source packages
extbin
- this is for PHP extension binary packages
zendextsrc
- this is for PHP Zend extension source packages
zendextbin
- this is for PHP Zend extension binary packages
If set to 0
, the file will not actually be installed onto
the system. Most file roles should set this to 1
.
This element is used to tell Pyrus which configuration variable should be used to determine where to install the file. This can be a custom configuration variable.
The <honorsbaseinstall>
element tells Pyrus to
honor the baseinstalldir
attribute (documentation on
baseinstalldir is here)
for roles like php
.
The <unusualbaseinstall>
element tells Pyrus to
honor the baseinstalldir
attribute for roles like
data
that also prepend channel/package name to the
installation location.
If <executable>
is set to 1
,
Pyrus will make the installed file an executable file on unix systems
(the equivalent of
chmod +x filename
).
A custom file role can optionally define a single or multiple custom
configuration variables. There are 3 kinds of configuration variables
that Pyrus supports, system
, user
and
channel-specific
. Documentation on configuration is
available here. Read that before
continuing in order to understand how different configuration variables work.
Pyrus supports one type of configuration variable: string
.
The values can be restricted to a specific set of values with the
<validset>
element.
If you are not familiar with how file tasks work, read the introduction to file tasks first.
Custom file tasks can implement a single task, and are defined by
an xml file that is noted in package.xml with the customtask
file role. The XML format is defined and validated by pyrus with the
customtask-2.0.xsd
XSchema file.
Here is a human-readable custom file task xml file definition. Optional elements are enclosed in [brackets].
<?xml version="1.0" encoding="UTF-8"?> <task version="2.0" xmlns="http://pear2.php.net/dtd/customtask-2.0"> <name>taskname</name> <class>MyPlugin_Taskname</class> [<autoloadpath>relative/path/to/MyPlugin</autoloadpath>] </task>
This is the same method used for all plugins, and is documented here.
A custom file task must extend the \PEAR2\Pyrus\Task\Common
class, and must implement a few methods:
Optionally, __construct() may be used to do special pre-processing, storing of information or other setup tasks.
A task must also define two class constants, TYPE
and PHASE. TYPE should be one of
'simple'
or
'script'
. 'script'
tasks are
post-install scripts, and are executed by the
run-scripts command.
'simple'
tasks are executed while installation is
in progress.
PHASE should be one of:
\PEAR2\Pyrus\Task\Common::POSTINSTALL is only used by
post-install scripts, the other 3 are used to determine at what time the
task should be called. If your custom task only works at installation-time,
use \PEAR2\Pyrus\Task\Common::INSTALL. If the task can
perform only at package time (this is exceedingly rare), use
\PEAR2\Pyrus\Task\Common::PACKAGE. Most tasks should use
\PEAR2\Pyrus\Task\Common::PACKAGEANDINSTALL. If your task
can perform its task at packaging time, then you should override the
isPreProcessed() method and return true. Some tasks, such
as the replace
task, perform some of their actions at
package time, and the rest at install time. The isPreProcessed()
method should only return true if the XML of the specific task could be
processed at package time.
The startSession() method is used to actually execute the
task, and is passed a read/write file resource that is set to the beginning
of the file being installed. The full path of the file's final installation
location is also passed in, and can be used for throwing exceptions. All
errors should be handled by throwing a PEAR2\Pyrus\Task\Exception
or one of its descendants.
The validateXml() method should validate the XML as
represented in an array format. The array uses associative arrays for tags,
the attribs
index for attributes, and for a tag with both
attributes and content, the _content
index contains the
content. If present, the tasks:
namespace is removed
from the tags prior to passing to validateXml(). On
encountering validation errors, the method should throw one of the four
exceptions in the example below.
<?php
class MyPlugin_Taskname extends \PEAR2\Pyrus\Task\Common
{
const TYPE = 'simple';
const PHASE = \PEAR2\Pyrus\Task\Common::PACKAGEANDINSTALL;
/**
* Initialize a task instance with the parameters
* @param array raw, parsed xml
* @param array attributes from the <file> tag containing this task
* @param string|null last installed version of this package
*/
function __construct($pkg, $phase, $xml, $attribs, $lastversion)
{
parent::__construct($pkg, $phase, $xml, $attribs, $lastversion);
// do any special parsing of attributes/contents here
// phase is one of \PEAR2\Pyrus\Task\Common::PACKAGE,
// \PEAR2\Pyrus\Task\Common::INSTALL, or
// \PEAR2\Pyrus\Task\Common::POSTINSTALL
}
/**
* Validate the basic contents of a <taskname> tag
* @param PEAR_Pyrus_IPackageFile
* @param array
* @param array the entire parsed <file> tag
* @param string the filename of the package.xml
* @throws \PEAR2\Pyrus\Task\Exception\InvalidTask
* @throws \PEAR2\Pyrus\Task\Exception\NoAttributes
* @throws \PEAR2\Pyrus\Task\Exception\MissingAttribute
* @throws \PEAR2\Pyrus\Task\Exception\WrongAttributeValue
*/
static function validateXml(PEAR2\Pyrus\IPackage $pkg, $xml, $fileXml, $file)
{
if (!isset($xml['attribs'])) {
throw new \PEAR2\Pyrus\Task\Exception\NoAttributes('taskname', $file);
}
if (!isset($xml['attribs']['attributename'])) {
throw new \PEAR2\Pyrus\Task\Exception\MissingAttribute('taskname',
$attrib, $file);
}
if ($xml['attribs']['attributename'] != 'hi'
&& $xml['attribs']['attributename'] != 'bye') {
throw new \PEAR2\Pyrus\Task\Exception\WrongAttributeValue('taskname',
'attributename',
$xml['attribs']['attributename'],
$file,
array('hi', 'bye'));
}
}
if (MyPlugin_Other_Class::somecondition()) {
throw new \PEAR2\Pyrus\Task\Exception\InvalidTask(
'general validation errors should use this exception');
}
return true;
}
/**
* Perform the taskname task
* @param \PEAR2\Pyrus\IPackage
* @param resource open file pointer, set to the beginning of the file
* @param string the eventual final file location (informational only)
* @return string|false
*/
function startSession($fp, $dest)
{
// use \PEAR2\Pyrus\Logger::log() to send messages to the user
\PEAR2\Pyrus\Logger::log(3, 'doing stuff');
\PEAR2\Pyrus\Logger::log(0, 'doing more important stuff');
// operate directly on the file pointer
$contents = stream_get_contents($fp);
$contents .= 'hi';
rewind($fp);
ftruncate($fp, 0);
fwrite($fp, $contents);
return true;
}
/**
* This is used to tell the installer to skip a task that has already been
* executed.
*
* For example, package-info replacement tasks are performed at packaging
* time, and do not need to be re-applied on installation
* @return bool true if task has already been executed on the file at
* packaging time
*/
function isPreProcessed()
{
// do not implement this method unless your task does its processing
// at package-time!
return true;
}
}
?>
2009-06-16
How to use Pyrus to develop packages for installation by Pyrus. This documentation is a work in progress.
Pyrus is particularly well-suited to maintaining a PEAR2 package, and can be used to easily create a package.xml with the make command. Once the requisite files are in place for this command, creating a release is as simple as
php -dphar.readonly=0 pyrus.phar make -pphar,tar,tgz,zip
In addition, Pyrus provides a simple facility for creating a new PEAR2 package, the generate-pear2 command. With this command, a skeleton directory and file setup is created that can be immediately used to get started with a new package.
Using Pyrus to manage your existing PEAR package is simple, although not as easy as managing a PEAR2 package or PECL extension. Generally speaking, the command you will take advantage of the most is the package command, which can be used to create the release needed.
As a PECL developer, managing your extensions could not be simpler with
Pyrus. The new pickle
command makes creating a package.xml as simple as creating a few text files
and then running the command. In addition, the PECL packages created
with the pickle
command are installable by the most
widely installed version of PEAR as well as by the newest versions of PEAR
and by Pyrus.
Pyrus's pickle
command fully eliminates the need to ever
even look at package.xml, greatly simplifying the lives of PECL extension
developers.
In addition, the run-phpt command can be used to run all the extension's phpt test files.
If you are creating a brand new extension, you should use the generate-ext command to create the basic structure of your extension.
After creating your extension, edit the newly-created CREDITS
file to have the correct information:
;; extensionname
My Name (myhandle) <myemail@php.net> (lead)
Add any additional maintainers using the same format.
Next, edit the README
file that describes your package.
The first line should be a brief summary of what the extension does, and the
rest of the file should be a detailed description of information the user
should know about the origin of the extension, and perhaps a simple example
of its usage.
Finally, edit the file named RELEASE-0.1.0
. Put any
release notes in here. Generally initial release
is
enough information for the first release.
Last, you can optionally create a file named API-0.1.0
.
Pyrus and the PEAR Installer differentiate between API version and
release version, to allow clearly defining an API change or backwards
compatibility break. Put information on any API changes into
API-0.1.0
.
After creating these files, simply run:
php pyrus.phar pickle extensionname
where extensionname
is the name of your extension. If
you have not yet installed the developer tools, Pyrus will ask if you would
like to download and install them. Choose yes
and then
re-run the command.
Pyrus will create a package.xml and a release called
extensionname-0.1.0.tgz
, or
extensionname-0.1.0.tar
if the zlib
extension is not enabled. This release can be uploaded to pecl.php.net
for immediate distribution.
Note that the directory structure described in pickle command documentation is required for pickle to detect documentation, tests, or data files.
To prepare an existing PECL package for Pyrus, you should create all of the
information files described in the section
Creating a new PECL Package with Pyrus,
but instead of creating RELEASE-0.1.0
and
API-0.1.0
, you would create a file named
RELEASE-X.Y.Z
where X.Y.Z
is the
version of the extension. So if your extension's version is 1.2.3
you would create a file named RELEASE-1.2.3
. For each
of the files above, rather than putting new content, you can copy/paste the
existing description, summary and release notes from your existing package.xml.
Note that any entities such as <
should be
converted to their values <
, as Pyrus escapes automatically
from the text files.
Note that the directory structure described in pickle command documentation is required for pickle to detect documentation, tests, or data files.
When the time rolls around for the next release, readying for this is also
simple. If you are about to release version 0.2.0
, create
a file named RELEASE-0.2.0
and put the release notes
into the file. Pyrus will automatically detect that 0.2.0
is newer than 0.1.0
and create a new release with those notes.
2009-07-08
If you are doing advanced work with Pyrus, it is important to understand how to access its internals through its public API. This guide documents how to perform common tasks such as accessing components of a package.xml, managing installation tasks, and accessing configuration settings.
The section on developing plugins for Pyrus is a good starting point for understanding how to extend Pyrus, this guide is more specific to using the actual classes contained within Pyrus.
If you are not familiar with the package.xml format, you will need to read The description of the file.
This guide is divided into several segments.
If you want to access basic properties such as the name of the package, the channel, summary, description, license, version, stability, date, time, or notes, read the section on Accessing basic properties
To learn about how to manage maintainers of the package, read the section on Accessing package maintainers.
To learn about how to manage and iterate over dependencies, read the section on Accessing dependencies.
To understand how to iterate over and add files to both the contents and release sections of package.xml, read the section on Accessing files
To learn about managing custom tasks, roles, and compatible packages, read the section on usesrole, usestask and compatible.
To learn about accessing PECL-specific properties such as configureoption, srcpackage, srcuri and providesextension, read the section on Accessing PECL properties
The package.xml file is represented using a PEAR2\Pyrus\PackageFile\v2
object, but most of the time you will access it using the most packagefile
version-agnostic PEAR2\Pyrus\Package
class, which supports
the same API seamlessly for accessing any kind of package as well as a few
higher level options such as directly accessing the source of a file in a package.
This documentation assumes you are working with a PEAR2\Pyrus\Package
object named $package
.
To instantiate a PEAR2\Pyrus\Package
, simply pass in the
path to a local package, a URI of a remote package, or an abstract remote package
name to the constructor.
<?php
$package = new PEAR2\Pyrus\Package('package.xml');
$package = new PEAR2\Pyrus\Package('Release-1.2.3.tgz');
$package = new PEAR2\Pyrus\Package('http://example.com/Release-1.2.3.tgz');
$package = new PEAR2\Pyrus\Package('pear2/Release-1.2.3');
?>
Basic properties can be accessed as if they were object properties:
<?php
echo "Package name is ", $package->name, "\n";
echo "Package channel is ", $package->channel, "\n";
echo "Package summary is ", $package->summary, "\n";
echo "Package description is ", $package->description, "\n";
echo "Package license name is ", $package->license['name'], "\n";
if ($package->license['uri']) {
echo "Package license URI is ", $package->license['uri'], "\n";
}
if ($package->license['filesource']) {
echo "Package license path within the package.xml is ", $package->license['filesource'], "\n";
}
echo "Package release version is ", $package->version['release'], "\n";
echo "Package API version is ", $package->version['api'], "\n";
echo "Package release stability is ", $package->stability['release'], "\n";
echo "Package API stability is ", $package->stability['api'], "\n";
echo "Package release date in YYYY-MM-DD format is ", $package->date, "\n";
if ($package->time) {
echo "Package release time in HH:MM:SS format is ", $package->time, "\n";
}
echo "Package release notes is ", $package->notes, "\n";
?>
By the same token, changing these properties is as simple as setting an object property:
<?php
$package->name = 'MyPackage';
// this must be full channel, not a shortcut like "pear2"
$package->channel = 'pear2.php.net';
$package->summary = "My package's short description";
$package->description = "My package's multi-line description
of how things are done with it.";
$package->license = 'BSD License';
// or
$package->license['name'] = 'BSD License';
$package->license['uri'] = 'http://www.opensource.org/licenses/bsd-license.php';
$package->license['filesource'] = 'LICENSE';
$package->version['release'] = '1.2.3';
$package->version['api'] = '1.0.0';
$package->stability['release'] = 'stable';
$package->version['api'] = 'stable';
$package->date = '2009-12-25';
$package->time = '00:00:01';
$package->notes = "This version of MyPackage has the blessing of the pope, so
it's way better than the last one.";
?>
Maintainers are accessed through the maintainer
property. An of maintainers organized by maintainer role can be obtained via
the allmaintainers
property, but it is also possible to
iterate over the maintainer
property to iterate over all
maintainers.
The maintainer
property acts as an array organized by
developer handle. To learn more about maintainers in package.xml, see
the concepts section on maintainers
in the user guide.
An individual maintainer's properties are set via a fluent method call. The order of method call is not important, but a call to the role() method is required in order to properly save the data.
<?php
// setting a maintainer's properties
$package->maintainer['cellog']
->role('lead')
->name('Gregory Beaver')
->email('cellog@php.net')
->active('yes');
// access a maintainer's properties
echo "Maintainer cellog's name is ",
$package->maintainer['cellog']->name, "\n"; // Gregory Beaver
echo "Maintainer cellog's role is ",
$package->maintainer['cellog']->role, "\n"; // lead
echo "Maintainer cellog's handle is ",
$package->maintainer['cellog']->user, "\n"; // cellog
echo "Maintainer cellog's email is ",
$package->maintainer['cellog']->email, "\n"; // cellog@php.net
echo "Is maintainer cellog active? ",
$package->maintainer['cellog']->active, "\n"; // yes
?>
The maintainers can be iterated over as well, and even modified:
<?php
foreach ($package->maintainer as $handle => $maintainer) {
echo "Maintainer $handle's name is ",
$maintainer->name, "\n";
echo "Maintainer $handle's role is ",
$maintainer->role, "\n";
echo "Maintainer $handle's handle is ",
$maintainer->user, "\n";
echo "Maintainer $handle's email is ",
$maintainer->email, "\n";
echo "Is maintainer $handle active? ",
$maintainer->active, "\n";
$maintainer->active('no'); // shows modifying a setting in iteration
}
?>
The allmaintainers
property returns an associative
array of arrays of maintainer objects indexed by maintainer role.
Before it is possible to understand how Pyrus provides access to dependencies, it is necessary to understand their format in package.xml, which is documented here.
Pyrus provides access to dependencies through the dependencies
property. In addition to supporting access to and creation of individual
dependencies, it is possible to iterate over all dependencies, or over just
required dependencies, or just a particular dependency type.
Each of the dependency types (package
, subpackage
,
pearinstaller
, extension
, php
,
arch
, and os
) is accessed slightly
differently based on how they are used in package.xml.
Perhaps the best introduction is 3 examples highlighting the different methods of accessing dependency properties. First, an example highlighting the methods for reading existing dependency properties:
<?php
echo "First, required/optional dependencies:\n";
// these same principles apply to all properties for dependencies.
// isset() is used to test presence, ->get to retrieve.
echo "Minimum PHP version supported: ",
$package->dependencies['required']->php->min, "\n";
echo "Minimum PEAR Installer/Pyrus version required: ",
$package->dependencies['required']->pearinstaller->min, "\n";
if (isset($package->dependencies['required']->pearinstaller->max)) {
echo "Maximum PEAR Installer/Pyrus version supported: ",
$package->dependencies['required']->pearinstaller->max, "\n";
}
echo "PHP extension ext is required? "
isset($package->dependencies['required']->extension['ext'])
? "Yes\n" : "No\n";
if (isset($package->dependencies['required']->os['windows'])) {
echo "Conflicts with Windows operating system? "
$package->dependencies['required']->os['windows']->conflicts
? "Yes\n" : "No\n";
}
if (isset($package->dependencies['required']->arch['*386*'])) {
echo "Requires i386 processor? "
$package->dependencies['required']->arch['*386*']->conflicts
? "No\n" : "Yes\n";
}
echo "Now for package and subpackage dependencies:\n";
echo "Is package foo from channel gronk.example.com required? ",
isset($package->dependencies['required']->package['gronk.example.com/foo'])
? "Yes\n" : "No\n";
echo "Does this package conflict with our package? ",
$package->dependencies['required']->package['gronk.example.com/foo']->conflicts
? "Yes\n" : "No\n";
echo "gronk.example.com/foo2 minimum version required is ",
$package->dependencies['required']->subpackage['gronk.example.com/foo2']->min, "\n";
echo "gronk.example.com/foo2 incompatible versions:";
foreach ($package->dependencies['required']->subpackage['gronk.example.com/foo2']->exclude
as $version) {
echo "\n ", $version;
}
echo "\n";
echo "Optional dependencies are accessed the same way with optional index\n";
echo "Optional dependency gronk.example.com/foo3 minimum version allowed is ",
$package->dependencies['optional']->subpackage['gronk.example.com/foo3']->min, "\n";
echo "Dependency groups are accessed with the group index\n";
echo "Dependency group foo hint is ",
$package->dependencies['group']->foo->hint, "\n";
echo "channel pear2.php.net package Foo package dependency in dependency group",
" foo minimum version is ",
$package->dependecies['group']->foo->package['pear2.php.net/Foo']->min, "\n";
// and so on...
?>
Iteration is similar to the access, in that each dependency object is the same object as that when accessed using the full access syntax:
<?php
// display all required and optional package and subpackage dependencies
foreach (array('required', 'optional') as $required) {
foreach (array('package', 'subpackage') as $packagedep) {
foreach ($package->dependencies[$required]->$packagedep as $dep) {
echo $required, ' ', $packagedep, ' ', dependency,
$dep->channel, '/', $dep->name;
if (isset($dep->uri)) {
// the channel is "__uri" for all URI dependencies
echo "\nPackage URI: ", $dep->uri;
}
if (isset($dep->min)) {
echo "\nMinimum version: ", $dep->min;
}
if (isset($dep->recommended)) {
echo "\nRecommended installation version: ", $dep->recommended;
}
echo "\n";
}
}
}
// iterating over dependency groups and their contents
foreach ($package->dependencies['group'] as $group) {
echo "Optional dependency group ", $group->name,
" (", $group->hint, ")\n";
foreach ($group->extension as $ext) {
echo "Extension ", $ext->name;
}
foreach (array('package', 'subpackage') as $type) {
foreach ($group->$type as $dep) {
echo ucfirst($type), ' ', $dep->channel, '/', $dep->name;
}
}
}
?>
Finally, setting dependencies can be accomplished in several ways.
<?php
// setting minimum PHP version required:
$package->dependencies['required']->php = '5.3.0';
$package->dependencies['required']->php->min = '5.3.0';
$package->dependencies['required']->php->min('5.3.0');
// setting minimum PEAR Installer/Pyrus version required:
$package->dependencies['required']->pearinstaller = '2.0.0a1';
$package->dependencies['required']->pearinstaller->min = '2.0.0a1';
$package->dependencies['required']->pearinstaller->min('2.0.0a1');
// setting excluded versions (all deps supporting the <exclude> tag use this syntax):
$package->dependencies['required']->php->exclude = '5.2.0';
$package->dependencies['required']->php->exclude('5.2.0')->exclude('5.2.10');
$package->dependencies['required']->php->exclude('5.2.0', '5.2.10');
// setting OS/architecture requirements:
$package->dependencies['required']->os['windows'] = true; // Windows required
$package->dependencies['required']->arch['386'] = false; // Conflicts with 386 architecture
// saying "this package must be installed" to the installer
$package->dependencies['required']->package['pear2.php.net/Packagename']->save();
// saying "this package must not be installed"
$package->dependencies['required']->package['pear2.php.net/Packagename']->conflicts();
// setting up complex versioning requirements
$package->dependencies['optional']->package['pear2.php.net/Packagename']
->min('1.2.0')
->max('1.3.0')
->recommended('1.2.3')
->exclude('1.3.0', '1.2.2');
// depending on a PECL package
$package->dependencies['required']->package['pecl.php.net/lua']
->providesextension('lua');
// optionally depending on a core PHP extension
$package->dependencies['optional']->extension['PDO']->save();
// setting up a dependency group "remotefeatures"
$package->dependencies['group']->remotefeatures->hint('Remote management capabilities');
// add dependencies to the group
$package->dependencies['group']->remotefeatures
->package['pear2.php.net/Foo']->save();
$package->dependencies['group']->remotefeatures
->extension['curl']->save();
?>
There are two reasons to access the files within a package.xml
Pyrus supports several use cases for iterating over package.xml:
All of these use cases are illustrated in the following example:
<?php
// *************** installation iteration ***************
foreach ($package->installcontents as $file) {
// retrieve actual file path to be used with fopen
$path = $package->getFilePath($file->packagedname);
// retrieve open file pointer
$fp = $package->getFileContents($file->packagedname, true);
// retrieve file contents as string
$contents = $package->getFileContents($file->packagedname);
// get file properties (XML attributes of the <file> tag)
$role = $file->role;
if ($file->md5sum) {
$md5sum = $file->md5sum;
}
// get relative path of file after applying all modifications such
// as <install name="" as=""/>
$relativepath = $file->name;
// get the path as specified in the <contents> <file> tag
// This is the same as $file->name if no <install as> tags exist
$originalpath = $file->packagedname;
// get the tasks associated with this file
$tasks = file->tasks;
// iterate over the tasks
$lastversion = '1.0.0'; // last version of $package that was installed, or null
foreach (new \PEAR2\Pyrus\Package\Creator\TaskIterator($tasks, $package,
\PEAR2\Pyrus\Task\Common::INSTALL, $lastversion)
as $name => $task) {
// $name is the task name, $task is the actual task object, which can be
// processed with $task->startSession(resource $fp, '/full/final/install/path');
}
}
// *************** packaging iteration ***************
$packagingloc = '/tmp/example';
foreach ($package->packagingcontents as $packageat => $info) {
// $packageat is the location in which the file will reside in package.xml,
// which is often the same relative path it will end up being installed into
// $info is an array representing the actual file XML
$name = $info['attribs']['name'];
// this retrieves an open file pointer to the file in order to process it
$contents = $package->getFileContents($info['attribs']['name'], true);
if (!file_exists(dirname($packagingloc . DIRECTORY_SEPARATOR . $packageat))) {
mkdir(dirname($packagingloc . DIRECTORY_SEPARATOR . $packageat), 0777, true);
}
$fp = fopen($packagingloc . DIRECTORY_SEPARATOR . $packageat, 'wb+');
ftruncate($fp, 0);
// copy from the source directory to the packaging location
stream_copy_to_stream($contents, $fp);
fclose($contents);
rewind($fp);
$lastversion = '1.0.0'; // last version of $package that was installed, or null
foreach (new \PEAR2\Pyrus\Package\Creator\TaskIterator($info, $package,
\PEAR2\Pyrus\Task\Common::PACKAGE,
$lastversion) as $task) {
// do pre-processing of file contents
try {
$task->startSession($fp, $packageat);
} catch (\Exception $e) {
// handle task processing problems here
}
}
fclose($fp);
}
// *************** traversing deep tree iteration ***************
foreach ($package->contents as $file) {
// retrieve actual file path to be used with fopen
$path = $package->getFilePath($file->name);
// retrieve open file pointer
$fp = $package->getFileContents($file->name, true);
// retrieve file contents as string
$contents = $package->getFileContents($file->name);
// get file properties (XML attributes of the <file> tag)
$role = $file->role;
if ($file->md5sum) {
$md5sum = $file->md5sum;
}
// unlike installcontents, <install as> tags are not applied, so
// $file->name == $file->packagedname
$relativepath = $file->name;
}
// *************** general purpose iteration ***************
foreach ($package->files as $relativepath => $infoarray) {
// $relativepath is the same as $file->name in the other examples
// $infoarray is the same array returned by the packagingcontents
// iterator
}
// *************** retrieving post-install scripts ***************
foreach ($package->scriptfiles as $file) {
// $file is the same object returned by the installcontents iterator
}
?>
Finally, adding files to package.xml is done by directly setting an
index in the files
property. Properties are
manipulated with the setFileAttribute() method.
<?php
$package->files['path/to/file.php'] = array(
'role' => 'php'
);
$package->setFileAttribute('path/to/file.php', 'md5sum', md5('hi'));
// adding a task to a file
$replace = $package->getTask('path/to/file.php', 'replace');
$replace->setReplacement('package-info', '@API-VER@', 'api-version');
$package->setFileAttribute('path/to/file.php', 'tasks:replace', $replace);
?>
Releases are documented here, you should familiarize yourself with this before continuing.
Release properties can be accessed with the following syntax:
<?php
// setting release type
$package->type = 'php';
$package->type = 'extsrc';
$package->type = 'zendextsrc';
$package->type = 'bin';
$package->type = 'zendextbin';
$package->type = 'bundle';
// for types other than bundle
$package->release[0]->installconditions['php']->min('5.2.0');
// defaults to "min"
$package->release[0]->installconditions['php'] = '5.2.0';
// defaults to "pattern"
$package->release[0]->installconditions['arch'] = 'i386';
$package->release[0]->installconditions['arch']->pattern('i386')->conflicts();
// defaults to "name"
$package->release[0]->installconditions['os'] = 'windows';
// defaults to existing
$package->release[0]->installconditions['extension'][0]->name('PDO');
$package->release[0]->installconditions['extension'][0]->name('PDO')->min('1.0');
$package->release[0]->ignore('path/to/file.ext');
$package->release[0]->installAs('path/to/anotherfile.ext', 'new/name.php');
// add another release
$i = count($package->release);
$package->release[$i]->ignore('path/to/anotherfile.ext');
$package->release[$i]->installAs('path/to/file.ext', 'new/name.php');
// remove release
unset($package->release[1]);
// remove all releases
$package->release = null;
// retrieve the release that will be used on this system
$release = $package->installrelease;
?>
Accessing PECL properties is relatively simple, here is an example illustrating them:
<?php
$package->type = 'extsrc'; // extension source release
$package->configureoption['foothing']->prompt = 'Value for thing';
$package->configureoption['with-zlib']->prompt('Support zlib compression?')->default('yes');
$package->providesextension = 'foo';
$package->type = 'extbin'; // extension binary package release
$package->srcpackage = 'pecl.php.net/foo';
// for packages that are binary releases of URI-based packages
$package->srcuri = 'http://example.com/foo-1.2.3';
?>
When declaring that a package.xml uses a custom role or task, this code should be used:
<?php
$package->files['example/file.php'] = array(
'role' => 'myrole',
'tasks:mytask' => '',
);
$package->usesrole['myrole']->package('MyRole')->channel('mypear.example.com');
$package->usestask['mytask']->package('MyTask')->channel('mypear.example.com');
// for uri-based packages:
$package->usesrole['myrole']->uri('http://my.example.com/MyRole-1.2.3');
?>
If you are unfamiliar with the <compatible>
tag,
you should first read the documentation on its use
here. Creating
and accessing compatible tags in Pyrus is as easy as:
<?php
$package->compatible['pear.php.net/Archive_Tar']
->min('1.2')
->max('1.3.0')
->exclude('1.2.1', '1.2.2');
// remove a compatibility declaration
unset($package->compatible['pear.php.net/Archive_Tar']);
// test for existence of compatible declaration
isset($package->compatible['pear.php.net/Archive_Tar']);
// display info:
echo $package->compatible['pear.php.net/Archive_Tar']->min;
// iterating over compatible packages
foreach ($package->compatible as $package => $info) {
echo "Package :", $package, "\n";
echo "min: ", $info->min, "\n";
echo "max: ", $info->max, "\n";
if (isset($info->exclude)) {
echo "Excludes versions ",
foreach ($info->exclude as $version) {
echo ' ', $version, "\n";
}
}
}
?>
This documentation describes how to use Pyrus's public class API to access configuration values. Before reading further, you should be familiar with the documentation on Pyrus's configuration.
Pyrus's configuration is controlled by the PEAR2\Pyrus\Config
class, which is implemented as a multiton mapping Pyrus installation location
to a configuration object. In addition, the current configuration (most
recently instantiated) is also accessible to create configuration-agnostic
functionality.
Configuration variables are accessed by referring to them as class
properties. Here is an example requesting the doc_dir
configuration variable, and setting the test_dir
configuration variable:
<?php
$docdir = PEAR2\Pyrus\Config::current()->doc_dir;
PEAR2\Pyrus\Config::current()->test_dir = '/path/to/tests';
?>
The user configuration in use is also accessible from each configuration
in the same manner as the installation-specific configuration. Here is
an example requesting preferred_state
and setting
verbose
:
<?php
$pref = PEAR2\Pyrus\Config::current()->preferred_state;
PEAR2\Pyrus\Config::current()->verbose = 3;
?>
In addition to the configuration variables, Pyrus also defines a few other values that are available, and cannot be used as the names of custom configuration variables:
path
- the complete PATH_SEPARATOR
separated list of cascading directories this configuration represents.
location
- the writable pyrus installation this configuration
represents
registry
- the registry corresponding to the configuration
pluginregistry
- the registry corresponding to the
location that plugins are installed
channelregistry
- the channel registry corresponding to
the configuration
systemvars
- an array of names of system configuration
variables, built-in and custom
uservars
- an array of names of user configuration
variables, built-in and custom
channelvars
- an array of channel-specific configuration
variables, built-in and custom
mainsystemvars
- an array of names of built-in
system variables
mainuservars
- an array of names of built-in user
variables
mainchannelvars
- an array of names of built-in
channel-specific variables
userfile
- path to the user configuration file in use
by this configuration.
customsystemvars
- an array of names of custom
system variables
customuservars
- an array of names of custom user
variables
customchannelvars
- an array of names of custom
channel-specific variables
Pyrus provides a very simple API for performing installation tasks. This
API begins with the ability to pass any packagename that can be
specified on the command-line to a PEAR2\Pyrus\Package
object:
<?php
// examples of the range of valid package names
$package = new \PEAR2\Pyrus\Package('package.xml');
$package = new \PEAR2\Pyrus\Package('/full/path/to/package.xml');
$package = new \PEAR2\Pyrus\Package('Package-1.2.3.tgz');
$package = new \PEAR2\Pyrus\Package('/full/path/to/Package-1.2.3.zip');
$package = new \PEAR2\Pyrus\Package('RemotePackage');
$package = new \PEAR2\Pyrus\Package('RemotePackage-alpha');
$package = new \PEAR2\Pyrus\Package('RemotePackage-1.2.3');
$package = new \PEAR2\Pyrus\Package('channelname/RemotePackage');
$package = new \PEAR2\Pyrus\Package('http://example.com/RemotePackage-1.2.3.phar');
?>
If there is a problem with the package name as passed to the constructor,
an exception is thrown. This can be any of a wide variety of exceptions
ranging from a PEAR2\Pyrus\PackageFile\Exception
for
invalid package.xml, a PEAR2\Pyrus\Package\Exception
for
higher-level errors (file does not exist, invalid abstract package name),
a PEAR2\Pyrus\Package\InstalledException
if an abstract
remote package was requested and a newer version is installed, and
a PEAR2\Pyrus\Channel\Exception
if any problems with
retrieving remote REST information occur. Also possible are
PEAR2\Pyrus\Package\Phar\Exception
for errors relating
to local tar
, tgz
, zip
or phar
archives.
Once you have a valid package object, installation is very simple. Pyrus conducts all installation activities within a transaction, meaning that all changes are applied nearly simultaneously, and any failure mid-transaction does not leave an invalid installation lying around.
<?php
// import the class names into the current scope
// this step is optional, you can also use the full class names
// like PEAR2\Pyrus\Installer::begin()
use PEAR2\Pyrus\Installer as Installer,
PEAR2\Pyrus\Package as Package;
try {
$p1 = new Package('package.xml');
$p2 = new Package('Package.tgz');
$p3 = new Package('pear2/RemotePackage');
// here is the meat of the installation transaction
Installer::begin();
Installer::prepare($p1);
Installer::prepare($p2);
Installer::prepare($p3);
Installer::commit();
} catch (\Exception $e) {
echo "Install failed\n";
}
?>
Uninstalling a package is even simpler:
<?php
// import the class names into the current scope
// this step is optional, you can also use the full class names
// like PEAR2\Pyrus\Uninstaller::begin()
use PEAR2\Pyrus\Uninstaller as Uninstaller;
try {
Uninstaller::begin();
Uninstaller::prepare('pear2.php.net/Package1');
Uninstaller::prepare('pear.php.net/Package');
Uninstaller::prepare('__uri/Package');
Uninstaller::commit();
} catch (\Exception $e) {
echo "Uninstall failed\n";
}
?>
Pyrus provides a very simple API for accessing its registry. Pyrus stores
meta-information on installed packages in redundant registries. There are
three kinds of registries that Pyrus recognizes, Sqlite3
,
Xml
and the legacy Pear1
registry. A
pyrus-based installation can have up to all three kinds of registries
redundantly storing the installed packages and the known channels. By default
the Sqlite3
registry is the primary registry used for
querying information, with the Xml
registry as a backup.
When Pyrus is used to manage an installation, it checks to see which registries are already present, if any, and will use the existing registries. This fact can be used to provide more flexible installation options. For instance, Pyrus can be used to manage an existing legacy PEAR installation without any special configuration, it will simply detect that the legacy registry is present and use it. If a package is extracted into a bundled location, Pyrus will detect its extracted package.xml as belonging to the Xml registry, and will use only that registry for installation purposes, which allows upgrading the extracted package and avoids placing any absolute paths into the installation.
Pyrus provides full atomic installation transactions for all of its registry types, including the legacy Pear1 registry, unlike the PEAR installer. In addition, each registry provides a single method which can be used to remove it from disk, and there is also a single method which can be used for converting from one registry type to another. Another method is available for repairing a corrupted registry from one of its redundant registries.
Pyrus provides a separate logical registry for storing channels from the registry that stores packages. Each registry handles this slightly differently. The Sqlite3 registry, for instance, stores all information in a single database. The Xml registry stores information in separate files, like the legacy Pear1 registry.
All registry classes implement the PEAR2\Pyrus\IChannel
interface, and all channelregistry classes implement the
PEAR2\Pyrus\ChannelRegistry
interface. The
PEAR2\Pyrus\Registry
class acts as an aggregator of
underlying registries, and implements the ability to cascade to parent
registries, as does the PEAR2\Pyrus\ChannelRegistry
class.
The simplest way to retrieve a registry object is to use the one strongly tied to the PEAR2\Pyrus\Config object:
<?php
$reg = PEAR2\Pyrus\Config::current()->registry;
$creg = PEAR2\Pyrus\Config::current()->channelregistry;
?>
Accessing a specific installed package retrieves an object that is
API-identical to a PackageFile
object. The registry is implemented logically as an associative array.
By requesting a package's logical name, which is channel/packagename
,
we get an object that can be manipulated just as if it were the package prior
to installation
<?php
$package = PEAR2\Pyrus\Config::current()->registry->package['pear2.php.net/PEAR2_Pyrus_Developer'];
$remotepackage = new PEAR2\Pyrus\Package('pear2.php.net/PEAR2_Pyrus_Developer');
// both packages can be queried with the same API
?>
The same principle applies to channels:
<?php
$channel = PEAR2\Pyrus\Config::current()->channelregistry['pear2.php.net'];
$localchannel = new PEAR2\Pyrus\ChannelFile('channel.xml');
// both channels can be queried with the same API
?>
Iteration also works with both just as it would for an array:
<?php
foreach (PEAR2\Pyrus\Config::current()->registry->package as $name => $package) {
// $name is channel/package
// $package is a packagefile object
}
foreach (PEAR2\Pyrus\Config::current()->channelregistry as $name => $channel) {
// $name is the channel name
// $channel is a channelfile object
}
?>
There are 4 installation-related methods, as well as 3 transaction methods. These methods are:
The install() method registers a package as installed,
and sets its date/time to the current time so that the installation time
can be tracked. The replace() method registers a package
as installed, but does not modify its date/time. This is useful for
repairing a corrupted entry, or simply storing a package as it is. Both
methods accept a PEAR2\Pyrus\IPackageFile
object. A
packagefile object can be retrieved from a PEAR2\Pyrus\Package
object by calling its getPackageFileObject() method.
A PEAR2\Pyrus\Registry\Exception
is thrown on any errors.
The uninstall() method accepts two parameters, the
name of the package, and the package's channel. A
PEAR2\Pyrus\Registry\Exception
is thrown on any errors.
The exists() method also accepts two parameters, and
returns TRUE or FALSE depending on whether the package exists. If
severe errors occur such as registry corruption, a
PEAR2\Pyrus\Registry\Exception
object is thrown.
Note that array access can also be used to handle installation-related tasks:
<?php
$reg = PEAR2\Pyrus\Config::current()->registry;
$package = new PEAR2\Pyrus\Package('/path/to/package.xml');
// equivalent to $reg->install($package)
$reg->package[] = $package;
// equivalent to $reg->uninstall('Foo', 'pear2.php.net')
unset($reg->package['pear2.php.net/Foo']);
// equivalent to $reg->exists('Foo', 'pear2.php.net');
isset($reg->package['pear2.php.net/Foo']);
?>
When performing any installation or uninstallation task, it is recommended
to use the registry's built-in transaction support. The
Sqlite3
registry uses the database's native transaction
support. Both the Xml
and Pear1
registries use Pyrus's PEAR2\Pyrus\AtomicFileTransaction
for its transaction support. Thus, it is always best to do a transaction
by first enabling the registry transaction, and then the atomic file transaction
within this registry transaction:
<?php
$reg = PEAR2\Pyrus\Config::current()->registry;
$package = new PEAR2\Pyrus\Package('Whatever');
try {
$reg->begin();
PEAR2\Pyrus\AtomicFileTransaction::begin();
$reg->install($package);
PEAR2\Pyrus\AtomicFileTransaction::commit();
$reg->commit();
} catch (Exception $e) {
$reg->rollback();
PEAR2\Pyrus\AtomicFileTransaction::rollback();
throw $e;
}
?>
If using the Installer API, the transactions and installation to registry is all automatic, this code is only needed for customizing installation.
Other methods for querying the registry include:
The info() method provides a way of peeking at
a single attribute of a package. When used with the Sqlite3
registry, it is extremely efficient both in terms of memory use and speed.
Both the Xml
and Pear1
registries are
far slower because they must load the complete packagefile into memory for
every query. For these registries, it is better to simply retrieve a
packagefile and query it using the
PackageFile API.
Parameters to info() are the package name, package channel, and the field name to retrieve.
All of the
Basic package.xml properties
can be directly accessed using info(). In addition, two
special properties, installedfiles
and dirtree
are available.
installedfiles
returns a list of files and their properties
as they have been installed. Here is a sample return value:
<?php
array(
'/full/path/todocs/PEAR2_SimpleChannelServer/pear2.php.net/examples/update_channel.php' =>
array(
'role' => 'doc',
'name' => 'examples/update_channel.php',
'installed_as' => '/full/path/to/docs/PEAR2_SimpleChannelServer/pear2.php.net/examples/update_channel.php',
'relativepath' => 'PEAR2_SimpleChannelServer/pear2.php.net/examples/update_channel.php',
'configpath' => '/full/path/to/docs',
),
// ... and so on
);
?>
dirtree
returns a list of every directory that would have
been created if installing the package in a new installation. This can
be used to prune empty directories after uninstalling. Here is a sample
return value:
<?php
array (
'/full/path/to/php/PEAR2/SimpleChannelServer/REST',
'/full/path/to/php/PEAR2/SimpleChannelServer/Categories',
'/full/path/to/php/PEAR2/SimpleChannelServer',
'/full/path/to/php/PEAR2',
'/full/path/to/php',
'/full/path/to/docs/PEAR2_SimpleChannelServer/pear2.php.net/examples',
'/full/path/to/docs/PEAR2_SimpleChannelServer/pear2.php.net',
'/full/path/to/docs/PEAR2_SimpleChannelServer',
'/full/path/to/docs',
'/full/path/to/bin',
);
?>
This method accepts a channel name as an argument, and returns an array of the names of installed packages from that channel.
getDependentPackages() requires a single argument,
a PEAR2\Pyrus\IPackageFile
object.
This method returns an array of PEAR2\Pyrus\Package
objects representing installed packages that depend upon the package
passed in. If the optional second boolean parameter is set to true
(which it is by default), performance is improved when querying an
Sqlite3
database by returning packages containing only
the name of the package and its dependencies.
This method is used to implement file conflict detection to prevent
overwriting installed files with those from another package. It accepts a
single argument, a PEAR2\Pyrus\IPackageFile
object.
The Pear1
registry is the most efficient at this
operation (at the expense of drastically decreased efficiency at installation or
uninstallation), the Sqlite3
is the next most
efficient, and the Xml
registry is the least efficient,
and in fact is so inefficient, this method should only be called
on an Xml registry that is for a very small installation.
This static method accepts a string containing the path to check for registries,
and returns an array containing the names of registries
found. The possible return values include Sqlite3
,
Xml
and Pear1
. Note that only a call
to PEAR2\Pyrus\Registry::detectRegistries() will return
a list of all registries found. A call to
PEAR2\Pyrus\Registry\Sqlite3::detectRegistries() will
only return either array()
or
array('Sqlite3')
depending on whether the registry exists.
This static method accepts a string containing the path to remove a registry from. A call to PEAR2\Pyrus\Registry::removeRegistry() will completely remove all traces of a PEAR installation. A call to an individual registry's removeRegistry, such as a call to PEAR2\Pyrus\Registry\Pear1::removeRegistry() will only remove that registry from the installation path.
The PEAR Manifest
2007-03-14
PEAR is dedicated to Malin Bakken, born 1999-11-21.
PEAR is short for "PHP Extension and Application Repository" and is pronounced just like the fruit. The purpose of PEAR is to provide:
PEAR is a community-driven project governed by its developers. PEAR's governing bodies are subdivided into the PEAR Group, Collectives, and a President. PEAR's constitution (adopted in March 2007) defining these groups is documented here. The PEAR project was founded in 1999 by Stig S. Bakken and quite a lot of people have joined the project.
PEAR's mission is to provide reusable components, lead innovation in PHP, provide best practices for PHP development and educate developers.
The code in PEAR is partitioned in "packages". Each package is a separate project with its own development team, version number, release cycle, documentation and a defined relation to other packages (including dependencies). Packages are distributed as gzipped tar files with a description file inside, and installed on your local system using the PEAR installer.
Packages may relate to each other through explicit dependencies, but there is no automatic dependency relationship between packages based on the package name. For example, "HTTP_Post" is by default independent of "HTTP". Dependencies between packages with similar names is not forbidden, and does happen. As an example,the "DB_DataObject" package depends on the "DB" package.
A style guide, the PEAR Coding Standards (short PCS), exists to ease collaboration between PEAR developers, to enforce quality, and to enforce a consistent visual appearance of all source code distributed as a PEAR package.
All PEAR packages are registered in and downloaded from a central server at pear.php.net. Other third-party servers called "channels" also distribute packages that can be installed by the PEAR Installer, see the Channels list for more information. pear.php.net does not endorse the packages from these channels, and only provides support for packages distributed from pear.php.net.
Pear.php.net provides both a human-friendly (HTML) and
machine-friendly (currently REST
)
interface to the packages available from pear.php.net.
All communication occurs over the HTTP protocol.
Other functions the pear.php.net site provides are:
Packages are distributed as a gzipped tar file with an XML description file inside. The description file (package.xml) contains some information about the package, a list of files and their roles, and dependencies.
PECL
PECL (pronounced "pickle") is a separate project
that distributes PHP extensions (compiled code written in C, such
as the PDO extension).
PECL extensions are also distributed as packages and can be installed
using the PEAR installer with the pecl
command.
More information and all PECL packages can now be found on the PECL homepage.
This chapter describes, how you can get support, if you have questions concerning PEAR.
As in most other open-source projects, the support is done via mailing lists. In our case, we currently have seven PEAR related mailing lists:
SVN commit list
Quality Assurance list
The first four lists are intended to help you, if you have questions. Please read below to get to know, what list fits to your needs. The SVN commit list is intended for people that are contributing to PEAR: All commits to our revision control system (SVN) are sent to this list. The core and QA lists are intended for people that want to take part in the development of PEAR.
You can subscribe to the PEAR mailing lists via this website.
The PEAR general mailing list is the mailing list, where you ask, if you have a question about installing PEAR, using a certain PEAR package etc.
The list is not a support forum for questions concerning the usage of PHP. Please use php-general@lists.php.net instead.
As the name implies, this list is where the developers of PEAR come together to make decisions, to discuss code and to handle things that are related to PEAR.
If you are not a developer of PEAR itself, this list is of no real interest to you, unless
The address for this list is pear-dev@lists.php.net.
This list is the place where all the things concerning the PEAR documentation, the manual etc. are discussed.
If you want to help out documenting PEAR packages don't hesitate to drop a mail there.
People interested in improving the overall quality of PEAR come together on the QA list.
The Quality Assurance team is always seeking people that want to help. If you are interested, just sign up and announce yourself on the mailing list.
If you have found a problem on our website or have a question concerning the site, you can email the guys on pear-webmaster@lists.php.net .
This list is the place, where the core infrastructure of PEAR is discussed. Members of this list also come from other PHP projects such as PECL and PHP QA, since the PEAR infrastructure is also relevant to these projects (like the PEAR installer, the PEAR website etc.).
Specifically the list is concerned with the following topics:
There are a number of web sites that deal with PEAR. We have compiled a list of them here. If a site is missing, you can email the webmaster.
The PEAR project has its own IRC channel #PEAR, which is available on EFnet. Come along and meet the gurus, which don't have to pay for their internet connection and can hang around in IRC the whole day.
2004-04-15
This chapter describes the different ways in which one could contribute to PEAR.
PEAR is an open source project, which means that everyone can help improving it. Improving doesn't always mean writing code. It can also mean reporting bugs, giving feedback, submitting feature requests and even financial support.
Explanations about submitting new packages can be found in the Developers' Guide.
If you have modified a package to expand its functionality or to fix a bug, you should contribute your changes back to the community (some licenses force you to do so, and it is generally considered immoral not to).
Before creating the patch, you must first obtain the latest sources of the package you wish to patch from SVN by running the commands (the package in this example is Foo_Bar):
svn checkout http://svn.php.net:/repository/pear/packages/Foo_Bar/trunk
Now that you have the latest sources, you can edit the relevant file(s). Make sure that your patch is fully compatible with the PEAR coding standards.
After you have finished adding/changing the code, TEST it: We will not accept code that hasn't been carefully tested. When you are absolutely sure the new code doesn't introduce bugs, create a unified diff by running:
cd pear/Foo_Bar
svn diff >Foo_Bar.diff
The resulting .diff file contains your patch. This diff makes it easy for us to see what has been changed.
The next step is to submit the patch. There are basically two options to do this: The first option is to submit the patch by creating a bug report for the package in question. You can do this by visiting the package homepage on pear.php.net and clicking on the "Bugs" tab at the top of the page. Another option is sending a mail to pear-dev@lists.php.net and Cc'ing the maintainer(s) of the package. The subject of the mail should be prefixed with '[Patch]' to make it clear you are submitting a patch. Also include a verbose explanation of what the patch does. Don't forget to attach the .diff file to the mail. The maintainers of the package are usually listed in the header of each source file. Apart from that their email addresses are available on the package information page on http://pear.php.net/.
If you are using Outlook or Outlook Express, please change the file extension of the diff file to .txt, because Outlook's MIME-Type detection depends on the file extension and attachments with a MIME-Type not equal to
text/plain
will be rejected by our mailinglist software.
If your patch does break backwards compatibility, the chances are fairly good that the maintainers won't be happy about it. Thus you should always try to fix a bug in a way that does not seriously change the public API. But if there is absolutely no way to keep backwards compatibility and/or if your patch contains a groundbreaking improvement, even API changes are fine.
If you think that you have found a bug in a PEAR package, please take care that you are using the latest version of the package and that your system does meet the packages' requirements.
If the bug still persists with the latest version of the package, don't hesitate to fill out a bug report. The easiest way is to click the link "Bugs" on the package information page for the package on the PEAR Homepage, which you think contains a bug. This will take you to a list of existing bugs of the package. Please double check if the bug hasn't already been reported! If you are unable to find it in the list, you can click on "Report new bug" to fill out the bug form.
More information and tips on how to report bugs in a proper way can be found at http://bugs.php.net/how-to-report.php.
If you have already fixed a bug that you have found in a package, please read this.
Good documentation is essential for users to fully understand any software. Several PEAR packages lack documentation or have docs which need improvement. Writing documentation provides more information about helping out on this front.
Translating documentation is another important task. Not only does new documentation need to be translated into the existing languages, additional languages are welcome. Also, existing translations need to be brought up to date because the English versions have been changed. Help on how to perform the translation process is in the Revision Tracking section of the manual.
Some of the PEAR developers have wishlists at Amazon or a similar service. If you appreciate the work of a certain developer, feel free to buy something for her from her wishlist. To find out if a developer has one of those wishlists, go to the account browser, look for the details of the developer and there you'll see if she has a wishlist. Buying something from people's wishlists may even speed up the time in which annoying bugs are fixed ;-).
This is described here.
answer by Greg Beaver
The package in question does have releases, but none that are stable. There are two solutions.
Set preferred_state to alpha or beta and then install
$ pear config-set preferred_state alpha
$ pear install Packagename
Find out the stability or version number of the latest release and install it directly.
$ pear install Packagename-alpha
$ pear install Packagename-1.5.3
You are using a PEAR version lower than 1.4.0.
To install the package, you have to update PEAR via:
$ pear upgrade PEAR
This will install the latest available version of PEAR which is capable of installing packages that have only a package.xml version 2.0.
If you have an old PEAR version installed (i.e. < 1.3.6), you need to use the following commands to install the latest available version of PEAR:
$ pear upgrade --force http://pear.php.net/get/PEAR-1.3.6.tgz http://pear.php.net/Archive_Tar-1.3.1.tgz http://pear.php.net/get/Console_Getopt-1.2.tgz
$ pear upgrade -a http://pear.php.net/get/PEAR-1.4.3.tgz
$ pear upgrade --force PEAR-1.4.11
$ pear upgrade PEAR
Even if not all packages do have end-user documentation in
the PEAR manual, nearly all of the packages do include
examples. The examples are automatically installed when
you install a package via the command line installer and
are located in $peardir/docs/$packagename/
.
You can find the example/documentation directory by executing $ pear config-get doc_dir on command line.
General questions about the usage of PEAR components should be posted to the mailing list pear-general@lists.php.net.
All technical discussions concerning the development of PEAR components should be posted to pear-dev@lists.php.net.
Question concerning the website can be sent to pear-webmaster@lists.php.net.
Information on subscribing this mailing lists can be found here.
On all mailing lists mentioned above the language is english and the way you ask questions should always be polite :-).
answer by Brett Bieber
All Mac OS X versions up until 10.5 come with PEAR by default. Mac OS X 10.5 (Leopard) users will need to install PEAR using the standard installation instructions.
If you are a OS X 10.4 or lower user, you may wish to upgrade to a newer version of PHP using a binary installable package such as Marc Liyanage's PHP packages. If you have installed another PHP package, you may have two versions of PHP and PEAR installed on your system. To be sure which version you are using, type which pear in your console to find out where PEAR is located, and pear -V to find out some info about the pear version, and also pear config-show to see the configuration details.
If you have not made any modifications to the default Max OS X install, the PEAR
version you are running is the /usr/bin/pear
which is
configured for the PHP 4 version distributed with OS X. If this is the case,
there are a couple options to use a PHP 5 version on your Mac.
If you're using Marc Liyanage's PHP package, you can specify the full path to your PHP5 PEAR installation. For example /usr/local/php5/bin/pear install {packagename} to install a package for your PHP 5 installation (installing/upgrading packages will probably require sudo).
Alternatively, you can change every path for the OS X provided PEAR using pear config-set. Remember to specify all paths necessary to point to your PHP 5 installation.
What I prefer to do, is remove the Apple supplied PHP 4 with
sudo rm /usr/bin/php /usr/bin/pear and then symbolically
link php and PEAR in /usr/bin
to the location where Marc
Liyanage's packages are installed:
sudo ln -s /usr/local/php5/bin/php /usr/bin/php &&
sudo ln -s /usr/local/php5/bin/pear /usr/bin/pear.
Keep in mind, Apple occasionally updates PHP on OSX, which will revert your
/usr/bin/php
to their distributed version. Pay attention
to software update, and read the information regarding updated software bundled
with OS X updates.
Note: It is uncertain if Apple will continue to distribute PHP version 4 for Mac OS X <= 10.4. So these instructions may change, and may differ for your specific version of Mac OS X. Always consult the Apple documentation to understand the default install provided before making modifications.
To make PEAR work on Windows, you simple need to add the PEAR
installation dir (e.g. c:\php\pear
) to the
include_path directive in your php.ini
.
Note: There are some classes (like Schedule/At.php), that do not work on Windows, because they use *nix specific commands.
Unlike other aspects of PEAR development, the windows build of PHP 4.3.x is not tracked in SVN. Instead, it is located on the machine that builds windows snapshots. Often, this will not be updated when the rest of PEAR is updated. Note that PHP 5.x releases use a different build system and are automatically updated to the latest versions of PEAR.
If you find that PHP 4.3.x has out-of-date versions of packages, or no longer works, then please report that the windows bundle of PEAR is out of date to pear-dev@lists.php.net
You are seeing the warnings because pear.php.net
uses a SSL key that is signed by CAcert,
whose root certificate is unfortunately not bundled with most
browsers.
If you are using a Mozilla browser, you can import the certificate on this site by clicking on the link "Root Certificate (PEM Format)". When asked if you want to trust the new Certificate Authority, you need to check at least the "Trust this CA to identify web sites." box and click "Ok".
People using Internet Explorer may find help here.
Mac OS X users must download the above mentioned PEM file. The certificate can then be imported with the "Keychain Access" utility via "Import" in the "File" menu.
To know that the folder has a customized view, Windows sets the "read-only" attribute to the folder. The "read-only" attribute is not actually used to control write access. You can create files in a "read-only" folder. http://support.microsoft.com/default.aspx?scid=kb;en-us;326549
The PEAR Installer detects the read-only attribute and refuses to install into these folders. Unfortunately, there is no way to distinguish between customized folders and actual read-only folders on Windows.
The work-around is to avoid installing PEAR packages into customized Windows folders.
If $ which pear gives you something like /usr/local/bin/pear
and $ pear -V shows a very old version or if you don't want
to use it for other reasons, you have to install PEAR in your home directory.
Go to http://pear.php.net/go-pear.phar, save as gopear.phar
and do
$ php go-pear.phar.
Use /home/user/pear
as prefix while installing PEAR, where user is your
username.
Once the installation is done, edit your PATH variable to include your planned new PEAR directory before the old one.
For example put a export PATH=/home/user/pear/bin:$PATH
at the end of your ~/.bashrc
or ~/.profile
under Linux. $ echo $PATH should give you now
/home/user/pear/bin:[...]
$ which pear should result in /home/user/pear/bin/pear
and $ pear -V something like "PEAR Version: 1.4.8"
(at the time of writing) or newer. Check the latest stable version at
http://pear.php.net/package/PEAR under Current Release.
Now all you need is to set your include_path correctly, most likely via
<?php
$path = ini_get('include_path');
ini_set('include_path', '/home/user/pear'.PATH_SEPARATOR.$path);
?>
It is very likely that you have defined two PEAR directories in your include_path, and that you have updated the package in the directory that is defined behind the first directory in your include_path. If you either remove the wrong directory from your include_path or change the order of the directories in your include_path, the error messages should not be shown anymore.
If you have followed the instructions for getting go-pear.phar and you are using e.g. Windows and the Internet Explorer, you might get the strange effect that -- although you have named the file go-pear.phar -- the file isn't parsed and the PHP source code of it is shown.
The problem is that e.g. the Internet Explorer saves the file as a HTML file with some HTML code around the PHP source code to display the code nicely.
To avoid this problem, you should use the source code view of your browser, e.g. by clicking with the right mouse key and selecting "Show source code" from the context menu. If you save now the file that is shown in your editor as go-pear.php, PHP will be able to parse the file properly.
Please note that the Windows versions of PHP have a file named go-pear.bat bundled. It is recommended to use this batch file on Windows instead of using go-pear.php.
This is described here.
The PECL project (a spin-off from PEAR) is the place where C extensions for PHP are published.
The first elements of PEAR were stored in the directory pear-core (once php-src/pear) and so they were bundled with each new PHP release.
For future releases of PEAR this isn't workable because PEAR will become too big to be bundled with every release of PHP. So there was a decision to commit all new elements to an own top level directory pear. The other elements which are currently remaining in php-src/pear will be moved to the new top level directory soon. Only the PEAR package manager code will be bundled with every new release automatically.
The directory structure of pear-core/
matches that of the install tree (by default
/usr/local/lib/php
), and are organized as a
simple hierarchy. However, the directory structure of
pear/
is based on what package the files
belong to. Where files are located in each package directory is
completely independent from where they end up being installed,
this is all determined by the package description file.
In SVN, PEAR code is organized by package instead of hierarchical like it would finally be installed. For example, if you want to use the XML_RPC class, you include "XML/RPC.php". It's easy to think that in SVN, this file should be in pear/XML/RPC.php, but that's not the case. XML_RPC is an independent package with its own directory in SVN, in this case the RPC.php file is actually located at pear/XML_RPC/RPC.php. The package description file (package.xml) is used to say where the files should finally be installed.
The reason SVN is organized this way is that it makes package administration much easier.
Yes. But make sure, that you mark up the experimental/unstable
status of the project in the documentation. The best place to do
this is in the package.xml
in the
<state>-tag.
The values for this tag are
Examples are a good idea, especially when your class has a complex API. But examples are only a part of a documentation. Don't create examples instead of a documentation. You should store the documentation and the examples in a sub directory 'docs' of the package directory.
Test scripts are recommended when your class has to be compiled, requires special extensions/programs or needs correctly installed additional files (i.e. templates, graphics). Store them in sub directory called 'tests'.
There is no problem with competitive packages, but we want to avoid pollution with 10 template classes, 7 different mail classes, and 3 different layers for databases doing the same only with different function names.
First of all, do a reality check: Why do I want to commit a new package? Really bad answers are "To see my name in PEAR" or "I didn't understand the API of the existing class".
A good reason for a new class is often, that you are missing a function, behaviour or speed in an existing implementation. In this case, you should take a look at this class, if it possible to extend this class. If not, then you have a good reason to commit a new class. "If not" means, it isn't possible to add the required functionality without changing the basics of this class.
If you write a new class, try to keep API compatibility with existing classes as far as possible! If it isn't possible to keep the class compatible himself, try to create a wrapper class to keep compatibility. Don't care, if this wrapper needs a lot of time or memory, a wrapper class only has to keep compatibility and make migration easier.
Committing a competitive class has to be announced on the PEAR developers mailinglist!
Yes. You should include every package, you need with require_once() or include_once (), also when it is included already by another package.
Currently PEAR supports the following list of licenses:
Other licenses may be added to this list on a case-by-case basis. Please get in touch with the PEAR developers mailing list for this.
The allowed licenses were chosen to allow integration into closed source, open source as well as commercial applications. The only limitation is that the original credits must stay in the sources. For LGPL the license additionally requires that all changes to the source code must be licensed under the LGPL to anyone the source is distributed to.
From time to time people raise concerns of using PEAR packages licensed under the PHP license in GPL'ed code. In a discussion about this topic, the creator of PHP, Rasmus Lerdorf, issued the following statement:
It all comes down to semantics of what linking means. The PHP license is pretty much identical to the Apache license and you could indeed make a case for not allowing any GPL'ed software to be "linked to" from Apache either.
See http://www.apache.org/licenses/LICENSE-1.1.
The PHP license was chosen to match the Apache license because Apache and PHP are tied so closely to each other.
This hair splitting over linking, derivation and aggregation has been going on since the beginning of time. My stance is that you can indeed ship PHP licensed PEAR components on the same cd or in the same tarball as GPL'ed code because I see it as an aggregate work. This changes if you take PEAR code, modify it and copy-paste it directly into your own work. Then it moves from aggregate to derived. But the intent of the PEAR components is to be used in aggregate form. The PHP license allows you to use it in derived form as well, of course, but then you should be choosing a license other than the GPL for the derived work.
The FSF has a FAQ on aggregation here: http://www.fsf.org/licensing/licenses/gpl-faq.html#MereAggregation
That text is heavily biased towards compiled software and they talk about executables and memory spaces which don't really apply in this case. If you don't consider using a PEAR component as aggregation then it logically follows that you also cannot have Apache call your code so you will have to stipulate that nobody can use your code from Apache. I think this is an extreme interpretation that pretty much nobody out there shares.
In short, I don't see an issue here. Move along.
For PECL extensions that are linked into PHP, the license must be compatible with the PHP license. That means you can not GPL a PECL extension or you would be violating the GPL. Note also that if you write an extension that links against a GPL'ed library you will be violating the GPL. If you need to link against a GPL'ed library, get permission from the author of the library to use the library under a compatible license.
The license of any PEAR/PECL package can be found in the head of
all source code files, inside the <license> tag of the package
description file (package.xml
) and also on the package
homepage.
Using spaces and avoiding tabs is the only way to ensure that a piece of code is rendered consistently in all editors and viewers. Many editors render tabs as 4 spaces, and a lot of editors, terminal programs and utilities render tabs as 8 spaces. Example:
printf("%s",
$arg);
Here, there are 7 spaces before "$arg". If this code was written in an editor with 4-space tabs, it would store it as one tab and three spaces. Now, if another developer edits the same file in an editor with 8-space tabs, it will look like this:
printf("%s",
$arg);
Likewise, consider this code written with 8-space tabs:
if ($foo &&
$bar) {
}
If viewed in a 4-space-tab editor, it will look like this:
if ($foo &&
$bar) {
}
In a community like PEAR where people use lots of different systems and editors, using tabs simply doesn't work. People will end up doing whitespace commits fixing rendering in their editor, while breaking it for others. With only spaces it will look the same to everyone.
Jamie Zawinski has written a piece on the subject too.
There is also a tool called Astyle which can help you convert your code to the appropriate style.
Most likely you forgot to ./configure again. This will make sure that new pages are detected and included.
Another reason could be that you did not add links in the parent page.
In en/package/
are the category xml files that contain
links to the package documentation files. Add the link to your new pages
there.
This also applies if you get a message stating
cannot generate system identifier for general entity
"package.networking.net-ldap.search"
.
configure
did not find the Docbook DSSSL or XSLT
stylesheets. Install them first.
If the style sheets are installed, ./configure will list the found directories like this:
checking for docbook.dsl... autodetected: /path/to/dsssl-stylesheets-x.yz
checking for docbook.xsl... autodetected: /path/to/xsl-stylesheets-x.y.z
Working on the translations is not just translating an English file and committing the results. Much of the work is needed to update the already translated ones, to get in sync with the content of the English files. To follow the modifications in the English tree, you should subscribe to the PEAR documentation mailing list to get SVN commit messages, or read the archives. If you never update your files, the translations can get useless.
Updating a foreign language file can get difficult, as you may not
know when and who translated that file, so you may not know where
you should look for the updates needed. We have one system
for tracking the revisions and modification dates of the files
in peardoc
.
Instead of storing all responsibilities in a central file, the revision
comment system stores them in the files they provide information about.
Information about translator, revision numbers, and status information
is stored in the revision comment.
Let's see what would be in the header of the example file
bookinfo.xml
file in this case:
<!-- EN-Revision: 1.16 Maintainer: jane Status: ready -->
We can see the revision number for the last english file used to update the translation (EN-Revision: 1.16), the translator cvs account name. But we can also add some other status information in the case it is needed (eg. "partial" for incomplete translations). This revision comment system is basically about storing the information in the XML files, and not in a central place. This is extremely convenient now, as there are more than 2400 files in the English tree.
Currently, all three fields (English revision, Maintainer, Status)
are needed. Maintainer is intended to be a SVN user name, or some
nickname without a space, status can be anything without a space.
Note, that this header is not updated by SVN (in contrast with
$Revision
, which is updated automatically).
This is only updated when you edit the contents of the comment
yourself.
You may see this as a good thing, but using these comments, you lose the quick look on the whole list of your files. No, you do not lose this, but get much more! If you would like to build a full list of you files, you can go to the /peardoc/ directory and run:
./scripts/revcheck_pear.php xx > revcheck.html
Here xx
is the imaginary language code.
After running this script you'll get a
revcheck.html
in the same directory.
You can find revision comparisons and links to diffs
for all the files in this language.
You can also add a further restriction parameter, the maintainer name,
so the script will only list the files corresponding to
the given maintainer.
There are some optional extensions introduced for this script,
to be available in this generated HTML page.
This is why the translation.xml
files are
introduced.
Here comes a simple translation.xml
file
for the imaginary xx language :
In this file, you can add users without a SVN account, and can
assign ready documents or work-in-progress files to them.
The biggest advantage of using this addon is that all this information
is used to generate dynamic tables of translators and files in
the revcheck.html
file.
All translators are linked to from the individual files they are assigned
to, and there is a nice table showing the state of files for
all the translators.
Assigning ready files may be needed if a time consuming update is
in progress on that file.
There are two optional parameters you can add to a <file>,
if you would like to record it: the date
and revision
.
Date is assumed to be the date when the work was started, revision
is the checked out revision of the English file used to start the work
(denoted as CO-Revision in the generated table).
There is currently no fixed format for the date
parameter.
Another addon to this system is just to give credit to all people
who worked on one file, and not just the current maintainer. To achieve
this goal, we added the credit comments. One credit comment in
eg. history.xml
may look like this (in case
Joe Doe translated the file initially, but Jane followed him to
be the maintainer):
<!-- CREDITS: joedoe -->
The credits comment can contain a comma-separated list. These
comments only affect the display of the translators table in
revcheck.html
.
The PEAR Constitution
2007-03-14
PEAR's constitution was adopted by majority vote of active PEAR developers on 2007-03-08, see the Official Results.
The following summary of the constitution is only intended to clarify the most important features of the constitution. The text starting with Constitution is the official Constitution of PEAR.
Most importantly, this document's main purpose is to enable developers to innovate in a supportive environment, to foster good will between developers, to encourage best practices and to provide a clear path for resolving disputes. PEAR is about bringing PHP developers together to provide great solutions to the problems we encounter every day through the PHP language, it is ideal if the constitution is ancillary to the main activity at pear.php.net: coding.
Developers have supreme power over ultimately what bugs/features will be assigned to specific roadmap versions, and to releasing package versions. Developers will have to cede some control over API decisions/QA/docs to the collective that contains their package. Developers may be expected to mentor a new developer and help introduce them to the systems within PEAR (how to package, document, test, etc.)
In other words, the bulk of the day-to-day power will remain with developers, but some of the QA decisions and individual ownership will be relinquished to the collective that contains the package.
Collectives have supreme power over packages within the collective in terms of API decisions, QA, documentation, defining the way collaboration works in the collective, choosing a collective leader or leaders, self-promotion of packages within the collective and assigning mentors to new developers of packages within the collective.
The PEAR Group only has power over issues that affect all of PEAR such as coding standards, SVN karma, and all major decisions made about PEAR as a whole. For example, licenses allowed and what defines a collective are two issues that only the PEAR Group can resolve.
The PEAR president has no power over any of the things above, except for the ability to veto a PEAR Group decision. The president cannot create policy. The president's main job is public relations, talking to people outside of PEAR like an ambassador, trying to recruit new developers or bring packages into PEAR, and to solve big emergencies in a hurry such as finding a new hosting provider should the donated space for pear.php.net disappear.
The Website Collective is a special collective, consisting of developers
with pear.admin
karma (Website Administrators), and developers
who contribute to and maintain the actual infrastructure code for pear.php.net.
pear-dev@lists.php.net (or php.pear.dev newsgroup) remain the official channels for all PEAR communications. Specific enhancements to the website may of course be created in the future to facilitate better communication.
One of the jobs of the PEAR Group is to issue administrative documents about diverse topics in PEAR. This chapter contains all of them.
This document was issued on 4th November 2003 and describes which directory structure must be used in PEAR packages. The goal of this document is to unify the directory naming in SVN and after installations.
Let's assume we have a package
The_Package_Name
that contains one or more
sub-classes (eg. The_Package_Name_Module
),
with some documentation (perhaps a README
,
copies of RFCs, etc.), a battery of test
scripts (unit tests, regression tests, etc.), and it uses some
data files (localization strings, etc.), the dir tree would look
like:
The_Package_Name |-- Name (contains Module.php) |-- data |-- docs | `-- examples |-- misc |-- scripts `-- tests
Name
refers to the last part of the
The_Package_Name
, all subclasses of the main
class, should be put in there or subdirectories of it. You can
refer to http://svn.php.net/viewvc/pear/packages/Cache_Lite/trunk/ - the
directory Lite
as an example (this basically
documents what we currently do anyway).
The data
and misc
directories are optional, because it will not make sense to have
them for every single package in PEAR.
The directories that are required are
docs/examples
and
tests
. A package may have no extra
documentation, but it should have at least one example. There
must also be some basics test to be able to verify that the
package is working. The preferred type of testing script system
to use is PHPUnit or .phpt. But for now we would be content with
any sort of test script.
Files in scripts
will be installed into a
directory available in $PATH
, such as
/usr/local/bin
.
Anything that does not fit any of the above categories is placed
into the misc
directory.
Maintainers are expected to modify their existing packages to match this new standard.
This document was issued on 02 April 2004 and was revised on 08 January 2006 when adding the MIT License. It lists the licenses that PEAR packages can be released under.
The PEAR Group would like to announce the following refinement of the current license FAQ entry.
Vote result: 5 (+1), 0 (-1), 3 (+0)
The current list allows a great number of licenses which vary greatly. This means that users may have to learn the in's and out's of a lot of licenses. Also some of the license choices impose comparatively high restrictions to the standard PHP license (GPL, QPL ..). As PEAR aims to extend the functionality provided by PHP users of PHP should fairly safely be able to also use any PEAR package without licensing worries, be it for commercial or non commercial, closed or opensource use.
Therefore with this announcement the license choices are reduced to the following short list:
Other licenses may be accepted on a case by case basis, but will have to fit the above criteria. This decision has been made to simplify the current situation, and as with all decisions is open to be refined in the future using the RFC proposal methodology.
All packages, which are already part of PEAR as of now, which use other licenses, do not need to follow this regulation.
In the meantime the FAQ entry linked above has also been updated to reflect the decisions from this document.
This document has been published on 14th November 2003 and describes how releases that break backwards compatibility (BC) have to be handled.
The goal is to make it possible to be able to run multiple major versions in one script.
For this reason new major versions (BC break or when the maintainer feels it makes sense as a result of dramatic feature additions or rewrites) require a new package using the following naming convention in the following order of preference (where 'Foo' is the package name and 'X' the major version number):
The choice should be made based on preventing current and future misleading or ambiguous names. This means good care should be taken in making the right choice for the package. Obviously the first two allow for some ambiguity (is DB2 a package for IBM's DB2 or just the major version 2 of DB? - is IPv4 a package for IPv4 or is it the 4th major release if IP?). They don't break the idea of "_" mapping to directories (the class DB_NestedSet implies that there is a nestedset.php in the DB dir). The last one prevents any ambiguity but it's the least visually pleasing and also breaks the '_' to directory mapping and is therefore the last choice.
We also came to the conclusion that the pear installer should not be clever about the relationship between two major releases aside from printing out notices about the fact that there is a newer major version when a user installs an earlier one. However all major versions of a package will be listed on one package home. This is especially important in order to not break tutorials that cover older major releases (tutorial xyz for major version 1 simply says 'pear install Foo' - if the system would then install 'Foo2' the user might be in for an unpleasant surprise).
Therefore, new major versions are for all intents and purposes new packages with the above mentioned exceptions. The names of these new packages are derived using one of the above mentioned naming conventions.
This document is based on the PEPr voting initiated by PEAR's Arnaud Limbourg. The purpose is to define when a package is orphaned and how to handle this.
Sometimes a package is left without care for a long time. It becomes lonely and full of bugs. This is a very sad situation and somebody needs to step up to remedy it.
The following are considered orphaned packages:
A QA core-team member tries to contact the current maintainers and developers (cc'ing the QA list) asking about an update about their work on the package.
A package lead answers:
A message will be sent to pear-dev asking for volunteers.
If you are willing to take over a package you must notify the QA list and state who you are and why you want to take over.
If the QA team feels the person who stepped up is appropriate and capable, the team will make this person a new maintainer. In case several persons step up a case by case study will be carried out to assign new maintainer-ship.
A QA core team member will mark the package as orphaned.
If a package is orphaned, a warning and a call for maintainers will be displayed on the main package page.
Descriptions of terms used on this page
Term | Definition |
---|---|
QA team | Refers to the whole QA team (core and mailing list members) |
QA core | Refers to the 6 elected QA team members |
This document is based on the PEPr voting initiated by PEAR's Tobias Schlitt. The purpose is to define what to do with orphaned PEPr proposals.
It happens more or less often that
In these two cases PEAR QA should step in and try to get in touch with the specific proposers. If the proposer is no longer interested in the proposal, PEAR QA should search for a new maintainer for the package or the proposal should be deleted.
For these steps the following time frames are proposed:
Finished proposals should not be deleted, but marked as orphan. This marker should have the following content:
[QA-ORPHAN]
.
$Date: 2008-10-09 15:16:18 $
If you are planning to contribute a new package to the PEAR project, this guide will provide you with the necessary information to get you started The Right Way (tm).
We will start by describing the cases in which it makes sense to contribute and in which not. After we have ensured that the package you want to contribute fits into PEAR, we will describe how to get started with the community. Once you are confident about the inner workings of the PEAR developer community, we will explain how to start the formal process of proposing a new package and we will give some tips for making your first package as successful as possible.
$Date: 2008-10-09 15:16:18 $
In this chapter the different ways of contributing to PEAR will be discussed. The most common way of contributing to PEAR is to open a bug or feature request at our Bug Tracker. You might also add a patch to an existing bug, or add a helpful note to the user manual for a package that you use regularly. If you wish to step in deeper, some packages are in need of better documentation, and you could mail suggestions in text or docbook format to pear-doc@lists.php.net. The next level of commitment to PEAR is offering to become a regular developer for a specific package that you use often. This is easily accomplished by emailing the current active developers and asking where to begin.
If you decide to take the full plunge and contribute a brand new package to PEAR, then you will need to fulfill a number of guidelines and requirements. If you are unsure if your package belongs in PEAR, contact the developers mailing list.
As you may have already noticed while browsing the list of existing packages, the PEAR packages provide a (often abstract) solution for a general problem. Thus your code will most likely fit into PEAR if it solves a problem that will not only occur in one (e.g. your) specific application, but that occurs in a lot of (web) applications. Examples are:
The PEAR packages that provide XML parsing functionalities can be found in the category browser.
No matter which area is covered by a package, the API should be as abstract as possible (while not becoming too complex), so that it can be utilized painlessly in as much use cases as possible.
There are two kinds of packages that are not likely to be accepted as new contributions to PEAR:
There are exceptions to the duplication rule, for instance a package that requires a higher PHP version, and takes advantage of newer versions in PHP will definitely be considered seriously. Also, packages that provide a completely new paradigm for accessing the same thing are a good candidate for consideration.
Frameworks or other applications may also be considered if they are developer tools, such as phpDocumentor. However, if you have a great idea that you think would fit into PEAR, don't let this document discourage you. PEAR developers can be surprisingly open to new ideas. Remember, the best ideas are well documented and carefully presented. Be prepared to have patience and thick skin! We are a picky bunch about quality, but in the end we will all learn a lot and develop some stellar code in the process.
As the parameters for proposing a package change, this site will be regularly updated.
$Date: 2008-10-09 15:16:18 $
Welcome to the PEAR community! By this point, you have decided to contribute some of your time and effort to improving the PEAR repository. Before you go any further, we'd like to introduce you to the common practices and communication channels that PEAR developers use in order to collaborate across geographical and national divides. Additionally we will tell you the different places where you can find more information about the inner workings and rules of PEAR.
Mailing lists are the most important way to communicate in PEAR. We are running a number of public mailing lists, which are listed on the PEAR website. For starters the PEAR developers mailing list pear-dev@lists.php.net is the primary place where discuss various aspects of their packages and of the PEAR repository as a whole. pear-general@lists.php.net is a general support list that should be used for questions on using PEAR packages, installing PEAR, and other issues not directly related to the development of PEAR packages.
If you would like to get started with maintaining packages in PEAR, you are expected to be subscribed to pear-dev@lists.php.net. (Subscription instructions)
During your first steps in the PEAR community, you may stumble across the term "pear-dev", which is the abbreviation for the developers mailing list in PEAR's lingo.
Some people enjoy communicating via IRC channels. We are running the #pear channel in the EFnet network. Some of the most active PEAR developers hang out regularly on this channel. However, for serious inquiries or discussions, you should email the developers mailing list pear-dev@lists.php.net, as these discussions are the only official communication method of PEAR, and are archived for future reference.
The PEAR Group occasionally publishes Administrative Documents, which describe the Group's decisions. You should read those documents before starting serious contributions to PEAR, because they contain important information about the inner workings of PEAR. The developers mailing list will be informed when new documents are added.
This manual contains the Developer Guide, which provides valuable information for using the tools of a PEAR developer.
The Coding Standards describe the standards which PEAR code must adhere to.
$Date: 2008-10-09 15:16:18 $
In the early times of PEAR, all new packages were proposed by an informal email to the PEAR developers mailing list. Over the time, the number of new proposals increased quite dramatically, making it hard to keep track of all the proposals.
A web-based proposal handling system was established to solve these problems. The system is called PEPr. It is pronounced like the spice and stands for "PEAR Proposal system".
This chapter describes the requirements and the workflow for a PEPr-based proposal. After that we will provide you with information, on how to actually start a proposal. Finally, you will learn what to do once the proposal is finished.
After reading this chapter, you will be able to start a proposal for your code right away.
The following enumeration lists the most important things that need to be done or be made available before starting a new proposal.
Example for a dependency list
If your package does only work on Linux, requires the DB package, version 1.8.4 or higher of the Log package and at least PHP 4.3.0, your dependency list looks like this:
Linux DB Log >= 1.8.4 PHP >= 4.3.0
For every package you should start a proposal in PEPr as a draft to initiate discussion on the developers mailing list.
By discussing the package with the PEAR developers you will easily be able to find out if:
By reading between the lines, you should also be able to find out if you will be able to gather enough positive votes in the formal proposal process.
Be aware that people will scrutinize your code, so be prepared for criticism (both positive and negative) and save everybody a bunch of time by making sure your scripts adhere to the Coding Standards. By using PHP_CodeSniffer you will see quickly if your code complies to the standards.
Please note that PEAR is an international project. People come from different cultural backgrounds where English may not be their native tongue. Misunderstandings may happen because of that, so assume the person is trying to do good. Do not worry if your English is not perfect but do try to be as clear as possible and do not hesitate to ask for advice.
To be able to use the proposal tool (PEPr), you have to apply for a PEAR website account. This account will usually be granted within one or two days. After that you can open a new proposal using the "New Package Proposal" interface.
The package proposal process is divided into 4 stages: "Draft", "Proposal", "Call for Votes" and "Finished". Every proposal has to run through all of these stages. During each stage (except for the draft stage) an email is send to you (the proposer) and the PEAR developers mailing list for any action.
At first your proposal is a draft. This simply means you can edit it, view it and it is shown up on the PEPr overview page as a draft. This stage is for you to play around with PEPr, to shape your proposal nicely and to prepare it for the proposal process. You can switch to the next stage whenever you want to publish your proposal and get feedback from the PEAR community.
After switching to the stage Proposal you have a real proposal. Either through email or through PEPr itself the community will give you hints and ask questions about the proposed package. Generally it is a good idea to follow these suggestions, but sometimes people in the community will have different visions for your package, which should be sorted out during this stage. After a week of comments you may switch to the next phase. You should only switch to the next stage, if you are sure that the PEAR community will accept your package.
The next stage is the voting stage, during which you may not change your proposal anymore. You may not edit nor delete the proposal from now on. During the proposal stage, "Call for Votes," every active PEAR maintainer may give one vote on the proposal. Votes are given using the numbers -1, 0 and +1, where -1 means "I'm against this package to be added to PEAR in the current form," +1 says "I'm in favor of getting this package into PEAR in the current form" and 0 means "I have looked at your package, but I am undecided." The time to vote for a package is 7 calendar days. If after this time less than 5 votes have been cast, the developers get another 7 days to cast their votes. After this time the voting is ended, whether there are 5 votes or not.
Votes on proposals can be bound to a condition. These conditional votes indicate that you have to fulfill a certain condition the voters expect to be fulfilled. If there are conditional votes you are expected to read and follow them! The conditions on a vote will be provided in the votes comment. Each vote may have comments. Reading those is always a good idea!
Now your proposal is finished. To determine if the proposal was successful or not, the sum of all votes is computed. If the result is greater or equal to 5 the proposal has been accepted. Otherwise we are sorry to say, that the community has decided to reject it. Have a look at their comments during proposal and vote stage to find out why. Maybe you can rework your package and propose it again, but please do not try to hand in a proposal the same way twice.
If your proposal has been accepted, you may put your package into PEAR. If this is your first proposal, please contact the PEAR Group via email to get your current PEAR website account upgraded to a full featured developer account. After that, you can register your package and upload releases.
The process of preparing and uploading a release is described in the Developer Guide.
In general you may not edit or delete your proposal after it has left the Proposal stage. In urgent cases you can contact the PEAR community via the developers mailing list to reach a PEAR website administrator to do a change. This is heavily discouraged however! You should have made everything clear to the community during the Proposal stage to ensure a hassle free voting process. Even if the voting has a negative result, this is no reason to delete it.
$Date: 2008-10-09 15:16:18 $
If you want to become the new lead maintainer of a package that is marked as unmaintained on the PEAR website, the following section will explain to you the necessary steps for this to happen.
If you already have a SVN account for
svn.php.net
, it is only necessary for you
to get additional "karma" for the module where
the package resides. You can request this karma by sending an
email to the PEAR
Group. (Please also mention in this mail that you
have already talked to the QA team.)
A guide to PEAR written for developers by developers.
2007-03-23
The intention of this guide is to provide you, a new developer of PEAR packages, with all necessary information to start working effectively.
The first part of this guide covers the advantages a developer has when using PEAR code in everyday work. After that we will show you how to contribute your own code to PEAR. Finally the guide describes the ways a developer can give back to the PEAR project.
PEAR is first and foremost a repository of reliable and high quality code. All packages comply with the PEAR Coding Standards. A new PEAR package should provide an API that is consistent, be written for PHP version 5.0.0 or newer, make use of PEAR_Exception for error handling, and adhere to the requirements of the package collective that it is accepted into.
PEAR provides a stable, robust community of developers with years of experience and a great place to collaborate. New and exciting ways of using PHP to solve the most pressing problems of web development and beyond happen here in PEAR every day.
PEAR gives you the chance to contribute your code to an enormous number of PHP developers: To get more information about how to contribute your code in PEAR, read here.
PEAR is driven by an open source developer community. Thus anyone may contribute code in the form of a new package or as an improvement to an existing package. Over the years, PEAR has developed a set of conventions. Some of them are evolving, but others have solidified. This document defines those requirements that you must adhere to in order to contribute to PEAR.
We are making certain demands on both the code and the maintainers of packages:
If you want to contribute your code to PEAR in form of a new package or as an addition to an existing package, it has to be compliant to a set of Coding Standards. There have been numerous discussions about whether those standards are good or not, and we have arbitrarily decided that they are good and that the discussion is over. Please respect this decision and don't bring up changes to the basic coding standards, or you risk being ignored mercilessly.
Using PHP_CodeSniffer on your code is the most easy way to see if you comply to the PEAR coding standards.
Each package has to be licensed under an accepted OpenSource license. If you are unsure about the license, you should opt for the New BSD License. The PEAR Group has issued a License Announcement, which provides further details concerning this topic.
The latest revision of the package's source code has to be
available in a public SCM
(Source Code Management) system such as the
SVN server at
svn.php.net
, the Subversion repository
of Google Code, or
at sites such as SourceForge. Hosting at
Google Code or SourceForge is freely available under their terms
of use at any time. Write access to svn.php.net is available
once the package has been accepted as a PEAR package.
All packages must have a publicly available source repository in order to honor the need for access in order to provide patches for bugs or new features.
Always keep in mind that your code should be extensible and that it has to be easy to add new features in the future. If it is easily predictable for you that there is no clean way to add new functionality to the codebase without breaking backwards compatibility, you should consider to review the code and to adapt it, before contributing it to PEAR.
To this end, most PEAR packages make use of object oriented programming. Through concepts such as inheritance, polymorphism, patterns like singleton/factory/command, PEAR packages solve problems in elegant ways. There are numerous resources about object oriented programming all around the web and you will also find tons of books available for your reading pleasure. However we can can recommend Object-Oriented Programming Concepts on Sun's Java Homepage for a quick start.
Your code must come with appropriate documentation in one of the following formats:
If you write the documentation in Docbook XML and if the markup is valid, the documentation can be easily added to the official PEAR Manual, which you are reading right now.
As of August 2003, phpDocumentor is fully capable of converting in-code API documentation and external tutorials into Docbook XML. PhpDocumentor version 1.2.2 or greater is required. Install with
pear install phpDocumentor
. Use the XML:DocBook/peardoc2:default converter on your source code to generate output. The output should be generated directly into thepeardoc/lang/package
directory, where lang is en, or fr, etc.Be aware though that only shipping the API documentation does not suffice! Additionally your package has to come with usage examples and (even better) tutorials about its usage. More information can be found in the section describing how to write documentation
All developers have experienced the frustration of bugs. They are a fact of life in programming, but fortunately there are a few systems that can be used to catch them, kill them, and prevent their return. Regression tests should be included with every stable release, so that users can run them if a bug occurs to help you debug the package. Examples of .phpt regression tests can be found as part of the PEAR package in SVN. For examples of PHPUnit tests, see the Auth package.
Code can be removed from PEAR if the lead maintainers are not willing to maintain the code anymore and if there is no other person that is willing to take over as the maintainer.
The general syntax for package names is
<Category>_<Name>
. The value for
<Category>
should be chosen from a
predefined list of categories that are available in PEAR (e.g.
"HTTP", "Net", "HTML").
The second part is the name of the package (like
"Upload", "Portscan", "Table").
The categories that are currently available in PEAR are represented by the bold headings at the Package Browser. If you think that your package does not fit in any of the existing categories, you can ask the on the mailing list to create a new category. (PEAR is usually reluctant to do that.)
Apart from this general syntax, the package names can also contain
more than one category name. An example for this is
HTML_Template_PHPLIB
: The multiple categories indicate
that the package PHPLIB
is part of the category
Template
, that again is part of the HTML
category. This naming scheme is necessary here since it's possible
that there are Template systems in PEAR that don't work with HTML but
with another technology. (The cases where more than one
"category" is used are pretty rare.)
If you need further advice or help for finding a name for your package, you should ask on the developers mailing list.
The second step while contributing is to announce your package in PEPr. Usually this announcement will spawn some discussion. After one week you may call for votes with PEPr and the developers will then start to vote for or against your proposal. (You are of course urged to join the upcoming discussion.) Announcing a package does of course not mean that it is already accepted! That will still take some time and likely some more efforts from your side.
The following passages are taken from the administrative document Handling Package Proposals, which describes proposing new packages in more details. Reading this document should be a mandatory step for PEAR newbies.
Only the votes of active members of the PEAR community (must have a PEAR web account, however the proposer himself is not counted) are counted, however anyone may vote. Votes require that a final choice of package name is specified.
The votes are added up, which means that one -1 offsets a +1. However -1 vote should always be considered to be serious and may lead to decisions being made on a case by case basis by the PEAR Group who reserves a veto (it is intended that in the future the PEAR QA team will assist the PEAR Group in such situations). Therefore a -1 vote *must* contain a comment explaining that decision, it is desirable that votes in favour (+1) should also be accompanied with an explanation when appropriate.
A vote is accepted if the total of all votes exceeds +5.
In case the proposal is not accepted the package can be further discussed on the list and the proposer may attempt to make another "call for vote" (it is expected that this is only done for sensible reasons).
Right now one can distinguish between two types of accounts related to PEAR:
This account is always necessary for you, if you want to release your package through PEAR. With this account you have access to the necessary infrastructure on pear.php.net in order to propose, upload and roll new releases. The PEAR Group manages PEAR accounts and pearweb karma levels (i.e.: karma to use the web site to maintain packages).
If you want to administrate your code via SVN , you can also apply for a SVN account to have access to the pear SVN module on svn.php.net. This makes it easier for other users to contribute to your code. The PHP Group (group@php.net) manage the PHP SVN server, which is used for maintaining PEAR packages. If you already have a PHP SVN account you will ask the PEAR Group for Karma for a given package or set of packages. Please send an email to pear-group@php.net specifying what packages you need commit access along with credentials (e.g.: the package "lead" email giving you his go or a QA member email announcing you are to be given access).
If you already have a SVN repository somewhere else (e.g. on SourceForge), or if you don't want to maintain your code via SVN, you don't need the PHP SVN account. It is highly recommended to use some kind of public repository, so that users can try out any bug fixes you apply to the code, before a new release is rolled.
To sign up for your pear.php.net account, go to the PEAR account request page and fill out the form there. The PEAR Group will then receive your request and someone will open your account, if the request sounds reasonable. You will be notified about that via email. Please note that you do NOT need a pear.php.net account to download packages from there.
To get a PHP SVN account, go here to sign up for it. The PHP SVN account has to be approved by the PHP Group.
The registration process is quite straightforward: Fill out the form on this site and submit the information. After you have done that, the PEAR Group has to finally approve your submission. This usually happens in a few hours and you will be notified about it via email.
After having registered your package, you can create a first release, which is described here.
package.xml
, version 2.0package.xml
The package definition file package.xml
is,
as the name already implies, a well-formed XML file that contains
all information about a PEAR package. Version 2.0 contains several important
enhancements over version 1.0, including
Channel support
Binary PECL packages support (not fully implemented in PEAR 1.4.0)
More specific dependency resolution
For those of you with an existing package.xml version 1.0, you can create an approximate equivalent package using the
$ pear convert
command. Note that as of version 1.6.0, PEAR_PackageFileManager supports package.xml 2.0 with the PEAR_PackageFileManager2 class.
PECL developers: for more information on pecl-specific features, read here.
package.xml
version 2.0 is supported from PEAR 1.4 on. As of 2007-04, PEAR 1.4 or greater is used in more than 99.8% of all installations in the wild that downloaded packages - there is no need to support v1.0 for backwards compatibility anymore.
<?xml version="1.0"?> <package version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> <name>PEAR</name><channel>
pear.php.net</channel><extends>
OldPackage</extends> <summary>Any one-line summary</summary> <description>any static long description. This text should not change very much between releases, use the "notes" tag for release notes </description><lead>
<name>Greg Beaver</name> <user>cellog</user> <email>cellog@php.net</email> <active>yes</active> </lead> <date>2005-02-26</date> <time>20:30:13</time> <-- note: <time> is optional --><version>
<release>1.4.0a2</release> <api>1.4.0</api> </version><stability>
<release>alpha</release> <api>alpha</api> </stability><license uri="
http://www.php.net/license/">PHP License</license> <notes> Put release notes here. They can be multi-line </notes><contents> <dir name="/">
<dir name="PEAR"> <dir name="ChannelFile"><file name="Parser.php" role="php" />
</dir> <!-- /PEAR/ChannelFile --> <file name="Dependency2.php" role="php"> <tasks:replace from="@PEAR-VER@" to="version" type="package-info"/> </file> </dir> <!-- /PEAR --> <dir name="scripts" baseinstalldir="/"> <file name="pear.bat" role="script"><tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
<tasks:replace from="@php_bin@" to="php_bin" type="pear-config" /> <tasks:replace from="@include_path@" to="php_dir" type="pear-config" /><tasks:windowseol/>
</file> <file name="pecl.bat" role="script"> <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" /> <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" /> <tasks:replace from="@include_path@" to="php_dir" type="pear-config" /> <tasks:windowseol/> </file> <file name="pear.sh" role="script"> <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" /> <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" /> <tasks:replace from="@pear_version@" to="version" type="package-info" /> <tasks:replace from="@include_path@" to="php_dir" type="pear-config" /><tasks:unixeol/>
</file> <file name="pecl.sh" role="script"> <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" /> <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" /> <tasks:replace from="@pear_version@" to="version" type="package-info" /> <tasks:replace from="@include_path@" to="php_dir" type="pear-config" /> <tasks:unixeol/> </file> <file name="pearcmd.php" role="php"> <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" /> <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" /> <tasks:replace from="@pear_version@" to="version" type="package-info" /> <tasks:replace from="@include_path@" to="php_dir" type="pear-config" /> </file> <file name="peclcmd.php" role="php"> <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" /> <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" /> <tasks:replace from="@pear_version@" to="version" type="package-info" /> <tasks:replace from="@include_path@" to="php_dir" type="pear-config" /> <tasks:footask/> </file> </dir> <!-- /scripts --> <file name="package.dtd" role="data" /> <file name="postinstall.php" role="php"><tasks:postinstallscript/>
</file> <file name="template.spec" role="foo" /> </dir> <!-- / --> </contents><compatible>
<name>FooPackage</name> <channel>pear.php.net</channel> <min>1.3.0</min> <max>1.5.0</max> </compatible><dependencies>
<required><php>
<min>4.2</min> <max>6.0.0</max> </php><pearinstaller>
<min>1.4.0dev13</min> </pearinstaller><package>
<name>Archive_Tar</name> <channel>pear.php.net</channel> <min>1.1</min> <recommended>1.2</recommended> </package><package>
<name>Foo</name> <uri>http://www.example.com/Foo-1.2.0.tgz</uri> </package><extension>
<name>xml</name> </extension><os>
<name>windows</name> <conflicts/> </os><arch>
<pattern>*-i?86-*-*</pattern> </arch> </required><optional>
<package> <name>PEAR_Frontend_Web</name> <channel>pear.php.net</channel> <min>0.5.0</min> </package> <package> <name>PEAR_Frontend_Gtk</name> <channel>pear.php.net</channel> <min>0.4.0</min> </package> </optional> <group name="remoteinstall" hint="adds the ability to install packages to a remote ftp server"> <package> <name>Net_FTP</name> <channel>pear.php.net</channel> <min>1.3.0RC1</min> <recommended>1.3.0</recommended> </package> </group> <group name="webinstaller" hint="PEAR's web-based installer"> <package> <name>PEAR_Frontend_Web</name> <channel>pear.php.net</channel> <min>0.5.0</min> </package> </group> <group name="gtkinstaller" hint="PEAR's PHP-GTK-based installer"> <package> <name>PEAR_Frontend_Gtk</name> <channel>pear.php.net</channel> <min>0.4.0</min> </package> </group> </dependencies><usesrole>
<role>foo</role> <package>Foo</package> <channel>pear.example.com</channel> </usesrole><usestask>
<task>footask</task> <package>Footask</package> <channel>pear.example.com</channel> </usestask><phprelease>
<installconditions> <os> <name>windows</name> </os> </installconditions> <filelist> <install as="pear.bat" name="scripts/pear.bat" /> <install as="pecl.bat" name="scripts/pecl.bat" /> <install as="pearcmd.php" name="scripts/pearcmd.php" /> <install as="peclcmd.php" name="scripts/peclcmd.php" /> <ignore name="scripts/pear.sh" /> <ignore name="scripts/pecl.sh" /> </filelist> </phprelease> <phprelease> <filelist> <install as="pear" name="scripts/pear.sh" /> <install as="pecl" name="scripts/pecl.sh" /> <install as="pearcmd.php" name="scripts/pearcmd.php" /> <install as="peclcmd.php" name="scripts/peclcmd.php" /> <ignore name="scripts/pear.bat" /> <ignore name="scripts/pecl.bat" /> </filelist> </phprelease> <changelog> <release> <version> <release>1.3.5</release> <api>1.3.0</api> </version> <stability> <release>stable</release> <api>stable</api> </stability> <date>2005-02-26</date> <license uri="http://www.php.net/license/3_0.txt">PHP License</license> <notes> * fix Bug #3505: pecl can't install PDO * enhance pear run-tests dramatically * fix Bug #3506: pear install should export the pear version into the environment </notes> </release> <release> <version> <release>1.4.0a1</release> <api>1.4.0</api> </version> <stability> <release>alpha</release> <api>alpha</api> </stability> <date>2005-02-26</date> <license uri="http://www.php.net/license/3_0.txt">PHP License</license> <notes> This is a major milestone release for PEAR. In addition to several killer features, every single element of PEAR has a regression test, and so stability is much higher than any previous PEAR release, even with the alpha label. New features in a nutshell: * full support for channels * pre-download dependency validation * new package.xml 2.0 format allows tremendous flexibility while maintaining BC * support for optional dependency groups and limited support for sub-packaging * robust dependency support * full dependency validation on uninstall * support for binary PECL packages * remote install for hosts with only ftp access - no more problems with restricted host installation * full support for mirroring * support for bundling several packages into a single tarball * support for static dependencies on a url-based package Specific changes from 1.3.5: * Implement request #1789: SSL support for xml-rpc and download * Everything above here that you just read </notes> </release> </changelog> </package>
Most of the tags for PECL-style PHP extension releases are identical to those for PEAR-style PHP script releases. There are a few extsrc/extbin-specific tags that all PECL developers must know about.
Tag | Description of usage |
---|---|
<providesextension> |
The <providesextension> tag must be right after <dependencies>. This
tag tells the installer the name of the extension provided by the package, allowing
different package names from the extension. This could be important for binary packages
(such as PDO and PDO_windows or something along those lines).
<providesextension>PDO</providesextension> |
<srcpackage> or <srcuri> |
<srcpackage> or <srcuri> must follow <providesextension>.
Extension binary packages must define either a <srcpackage> tag (for package.xml
containing <channel>) or
a <srcuri> tag (for package.xml containing
<uri>).
<name>PDO_windowsbin</name> <channel>pecl.php.net</channel> <!-- snip --> <providesextension>PDO</providesextension> <srcpackage>PDO</srcpackage> <name>Foo_windowsbin</name> <uri>http://www.example.com/Foo_windowsbin-1.5.0.tgz</uri> <!-- snip --> <providesextension>Foo</providesextension> <srcuri>http://www.example.com/Foo-1.5.0.tgz</srcuri> |
<configureoption> |
The <configureoption> tag is used to ask a user a specific
question, which is then used to influence the building of the extension.
Any configure option beginning with "with" is assumed
to be a question about enabling a feature. Other options have the user's
response automatically passed to configure. The
following configureoptions:
<configureoption name="with-blah" prompt="enable blah?" default="yes"/> <configureoption name="foo" prompt="what foo to use?" default="bar"/> --with-blah --foo=barif the user accepts the default values. |
Each tag that needs further explanation is documented here (unfinished)
Channels are new in PEAR 1.4.0. Channels are a systemized way of differentiating between different download sources. One such download source is pear.php.net, another is pecl.php.net, and there are several third party channels slowly springing up around the net.
The <channel> tag should contain the full channel name, not any alias (don't use "pear", use "pear.php.net".
A good rule of thumb to use when determining what channel name to use is "where do I upload my releases to?" If the answer is pear.php.net, that is your package's channel. If the answer is pecl.php.net, then that is your channel.
If you aren't running a channel server and wish to serve your packages as well as allow other packages to remotely depend on your package, the new uri-based package distribution method is the best choice, see the <uri> tag documentation link below.
By adding an <extends> tag to your package.xml
,
you tell the world that your new package extends an old package in some way.
The new package could supersede the old one, or give new functionality to
it.
Using extends
... <name>Package_Name2</name> <channel>pear.php.net</channel> <extends>Package_Name</extends> <summary>Any one-line summary</summary> ...
Examples for <extends> usage in PEAR
If you do not have a channel server and wish to serve your packages so that others can depend on them, use <uri>. This should contain a static uri that links to a single package version's downloadable .tgz
If you do not need to allow external remote dependencies, then simply use the pear.php.net channel as your package's channel.
For instance, if you wish to serve package Foo version 1.1.0 from www.example.com, use the uri "http://www.example.com/Foo-1.1.0". Note that the uri must contain the full path MINUS THE EXTENSION. Provide both Foo-1.1.0.tgz and Foo-1.1.0.tar for users without gzip.
In package.xml 1.0, a developer was documented using the <maintainer> tag inside of a redundant <maintainers> container tag. This has been simplified in package.xml 2.0 both to slightly speed parsing and to make validation of the xml simpler. Now, the contents of the <role> tag has been extracted as 4 tags to describe the maintainers of a package.
In addition, a new internal tag <active> has been added, so that you can honor retired developers' work without having to remove them altogether from package.xml.
WARNING: tag order is important. List leads followed by developers followed by contributors and finally helpers.
In package.xml 1.0, the version was simply the version. package.xml 2.0 splits the concept of versioning into two components, release and api.
The release version is the same familiar versioning concept from package.xml 1.0. This version is validated very carefully by the packager.
The API version is informational only - the installer does not use it. It can be used for a package-time replacement by the installer. This can be very useful when implementing a reflective method such as getApiVersion(). In addition, the API version is very loosely validated, and only requires a version_compare()-compatible version number.
In package.xml 1.0, stability was known as "state" which is not the most accurate description. package.xml 2.0 introduces the term stability and like version splits the concept of stability into two components, release and api.
The release stability is the same familiar state from package.xml 1.0. It can be one of:
snapshot - a frozen picture of development at a particular moment
devel - a very new non-production release. Devel should be used for extremely new, practically untested code.
alpha - a new non-production release. Alpha should be used for new code that has an unstable API or untested code.
beta - a non-production release. Beta should be used for code that has a stable API and is nearing a fully stable release. Regresion tests and documentation should exist or soon follow to qualify as a beta release. Release candidates should use the beta stability.
stable - a production release. Stable releases must have a stable API, and must have regression tests and documentation.
The API stability is informational only - the installer does not use it.
In package.xml 1.0, the license tag only contained the name of the license. In package.xml 2.0, there are two optional attributes, "uri" and "filesource". "uri" contains a uri that identifies the text of a license, such as "http://www.php.net/license/" for the PHP License. "filesource" can be used to specify a LICENSE file within an actual package that contains the text of the license.
In package.xml 1.0, the contents tag was <filelist>. The purpose of the tag changed dramatically in package.xml 2.0, warranting a name change. Now, <filelist> can be found in the release section of the package.xml.
<contents> is used to describe the contents of a tarball. Nothing further. All files in the contents tag will be placed into the tarball regardless of whether they eventually get installed by the PEAR installer. This fact can be used to create a very versatile tarball, one that can be directly unzipped and work out of the box as well as be installed by the PEAR installer and work out of the box.
For most release types, contents contains a single <dir> tag that then either contains nested dir tags and/or <file> tags.
The <dir>
tag describes a directory in the
package sources.
A <dir>
tag may contain other
<dir>
tags as well as
<file>
tags.
All files must be contained in a single top-level <dir> tag. For simple packages, simply use <dir name="/"> as the directory name.
Attribute name | Description |
---|---|
name |
Name of the directory in the sources |
Attribute name | Description |
---|---|
baseinstalldir |
Relative location where all files and subdirectories will be installed |
An excerpt of the <contents>
tag of a real
package.xml:
<contents> <dir name="/"> <dir name="examples"> <file name="authors.php" role="doc" /> </dir> <dir name="HTML"> <dir name="Template"> <file name="PHPLIB.php" role="php" /> <dir name="PHPLIB"> <!-- more files --> </dir> </dir> </dir> </contents>
The baseinstalldir
attribute is mostly used when
the directory structure in the package source tree does not match the
layout when installed (e.g. when the QuickForm/
directory needs to be installed as
HTML/QuickForm/
:
<contents> <dir name="/" baseinstalldir="HTML"> <dir name="QuickForm"> <file name="Element.php" role="php" /> <!-- would be installed as HTML/QuickForm/Element.php --> </dir> </dir> </contents>
The <file>
tag describes a file in a
directory in the package sources.
File tags may only occur as children of
<dir>
tags.
File Tasks can be used to modify files at package time or at installation.
Attribute name | Description |
---|---|
name |
Name of the file in the sources |
role |
Type of the file. See roles. |
Attribute name | Description |
---|---|
baseinstalldir |
Relative location where all files and subdirectories will be installed |
md5sum |
MD5 hash about file contents.This is automatically generated when executing pear package, so you should never set it manually. |
Previous optional attributes (
package.xml
v1)<platform>
and<install-as>
have been replaced by the release tags.Specifically,
<install>
is used to specify<install-as>
, and the<ignore>
tag can be used in conjunction with<installconditions>
to exclude packages from being installed on particular platforms.
The <role>
attribute in the
<file>
tag defines what type the file has and in
which location it should be installed.
Role value | Description | Destination dir |
---|---|---|
php |
PHP source file | $php_dir (your include path) |
doc |
Documentation or example file | $doc_dir/Package_Name/ |
data |
Package related data files (graphics, data tables, CSS etc.) | $data_dir /Package_Name/ |
www |
Files for the HTTPd document root | $www_dir / |
test |
Package related test files (unit-tests etc) | $test_dir /Package_Name/ |
script |
Package related shell scripts | the PHP binary directory ($bin_dir ) or
PHP_PEAR_BIN_DIR if defined |
ext |
Extension, dynamically loadable library | the PHP extension directory ($ext_dir )
or PHP_PEAR_EXTENSION_DIR if
defined |
src / extsrc |
C or C++ source code | not copied directly - used to build a extension |
Directory locations like $php_dir
are
configurable in PEAR. You can use
pear config-show
or
pear config-get php_dir
to retrieve their values.
Tasks provide a flexible and customizable way to manipulate file contents or to perform complex installation tasks (hence the name "tasks"). By default, PEAR comes with 4 tasks, but customized tasks can be added simply by adding a file into the PEAR/Tasks directory that follows the conventions of existing tasks. This page does not describe how to create a custom task, only how to use them in package.xml.
There are 3 basic tasks and 1 complex task distributed with PEAR. The basic tasks are "tasks:replace", "tasks:windowseol", and "tasks:unixeol". The complex task is "tasks:postinstallscript". "tasks:replace" is nearly identical to the old <replace> tag from package.xml 1.0, and does a text search-and-replace of a file's contents. "tasks:windowseol" and "tasks:unixeol" manipulate the line endings of a file to ensure that the correct line endings are in place for critical files like DOS .bat batch files and unix shell scripts. "tasks:postinstallscript" allows users to choose to run a script to perform further installation functions.
The replace task has 3 required attributes:
type - This must be either package-info or pear-config. package-info replacements extract information from package.xml itself to use as the replacement text. pear-config replacements use the value of a configuration variable (as displayed by
pear config-show
) as the text for replacement.
from - Text to search for in a file. Traditionally, this text is enclosed in "@" to differentiate it from normal text, as in from="@version@"
to - Abstract item to use as a replacement for all occurrences of "from". package-info replacements can be one of api-state, api-version, channel, date, description, license, license-uri, name, notes, release_date, release-license, release_notes, state, summary, time, version, and for some packages extends, providesextension, srcpackage, and srcuri.
Note that package-info replacements are performed at packaging time, so files contain package-info replacements inside a .tgz/.tar release. pear-config replacements can only occur on the installation system, and are performed at install-time.
This task can be used to enable packaging of windows-specific files (like DOS batch files) on a non-windows system, such as unix systems that convert line endings to \n. Note that this task is performed at package-time, as well as at install-time, so files will contain the correct line endings inside a .tgz/.tar release.
This task can be used to enable packaging of unix-specific files (like sh shell scripts) on a non-unix system, such as windows systems that convert line endings to \r\n. Note that this task is performed at package-time, as well as at install-time, so files will contain the correct line endings inside a .tgz/.tar release.
The postinstallscript task informs the installer that the file it references is a post-installation script.
For security reasons, post-install scripts must be manually executed by the users, and as such the installer has special code that is separate from other tasks.
The <postinstallscript> tag may define parameters that are used by the installer to retrieve user input. In order to support both the web installer and the command-line installer, all processing of input is performed by PEAR and passed to the post-install script in a strict format. All you need to do is define the parameters using xml inside the <postinstallscript> tag.
Here is the xml representing a simple post-install script with parameters:
<tasks:postinstallscript> <tasks:paramgroup> <tasks:id>first</tasks:id> <tasks:param> <tasks:name>test</tasks:name> <tasks:prompt>Testing Thingy</tasks:prompt> <tasks:type>string</tasks:type> </tasks:param> </tasks:paramgroup> </tasks:postinstallscript>
Note that the only type recognized at this stage is "string" but others will follow. A more complex example follows:
<tasks:postinstallscript> <tasks:paramgroup> <tasks:id>first</tasks:id> <tasks:instructions>The first group of questions relates primarily to your favorite color. Answer wisely. </tasks:instructions> <tasks:param> <tasks:name>test</tasks:name> <tasks:prompt>Testing Thingy</tasks:prompt> <tasks:type>string</tasks:type> <tasks:default>hi</tasks:default> </tasks:param> <tasks:param> <tasks:name>test2</tasks:name> <tasks:prompt>Testing Thingy 2</tasks:prompt> <tasks:type>string</tasks:type> </tasks:param> </tasks:paramgroup> <tasks:paramgroup> <tasks:id>second</tasks:id> <tasks:name>first::test</tasks:name> <tasks:conditiontype>preg_match</tasks:conditiontype> <tasks:value>g+</tasks:value> <tasks:param> <tasks:name>test</tasks:name> <tasks:prompt>Testing Thingy a</tasks:prompt> <tasks:type>string</tasks:type> <tasks:default>hi</tasks:default> </tasks:param> <tasks:param> <tasks:name>test2</tasks:name> <tasks:prompt>Testing Thingy b</tasks:prompt> <tasks:type>string</tasks:type> </tasks:param> </tasks:paramgroup> </tasks:postinstallscript>
This post-installation script has two parameter groups. The first parameter group has special instructions that are displayed to the user to assist in answering the required prompts. After the first group is processed, the second group is processed (naturally). However, in this case, the second group is a conditional parameter group. A conditional parameter group examines the user input from previous parameter groups and only displays its parameter prompts if a single parameter fits a test. The condition is defined by three tags, <tasks:name>, <tasks:conditiontype>, and <tasks:value>. Note that all three tags are required or xml validation will fail.
<tasks:name> is the parameter name from a previous parameter group. The format of name is groupID::parameterName, so as you see above, "first::test" refers to the <tasks:param> test from the <tasks:paramgroup> first.
<tasks:conditiontype> determines how the parameter input function will process the value of the parameter specified in <tasks:name>, and can be one of three values, "=" "!=" or "preg_match".
=: This (obviously) tests whether the parameters value is equal to the <tasks:value> tag
!=: This (obviously) tests whether the parameters value is not equal to the <tasks:value> tag
preg_match: This uses the content of the <tasks:value> tag as if it were the stuff between / and / in a preg_match() function call. Do NOT include // brackets in the regular expression. In the <tasks:paramgroup> second, the value "g+" will become:
<?php
preg_match('/g+/', first::test value)
?>
The <compatible> tag is designed to be used with a <package> dependency that contains a <recommended> version tag from package pear.example.com/Bar version 1.3.0 like so:
<package> <name>Foo</name> <channel>pear.example.com</channel> <min>1.0.0</min> <recommended>1.5.2</recommended> </package>
The above dependency can be translated into English as follows: "Use the package pear.example.com/Foo, but only versions 1.0.0 or newer. If pear.example.com/Foo is not installed, install version 1.5.2. If pear.example.com/Foo is installed and is not version 1.5.2, fail unless --force is specified, or pear.example.com/Foo is compatible with me."
That last clause "...or pear.example.com/Foo is compatible with me." is controlled by the <compatible> tag. If package Foo version 1.5.3's package.xml has a <compatible> like so:
<compatible> <name>Bar</name> <channel>pear.example.com</channel> <min>1.2.0</min> <max>1.3.0</max> <exclude>1.2.9</exclude> </compatible>
This will instruct the installer that pear.example.com/Foo version 1.5.3 is compatible with pear.example.com/Bar versions 1.2.0 to 1.3.0 inclusive, but is not compatible with 1.2.9.
It is very important to note that only existing versions that have been tested with the package should be mentioned in the <compatible> tag. Future versions of pear.example.com/Bar should simply upgrade the <recommended> tag.
<compatible> may contain three versioning tags. The required <min> and <max> are used to define the range of tested and compatible versions, and <exclude> is used to exclude any versions within the range. In the example above, 1.3.0 and 1.2.0 are the highest and lowest versions that may be excluded. There can be an unlimited number of <compatible> tags inside a package.xml.
Dependencies can be tricky to manage. Using code written by other people requires a robust and simple mechanism to manage the risk of breaking your code because of bugs in the external package, or worse, an unexpected API change. PEAR excels at dependency handling, which mitigates these risks. As a PEAR developer, it is crucial to understand how to specify a dependency on other packages as well as the system requirements of your package.
In package.xml 1.0, dependencies are relatively simple, but not as powerful. Specifying a dependency on a package for applications was actually dangerous. If you wished to limit an installed version of a package to a single version, it would mean preventing upgrade at any cost. package.xml 2.0 provides a simple way to enforce stricter dependency versioning without making upgrades onerous.
package.xml 1.0 supports two kinds of dependencies, required and optional. package.xml 2.0 also supports these two dependency types, but introduces a new kind of dependency concept: an optional dependency group (documented here).
package.xml 1.0 only supported php, package, and extension dependencies. package.xml 2.0 supports dependencies on php, package, extension, os, architecture, and PEAR installer. In addition, package.xml 2.0 supports depending on a static package located at a url, and depending on a package that provides an extension to PHP like PECL packages.
The PEAR installer dependency is not a dependency on the PEAR package, but a dependency on the currently running PEAR installer, and is more similar to a PHP dependency in that it requires the specified version to be running in memory. This is very useful for circumventing difficult bugs in the PEAR installer that render a package install useless.
The <dependencies> tag re-organizes dependencies into groups and "extracts" attributes into tags. It also un-abbreviates words for clarity and human-readability. The following excerpt of a package.xml version 1.0:
<deps> <dep type="pkg" rel="ge" version="1.3.1">Archive_Tar</dep> <dep type="php" rel="ge" version="4.2.0"/> <dep type="pkg" rel="has" optional="yes">PEAR_Frontend_Web</dep> </deps>
Approximately translates into this format in package.xml 2.0:
<dependencies> <required> <pearinstaller> <min>1.4.8</min> </pearinstaller> <php> <min>4.2.0</min> </php> <package> <name>Archive_Tar</name> <channel>pear.php.net</channel> <min>1.3.1</min> </package> </required> <optional> <package> <name>PEAR_Frontend_Web</name> <channel>pear.php.net</channel> </package> </optional> </dependencies>
These changes were made to simplify xml validation and parsing. Note that unlike package.xml 1.0, the <pearinstaller> and <php> dependencies are required in all package.xml. In addition the <min> tag is required in both <pearinstaller> and <php> dependencies.
Optional dependency groups define feature sets that are not required, but
should be installed in a block. These feature sets consist of <package>
and <extension> dependencies. For instance, if a package can optionally
perform operations on a remote shell, it would create an optional dependency
group named remoteshell
with dependencies on the
ssh2
PECL extension and the (fictional)
SSH_RemoteShell
package. The dependency group could look
like this in package.xml:
<group name="remoteshell" hint="Add support for Remote Shell Operations"> <package> <name>SSH_RemoteShell</name> <channel>pear.php.net</channel> </package> <extension> <name>ssh2</name> </extension> </group>
To install this dependency group, the user would simply use
pear install Packagename#remoteshell
.
The <pearinstaller> dependency defines the minimum version of PEAR that can properly parse and install the package.xml containing it. As with all dependency tags that support versioning, these 4 tags are supported to define versioning:
<min> - minimum version of PEAR required to install this package.xml. This tag is required in all package.xml <pearinstaller> dependencies.
<max> - maximum version of PEAR installer supported. Use with caution! This tag will prevent the package from being installed by anyone with a newer version of PEAR!
<recommended> - recommended version of PEAR installer. This tag is used for strict version control. The installer will refuse to install a package without the --force option unless the version exactly matches recommended. This can be used to provide a level of extra security, as a package can be set to install using a version that is known to work without limiting future upgrades.
<exclude> - incompatible versions of PEAR installer. Use this to prevent the package from being installed by any PEAR version that cannot properly install the package. Multiple <exclude> tags may be used to exclude more than one version.
As with all dependency tags that support versioning, these 4 tags are supported to define versioning:
<min> - minimum version of PHP required to install this package.xml. This tag is required in all package.xml <php> dependencies.
<max> - maximum version of PHP supported.
<exclude> - incompatible versions of PHP. Use this to prevent the package from being installed by any PHP version that cannot properly work with the package. Multiple <exclude> tags may be used to exclude more than one version.
Subpackage dependencies share the same xml format as package dependencies. The subpackage dependency should only be used if a package is split into more than one package. In other words, if the child package contains the same files as any earlier version of the parent package, the child package would normally conflict with the parent package because it would be attempting to overwrite the parent package's files with its own files.
A simple example should make this clear. Package Foo 1.0.0 contains Foo.php and Foo/Bar.php. Package Foo's developers decide to split Foo into two packages: Foo and Foo_Bar. Foo 1.1.0 contains foo.php, and Foo_Bar 0.1.0 contains Foo/Bar.php. Foo_Bar 0.1.0 conflicts directly with Foo 1.0.0, as both contain the file Foo/Bar.php.
If Foo has a subpackage dependency on Foo_Bar, then the installer will ignore the conflict between Foo 1.0.0's Foo/Bar.php and Foo_Bar 0.1.0's Foo/Bar.php just as it ignores the conflict between Foo 1.0.0's Foo.php and Foo 1.1.0's Foo.php.
Understandably, the <package> dependency is PEAR's most complex dependency. PEAR 1.4.0 supports 3 different kinds of package dependencies:
Normal, traditional channel server-based package dependencies (same idea as package.xml 1.0).
Dependencies on packages that provide PHP extensions (like PECL packages). (These can be both server-based and uri-based dependencies)
Static, non-traditional uri-based package dependencies.
The most common kind of package dependency is a channel-based dependency. This dependency from package.xml version 1.0:
<deps> <dep type="pkg" rel="has">PEAR</dep> </deps>
translates to this dependency in package.xml version 2.0:
<dependencies> <required> <!-- ... --> <package> <name>PEAR</name> <channel>pear.php.net</channel> </package> </required> </dependencies>
Note that <channel> is a required tag for all typical package dependencies. Use pear.php.net for all packages that were packaged using package.xml 1.0, regardless of where they are downloaded from.
As with all dependency tags that support versioning, all standard versioning tags are supported (min, max, recommended, exclude). In addition, the <conflicts> tag is supported to create a negative dependency.
<min> - minimum version of a dependency. If the dependency package is installed, and is older than this version, installation will fail.
<max> - maximum version of a dependency. If the dependency package is installed, and is newer than this version, installation will fail.
<recommended> - recommended version of a dependency. This tag is used for strict version control. The installer will refuse to install a package without the --force option unless the version exactly matches recommended. This can be used to provide a level of extra security, as a package can be set to install using a version that is known to work without limiting future upgrades.
Note that use of the <compatible> tag in the dependency's package.xml can be used to circumvent the installer's strictness. In essence, the <compatible> tag tells the installer that a dependent package is compatible with the current package, even though it is not the recommended version.
<exclude> - incompatible versions of a package. Multiple <exclude> tags may be used to exclude more than one version of a dependency.
<conflicts> - Negates the dependency. If the package is installed, it cannot satisfy the requirements of the dependency or installation will fail.
Here is a rough chart describing how to convert from package.xml 1.0 "rel" attributes to a package.xml 2.0 equivalent.
1.0 | 2.0 equivalent |
---|---|
<dep type="pkg" rel="has">Foo</dep> |
<package> <name>Foo</name> <channel>pear.php.net</channel> </package> |
<dep type="pkg" rel="ge" version="1.0.0">Foo</dep> |
<package> <name>Foo</name> <channel>pear.php.net</channel> <min>1.0.0</min> </package> |
<dep type="pkg" rel="gt" version="1.0.0">Foo</dep> |
<package> <name>Foo</name> <channel>pear.php.net</channel> <min>1.0.0</min> <exclude>1.0.0</exclude> </package> |
<dep type="pkg" rel="le" version="1.0.0">Foo</dep> |
<package> <name>Foo</name> <channel>pear.php.net</channel> <max>1.0.0</max> </package> |
<dep type="pkg" rel="ge" version="1.0.0">Foo</dep> |
<package> <name>Foo</name> <channel>pear.php.net</channel> <max>1.0.0</max> <exclude>1.0.0</exclude> </package> |
<dep type="pkg" rel="ge" version="1.0.0">Foo</dep> <dep type="pkg" rel="le" version="1.9.0">Foo</dep> |
<package> <name>Foo</name> <channel>pear.php.net</channel> <min>1.0.0</min> <max>1.9.0</max> </package> |
<dep type="pkg" rel="not">Foo</dep> |
<package> <name>Foo</name> <channel>pear.php.net</channel> <conflicts/> </package> |
Let's look at uri-based package dependencies. Here is a simple example:
<package> <name>Foo<name> <uri>http://www.example.com/Foo-1.3.0</uri> </package>
This dependency tells the installer to fetch http://www.example.com/Foo-1.3.0.tgz or http://www.example.com/Foo-1.3.0.tar (both must be available!) if the package Foo is not installed. All uri packages must contain a <uri> tag rather than a <channel> tag and will automatically belong to the pseudo-channel "__uri", but that is not important to the discussion of how to format the xml to create the uri-based package dependency.
uri-based package dependencies cannot contain any versioning information, as this is irrelevant: there is only one version possible with a static uri. uri-based dependencies can contain the <conflicts/> tag to specify an absolute conflict with the package, and the <providesextension> tag to specify an extension provided by the static package.
package.xml 2.0 supports differentiating release types, and as such also supports dependencies on PECL-style packages that use the extbinrelease or extsrcrelease type.
To specify a dependency on a PHP extension that can be distributed as a PECL package, but could also be bundled with PHP by default, such as the PDO extension, use this dependency style:
<package> <name>PDO</name> <channel>pecl.php.net</channel> <min>0.3.1</min> <providesextension>PDO</providesextension> </package>
The magic is in the <providesextension> tag. This tag tells the installer to take this process when validating the dependency:
Is the extension "PDO" present in memory? If so, is it version 0.3.1 or higher?
If not, is the user also installing pecl.php.net/PDO at the same time as this package? If so, is it version 0.3.1 or higher?
If not, is pecl.php.net/PDO installed, and is the version 0.3.1 or higher?
If any of the three conditions above validate in the order specified, the dependency will be satisfied and installation will continue. This system allows users to use a different php.ini to install PHP extensions and also provides a fail-safe system to depend on extensions.
<providesextension>, like all other extension-related functions in PHP, is case-sensitive. Do not use "pdo" for the "PDO" extension, or your dependency will always fail.
As with all dependency tags that support versioning, all standard versioning tags are supported (min, max, recommended, exclude). In addition, the <conflicts> tag is supported to create a negative dependency.
<min> - minimum version of PHP extension to install this package.xml.
<max> - maximum version of PHP extension supported.
<recommended> - recommended version of PHP extension. This tag is used for strict version control. The installer will refuse to install a package without the --force option unless the version exactly matches recommended. This can be used to provide a level of extra security, as a package can be set to install using a version that is known to work without limiting future upgrades.
<exclude> - incompatible versions of PHP extension. Multiple <exclude> tags may be used to exclude more than one version.
<conflicts> - Negates the dependency. If the extension is present, it cannot satisfy the requirements of the dependency or installation will fail.
The OS dependency is used to restrict a package to both a particular class of OSes (like unix) and to a specific OS (like darwin, or freebsd). Here is an example:
<os> <name>linux</name> </os>
To specify that a package can be installed on every OS except the one specified, use the <conflicts/> tag:
<os> <name>windows</name> <conflicts/> </os>
Possible OS values are:
In addition, any esoteric OS that supports the php_uname() function can be used. Note that the "unix" OS is defined as any of linux, freebsd, darwin, sunos, irix, hpux, or aix.
The arch dependency is used to restrict a package to a specific os and processor architecture. Here is an example:
<arch> <pattern>linux-*-i386-*</pattern> </arch>
To specify that a package can be installed on every architecture except the one specified, use the <conflicts/> tag:
<arch> <pattern>linux-*-i?86-*</pattern> <conflicts/> </arch>
The arch pattern is defined by the OS_Guess->matchSignature() method, and is as follows: sysname[-release[-cpu[-extra]]]. All segments within [] are optional, and the wildcard "*" can be used in all segments instead of a value. In addition, the "?" wildcard can be used to specify a single character that can match any value. i?86 will match i386, i686, i586 and so on.
sysname is the same as the os dependency, except unix is not supported.
release is the version of the operating system.
cpu is the specific cpu, and is typically i?86, sparc, powerpc.
extra is any other stuff on the end of php_uname(), including the glibc version
Standard file roles provided by default with PEAR are:
php
data
doc
test
script
src
ext
If your package chooses to use a role provided by a third party that implements some more advanced file installation handling, all you need to do is specify the role in the xml for the <file> tag that contains it like so:
<file role="foo"/>
However, if a user does not have the package installed that provides the custom role "foo", then the error message on installation will simply say "unknown role 'foo'", which is not very helpful.
The <usesrole> tag instead prompts the installer to tell the user "this package uses the custom role 'foo', install package pear.example.com/Foo to use"
<usesrole> <role>foo</role> <package>Foo</package> <channel>pear.example.com</channel> </usesrole>
Note that static URI packages (channel-less packages) are also supported:
<usesrole> <role>foo</role> <uri>http://pear.example.com/Foo-1.2.0</uri> </usesrole>
Standard file tasks provided by default with PEAR are documented in this location.
If your package chooses to use a task provided by a third party, all you need to do is specify the task as part of the xml for the <file> tag that contains it like so:
<file role="php"> <tasks:foo/> </file>
However, if a user does not have the package installed that provides the custom task "foo", then the error message on installation will simply say "unknown task 'foo'", which is not very helpful.
The <usestask> tag instead prompts the installer to tell the user "this package uses the custom task 'foo', install package pear.example.com/Foo to use"
<usestask> <task>foo</task> <package>Foo</package> <channel>pear.example.com</channel> </usestask>
Note that static URI packages (channel-less packages) are also supported:
<usestask> <task>foo</task> <uri>http://pear.example.com/Foo-1.2.0</uri> </usesrole>
In package.xml 1.0, there was one release type. package.xml 2.0 provides much finer control over the kind of release in order to provide three new release types: extension binary release, extension source release, and package bundle release.
All of the normal release tags (phprelease, extsrcrelease, extbinrelease) may contain two optional sections, <installconditions> and <filelist>. The purpose of these sections is to allow specification of different file install groups based on the target OS for installation, or other common install conditions.
To be clear: in package.xml, there was 1 <release> tag. package.xml 2.0 allows several adjacent release tags, each specifying a different install set. This actually simplifies complex installation filesets by separating the contents listing of the tarball from how the installer should manipulate this listing. Debugging installation file sets should be much simpler with this change.
The <filelist> tag can contain only two possible tags, <install> and <ignore>. install has two required attributes, "name" and "as". The install tag is used in the same manner as package.xml's install-as attribute for the <file>, to specify a new installation location for a file in the contents list. The ignore tag is used to completely ignore a file.
The <installconditions> tag can contain 4 tags whose format can be found in the <dependencies> section: <php>, <extension>, <os>, and <arch>. Each tag can appear exactly once except for the extension tag, which can appear limitless times.
The php tag is used to specify a php version or range of versions that an install set should be valid with.
The extension tag is used to specify extensions that must be present for an install set to be valid.
The os tag is used to specify an OS that must be present for an install set to be valid. Note that unix can be used to match all flavors, and linux can be used to match all linux-based OSes. Darwin should be used for Mac OS X, and * can be used to match all operating systems.
The arch tag is used to specify a uname string or portion of a uname string that must match in order for the install set to be valid.
The phprelease release type is designed for PEAR-style PHP script package releases. It causes a few specific validation changes. First of all, the <contents> tag must contain <file> and <dir> tags. The only valid roles for files are role="php", role="data", role="doc", and role="test" plus any custom roles that the user has installed for use in php releases.
The extsrcrelease release type is for PECL-style PHP extension releases that must be compiled in order to be useable. It causes a few specific validation changes. First of all, the <contents> tag must contain <file> and <dir> tags. The only valid roles for files are role="src", role="data", role="doc", and role="test" plus any custom roles that the user has installed for use in extension source releases.
In addition, the <providesextension> tag must be present in order to document the name of the extension this package provides must be in the package.xml as well.
The extbinrelease release type is for PECL-style PHP Extension binary releases that are pre-compiled. It causes a few specific validation changes. First of all, the <contents> tag must contain <file> and <dir> tags. The only valid roles for files are role="ext", role="data", role="doc", and role="test" plus any custom roles that the user has installed for use in extension binary releases.
In addition, the <srcpackage> or <srcuri> and the <providesextension> tags must be present in order to document the package that provides extension source for this binary release, and the name of the extension this package provides must be in the package.xml as well.
The bundle release type is designed to allow packaging several other package releases into a single bundle of packages that will all be installed at the same time. This can be used to distribute a complete application as one tarball, or to distribute a library of packages in a single tarball.
Unlike the other release types, a bundle release's <contents> tag must contain only the <bundledpackage> tag. The contents of the bundledpackage should be release names like "Foo-1.2.3.tgz"
In addition, the <bundle/> tag must be empty.
The steps required from package maintainers to release a package.
2003-03-20
Rolling a new release requires that the package to which the release belongs has been approved by the PEAR developers and that you have a valid PEAR website account. Information about meeting these two requirements can be found in the chapter Contributing your own code.
Please also read the various PEAR Group regulations that cover aspects like Version Naming, File Layout and other important topics not yet discussed in this manual. Furthermore there are a number of RFC's that followed these group regulations. These can be found under R for RFC in the finished PEPr proposals. Unfortunately these documents have not yet been included in a summarized coherent format in this manual. Please ask questions if you are in doubt about any of these documents.
Next, you should check the package definition file
(package.xml
) for validity. This is done by running the
command: pear package-validate package.xml. Fix the
errors and warnings, if any, and proceed to the next step.
For help with writing the package definition file see the package definition file description.
Now that your package has a valid package definition file, you can package the release tarball. Cd to the top-level directory of the package and run: pear package This will create the release tarball that will be used later to upload the new release.
Please note that the zlib extension needs to be enabled in your PHP build in order to create the release tarball.
Next, you should install the package locally by running: pear install <file> (file is the tarball you just created). This is done to ensure that the package definition file is not only valid but also contains valid information. You should manually check that every file is installed in the right place. If your package contains test scripts, which is highly recommended, you should run them.
If anything fails at this stage, correct it and re-package and re-test. When everything seems OK, proceed.
First, you should login to the PEAR website using the account you've been given (see the first step).
Now, finally, you can release your package. This is done again using the convenient web interface. Use the form at http://pear.php.net/release-upload.php.
That's it! Your package has been released, an announcement has been sent to the mailing lists.
You should subscribe yourself to the pear-general mailing list. This, besides IRC, is the first place users of your package will ask for help.
2002-05-27
There are numerous ways to support the PEAR project, that are described in this part of the guide.
There is a chapter devoted to reporting and fixing bugs in the "Reporting Bugs" section of the manual, found here.
If you have a feature you would like to see included in a package, don't hesitate to contact the maintainer of the package. You can find out who the maintainer of a package is by browsing the package list on the PEAR website and selecting the desired package. The maintainer(s) is/are listed on the package information page.
If a developer has given up the maintainership of his package, you can take over this job. If you feel brave enough to answer support questions, fix bugs and manage release cycles, you can contact the previous maintainer or the PEAR developers mailing list and announce your will to maintain the package.
For all the PEAR developers, PEAR is a project they are working on in their free-time, which means that they don't earn any money with it. If you or the company you are working for is using a PEAR package in one of your/their (commercial) products, you/they can do this for free, of course. But if you think that the author of the package you are using desires some credits, it would be nice to buy something from his wishlist at Amazon or another internet store. To see if a PEAR developer has registered his wishlist, surf to the Account information page and select the developer from the list there. If he has a wishlist, it's noted somewhere on the detail page.
Documentation is a critical part of PEAR. Even the most interesting and helpful package will not be used without proper documentation that helps users understand it.
Documentation for a package in PEAR means two things:
API documentation that explains all classes, methods and functions a package provides
Chapters and paragraphs about the package itself: What the package can be used for, how to use it, and examples.
API documentation is automatically generated after a package has been released on pear.php.net from the code using phpDocumentor.
The second type of documentation needs to be created manually. It is the documentation you see in this very manual below the packages book.
At first you will learn how to obtain and render this manual before going into detail about writing documentation for your own package.
The Docbook XML sources of the documentation are hosted on php.net's
SVN server in the peardoc
module.
The checkout process is relatively simple:
$ svn checkout https://svn.php.net:/repository/pear/peardoc/trunk
Details about this process as well as anonymous login data can be found on php.net's SVN page.
You need rights for the
peardoc
SVN module to be able to commit. Along with pear karma, every developer should have gotten the appropriate rights topeardoc
. This is how it should be in theory, but one will probably have to send a mail to pear-dev@lists.php.net asking for documentation karma.
The PEAR documentation gets - like the PHP Manual - built using PHP's very own DocBook rendering system called PhD. Installation is a breeze using pear:
$ pear install doc.php.net/phd-beta doc.php.net/phd_pear-beta
...
$ phd --version
PhD version: phd-from-cvs
Copyright (c) 2007-2009 The PHP Documentation Group
When using PEAR before verson 1.8.0, you need to discover the channel first.
Besides that, one only needs a SVN checkout of the peardoc module.
Before building, you need to use the
configure.php
script provided by
peardoc
. This script serves two purposes:
Validate XML
Combine all xml files into one large single file to speed up the build process
Just change into your peardoc
source folder
and run:
$ php configure.php
Generating chapters.ent for en
3376 xml files
6 php example files
done
Loading manual into one giant file
Validating done
Now call phd: phd -L en -P PEAR -f xhtml -o build/en -d .manual.xml
If you see those lines, everything is fine and you can continue compiling the manual.
Like with every proper unix tool, you can use php configure.php --help to get an overview about the supported command line parameters.
In case something goes wrong, the config script will tell you either where it failed, or which commands to execute to find the right spot:
$ php configure.php
Generating chapters.ent for en
3376 xml files
6 php example files
done
Loading manual into one giant file
There were warnings loading the manual Warning:
DOMDocument::load(): Opening and ending tag mismatch:
set line 46 and book in
/home/cweiske/Dev/cvs/pear/peardoc/manual.xml, line: 67 in
/home/cweiske/Dev/cvs/pear/peardoc/configure.php on line 278
....
Exception: Failed to load manual.xml
Fix the error and run configure.php
again
until it tells you everything is ok.
Now that you configured everything properly, run the command that
configure.php
told you. The command will be
something along that:
$ phd -L en -P PEAR -f xhtml -o build/en/ -d .manual.xml
configure.php
generated a giant manual file named.manual.xml
. If you use plainmanual.xml
your build will take about double as long as with.manual.xml
(note the leading dot).
When PhD doesn't report any errors, your generated HTML
documentation will be available in
build/en/html/
.
When writing documentation for a single package, you probably
don't always want to compile the whole manual. PhD allows you to
compile a small part of the manual which is faster than doing the
whole thing. Just pass "-p
package.category.packagename
" as parameter to PhD, and only
the part of the manual below that ID will be created.
At the time of writing, partial rendering in PhD is not noticable faster than creating the whole manual. This will change in the future, though.
In case you didn't change the structure (adding or removing IDs), you can skip PhD index creation (
-I
) which will save you about 15-20 seconds build time.
Several PEAR members regularly build the documentation on their own machine in shorter cycles than the pear.php.net server. When you don't have the chance to test and build your changes, you can use any of this build servers to check everything is fine:
PEAR member | Build interval | Format | Build log |
---|---|---|---|
Brett Bieber | 2 hours | Chunked HTML | log |
Benedikt Hallinger | at 06:00 and 18:00 CEST | Chunked HTML | log |
DocBook is an XML dialect that is used by a wide range of projects to maintain their documentation. Examples for DocBook usage in OpenSource projects are the documentations of KDE and PHP. PEAR has opted for using DocBook because we believe that it provides a solid foundation for the technical documentation for PEAR packages.
The trade-off for using DocBook is that it is relatively hard to use. Testing documentation requires a special tool to be installed and one needs to learn a (not very complicated) XML dialect. Once one is familiar with how DocBook works they will enjoy writing documentation with it though.
The book DocBook: The Definitive Guide, written by Norman Walsh and Leonard Muellner and published by O'Reilly & Associates, Inc., is available online and it makes up a great resource for people interested in learning DocBook.
Definitely check out the book's DocBook Element Reference section. This portion provides detailed information about each element, including which elements can (and must) be used as parents and children.
Instead of a long and boring description for writing documentation using DocBook, we would like to point you to a bunch of "reference documents", from which you should be able to learn quickly:
Console_ProgressBar - small documentation for a small package
System_Daemon - medium sized documentation on several pages
The scope of your package's documentation should be:
Explain which problem your package solves
Describe differences to other packages solving identical or similar problems
Give example how to use the package
(Maybe) give an introduction to concepts your package uses and that the user probably does not know/is not aware of
The documentation should not repeat the API docs! You should only link to it. The documentation for older packages often contains the whole API reference, but that is not the manual's scope anymore - phpDocumentor does a much better job at generating it automatically.
Filename | Description |
---|---|
packages/ |
Directory containing all package-specific documentation |
category.xml |
Description of a category |
category-entities.xml |
File with links to all packages in that category. Exists in english documentation only. |
category/ |
Directory with documentation of packages in that category |
package.xml |
Main file for documentation of a specific package |
package/ |
Files for the package documentation are collected in
here, e.g. examples.xml |
Before committing your changes into peardoc SVN, test and build it! This is absolutely necessary - it makes sure that other people writing documentation can build the manual without problems, and that the official pear.php.net build works.
The manual on pear.php.net is build once a week, 12:00 UTC every sunday. If it breaks, it will take a whole week until the next build attempt is made!
So do not commit updates shortly before the main build happens.
If you don't have peardoc karma, write to the peardoc mailing list to get it. It is also possible to send a patch to the list and get someone with karma to commit it.
This section of the chapter does not deal with the specifics of organizing documentation in the peardoc standard, but instead with how to organize documentation logically.
Every package solves a problem. What is this problem? Try to figure out what assumptions your end-users might not have about the problem (they may not realize that this is a problem that needs solving). For instance, a template package solves the problem of both separating design from code, and separating business logic from display logic. If possible, explain the problem in terms that even a novice programmer can understand.
Next, how does the package uniquely solve the problem? This is something that most documentation lacks. For example, there are many template engines. All of them solve the same problem, but none of them do it in the same way. A block-based template engine does not have any logic at all, whereas a template like Smarty defines a whole new template language. Some template engines compile their templates, others don't. What is unique about your package? Can someone who has never seen the code get a good idea of how it solves the problem?
Provide examples! Start right away with simple examples that show the basic feature set -- they will show users how to quickly start using the package. More complex examples will help the users in understanding advanced ways of using the package.
If your package exposes complex interfaces or multiple constants that can't be fully explained in one or two examples (which is very likely), it is still important to explain them thoroughly in the documentation. Document any interfaces that users must use, such as a database DSN, command-line arguments for applications, configuration file contents, or any other non-code elements.
Last, proofread your documentation. If possible, have someone else who is not as familiar with your project take a look at the documentation. They will catch assumptions that you have missed.
PEAR documentation is mostly written using plain text editors like
vim
or Kate
. XML editors mostly
don't help enough to be more useful than text editors; plus no tool yet
is able to work with the entity linking structure used in
peardoc.
One way to get documentation easily is writing initial package documentation using an XML editor like XXE, and make the necessary adjustments for peardoc after that. After doing that, you probably won't be able to use the XML editor anymore without losing data.
FIXME
We are well aware that we cannot cover all questions about writing
DocBook documentation in this chapter. If you have more questions or
problems, do not hesitate to get in touch with the documentation team at
pear-doc@lists.php.net.
To join the pear-doc
list send an email to pear-doc-subscribe@lists.php.net.
package.xml
(deprecated)package.xml
package.xml version 1.0 is deprecated
package.xml 1.0 is deprecated. You should really be using package.xml version 2.0. Documentation can be found at package.xml 2.0 documentation.
As of 2007-04, More than 99.8% of all PEAR installations in the wild are capable of using package.xml 2.0 files, so you should not worry about backwards compatibility.
The package definition file package.xml
is,
as the name already implies, a well-formed XML file that contains
all information about a PEAR package.
This chapter will describe the allowed elements of the package definition file and it will discuss how to create such a file for your package.
The PEAR_PackageFileManager package simplifies the creation of package.xml. You can install PEAR_PackageFileManager via the usual command
$ pear install PEAR_PackageFileManager
The toplevel element in package.xml
is the
element <package version="1.0">
. The
allowed sub elements are:
<name>
: The name of the package.
<summary>
: Short summary of the
package's description.
<description>
: Full length description
of the package.
<license>
: The license of the package
(LGPL, PHP License etc.).
<maintainers>
: Information about the
maintainers of the package.
maintainer: Information about a single maintainer. (May be used multiple times.)
<user>
: The account name of the user.
<role>
: The role the user has
during package development. (Can be either lead, developer,
helper.)
<name>
: The realname of the user.
<email>
: The email address of the user.
<release>
: Information about the current
release.
<version>
: The version number of
the release.
<state>
: The state of the release.
(Can be one of stable, beta, alpha, devel, or snapshot.)
<date>
: The date when the release has
been rolled.
<license>
: The license
under which the code is available.
<notes>
: Releasenotes
<filelist>
<file name="xxx" role="xxx" />
: Filename
<dir name="xxx" [role="xxx"]>
: Name
of a subdirectory. This subdirectory can again contain
<file role="xxx">
entries.
<deps>
: List of dependencies of the
package.
<dep type="xxx" rel="yyy" optional="yes">name</dep>
: For more information about dependencies, please
see
below.
<changelog>
: Changelog-like information
about the package.
<release>
<version>
: Version of the
specific release.
<state>
: State of the specific
release.
<date>
: Date when the specific
release has been rolled.
<notes>
: Changelog information
The letters allowed inside elements are A-Z and a-z.
Other characters, such as é
must use entities
(in this case: é
).
If you create your package.xml files using the PEAR_PackageFileManager, upgrade your PEAR installation to version 1.4.0a2 or greater and you won't have to worry about this because the file manager takes care of this automatically.
If you write your package.xml files manually, you will need to enter the entities yourself. A list of the most common entities can be found at: http://www.evolt.org/article/A_Simple_Character_Entity_Chart/17/21234/ If the characters you need aren't in that list, go to http://www.oasis-open.org/docbook/xmlcharent/0.1/index.shtml and look at the other entity lists.
In order to validate package.xml
files one
can use the xmllint tool that comes with
libxml2.
xmllint --dtdvalid http://pear.php.net/dtd/package-1.0 --noout package.xml
Basic package.xml
<?xml version="1.0" encoding="ISO-8859-1" ?> <package version="1.0"> <name>Money_Fast</name> <summary>Make money fast.</summary> <description> This package helps you to make money pretty fast. </description> <license>PHP License</license> <maintainers> <maintainer> <user>foo</user> <name>Joe Foo</name> <email>foo@example.com</email> <role>lead</role> </maintainer> </maintainers> <release> <version>1.0</version> <date>2002-05-27</date> <state>stable</state> <notes> This is the first release. </notes> <filelist> <dir name="/" baseinstalldir="Money"> <file role="php" name="Fast.php" /> </dir> </filelist> </release> </package>
This package.xml
can serve as a template for
you as it already contains all necessary elements. In most cases
you only need to change the character data between the tags in order
to use the example in your package.
Example for nested directories
<?xml version="1.0" encoding="ISO-8859-1" ?> [...] <release> <version>1.0</version> <date>2002-07-23</date> <state>stable</state> <notes> This is the first release. </notes> <filelist> <dir name="/" baseinstalldir="Money"> <file role="php" name="Fast.php" /> <dir name="Calculator"> <file name="Calculator.php" role="php" /> <file name="Currency.php" role="php" /> <file name="Stocks.php" role="php" /> </dir> <dir name="docs"> <file name="README.txt" role="doc" /> <file name="tutorial.txt" role="doc" /> <dir name="examples"> <file name="NASDAQ.php" role="php" /> <file name="DAX.php" role="php" /> </dir> </dir> </dir> </filelist> </release> </package>
In this example you get to know a very handy feature: When you
have a directory in your package that only contains files of
the same type, you can add to role attribute even to the
<dir>
tag instead of adding it to every
single <file>
tag.
With the knowledge you've acquired from this chapter you should now be able to produce a package definition file for your own package. If you still have questions concerning the topic, don't hesitate to ask on the mailinglist.
The role
-attribute in the <file> tag
defines what type the file has and in which location it should
be installed.
Value | Destination dir | |
---|---|---|
php | PHP source file | the directory is determined by the package name |
ext | Extension, dynamically loadable library | the PHP extension directory or PHP_PEAR_EXTENSION_DIR if defined |
doc | Documentation file | {PEAR_documentation_dir}/Package_Name/ |
data | Package related data files (graphics, data tables etc) | {PEAR_data_dir}/Package_Name/ |
test | Package related test files (unit-tests etc) | {PEAR_test_dir}/Package_Name/ |
script | Package related shell scripts | the PHP binary directory or PHP_PEAR_BIN_DIR if defined |
src and extsrc | C or C++ source code | not copied directly - used to build a extension |
The PEAR Package Manager supports checking for
different system capabilities. You define those
dependencies with the <dep>
tag:
package.xml
with dependencies
The following example shows how to specify dependencies for PHP 4.3.0 or better and XML_Parser 1.0.
<?xml version="1.0" encoding="ISO-8859-1" ?> [...] </release> <deps> <dep type="php" rel="ge" version="4.3.0" /> <dep type="pkg" rel="has" version="1.0">XML_Parser</dep> </deps> </package>
type
-attribute
The following type
s are supported:
Value | Meaning | Example | |
---|---|---|---|
pkg | Package | depends on a certain Package | "HTML_Flexy" |
ext | Extension | depends on a certain PHP extension | "curl" |
php | PHP | depends on a certain PHP version | "4.2" |
prog | Program | depends on a certain Program available in the system path. This is not supported in the PEAR installer. | "latex" |
os | Operating System | depends on a certain OS version | "Linux" |
sapi | Server API | depends on a certain Server API. This is not supported in the PEAR installer. | "Apache" |
zend | Zend | depends on a certain version of the Zend API. This is not supported in the PEAR installer. | "2" |
The DTD for the package definition file supports further types, but those are not supported yet.
rel
-attribute
The rel
-attribute
defines the relationship between the existing
capability and the required.
Value | Meaning | Can be used with | |
---|---|---|---|
has | has | the existing capability must have the requirement - version-attribute is ignored | pkg, ext, php, prog, os, sapi, zend |
eq | equal | the existing capability must exactly match the version value | pkg, ext, php, prog, os, sapi, zend |
lt | less than | the existing capability must be less than the version value | pkg, ext, php, zend |
le | less than or equal | the existing capability must be less than or equal to the version value | pkg, ext, php, zend |
gt | greater than | the existing capability must be greater than the version value | pkg, ext, php, zend |
ge | greater than or equal | the existing capability must greater than or equal to the version value | pkg, ext, php, zend |
not | conflicting dependency | the dependency conflicts with the package, the two cannot co-exist. version is ignored. | ext, php |
Has
will be used if no other value has been
defined. Note that rel is required in PEAR 1.4.0 and newer.
version
-attribute
The attribute version
defines the version
that is used to compare.
optional
-attribute
The attribute optional
can be used when
a dependency is not required but having the package installed
can bring enhanced functionalities. The only legal values are "yes"
and "no". If the optional attribute is not present, a dependency is
required.
When optional="yes"
is used, this attribute
will result in installation messages similar to the following messages:
$ pear install <package>
Optional dependencies:
Package `XML_Tree' is recommended to utilize some features.
Package `MDB' is recommended to utilize some features.
The PEAR Coding Standards apply to code that is part of the official PEAR distribution. Coding standards often abbreviated as CS among developers and they aim to keep code consistent to be easily readable and maintainable by most of PEAR folks.
The original coding standards have been adjusted several times through RFCs:
Those RFCs have been integrated into this document.
The PEAR2 Coding Standards define several other rules that have to be followed once PEAR2 is in place.
Use an indent of 4 spaces, with no tabs. This helps to avoid problems with diffs, patches, SVN history and annotations.
For Emacs you should set indent-tabs-mode to nil. Here is an example mode hook that will set up Emacs (ensure that it is called when you are editing PHP files):
(defun pear/php-mode-init() "Set some buffer-local variables." (setq case-fold-search t) (c-set-offset 'arglist-intro '+) (c-set-offset 'arglist-close '0) ) (add-hook 'php-mode-hook 'pear/php-mode-init)
Here are Vim rules for the same thing:
set expandtab set shiftwidth=4 set softtabstop=4 set tabstop=4
It is recommended to keep lines at approximately 75-85 characters long for better code readability. Paul M. Jones has some thoughts about that limit.
These include if, for, while, switch, etc. Here is an example if statement, since it is the most complicated of them:
<?php
if ((condition1) || (condition2)) {
action1;
} elseif ((condition3) && (condition4)) {
action2;
} else {
defaultaction;
}
?>
Control statements should have one space between the control keyword and opening parenthesis, to distinguish them from function calls.
You are strongly encouraged to always use curly braces even in situations where they are technically optional. Having them increases readability and decreases the likelihood of logic errors being introduced when new lines are added.
For switch statements:
<?php
switch (condition) {
case 1:
action1;
break;
case 2:
action2;
break;
default:
defaultaction;
break;
}
?>
Long if statements may be split onto several lines when the
character/line limit would be exceeded. The conditions have to be
positioned onto the following line, and indented 4 characters. The logical
operators (&&
, ||
, etc.)
should be at the beginning of the line to make it easier to comment (and
exclude) the condition. The closing parenthesis and opening brace get
their own line at the end of the conditions.
Keeping the operators at the beginning of the line has two advantages: It is trivial to comment out a particular line during development while keeping syntactically correct code (except of course the first line). Further is the logic kept at the front where it's not forgotten. Scanning such conditions is very easy since they are aligned below each other.
<?php
if (($condition1
|| $condition2)
&& $condition3
&& $condition4
) {
//code here
}
?>
The first condition may be aligned to the others.
<?php
if ( $condition1
|| $condition2
|| $condition3
) {
//code here
}
?>
The best case is of course when the line does not need to be split.
When the if clause is really long enough to be split, it might be better
to simplify it. In such cases, you could express conditions as variables
an compare them in the if()
condition. This has the
benefit of "naming" and splitting the condition sets into smaller, better
understandable chunks:
<?php
$is_foo = ($condition1 || $condition2);
$is_bar = ($condition3 && $condtion4);
if ($is_foo && $is_bar) {
// ....
}
?>
There were suggestions to indent the parantheses "groups" by 1 space for each grouping. This is too hard to achieve in your coding flow, since your tab key always produces 4 spaces. Indenting the if clauses would take too much finetuning.
The same rule as for if clauses also applies for the ternary operator: It may be split onto several lines, keeping the question mark and the colon at the front.
<?php
$a = $condition1 && $condition2
? $foo : $bar;
$b = $condition3 && $condition4
? $foo_man_this_is_too_long_what_should_i_do
: $bar;
?>
Functions should be called with no spaces between the function name, the opening parenthesis, and the first parameter; spaces between commas and each parameter, and no space between the last parameter, the closing parenthesis, and the semicolon. Here's an example:
<?php
$var = foo($bar, $baz, $quux);
?>
As displayed above, there should be one space on either side of an equals sign used to assign the return value of a function to a variable. In the case of a block of related assignments, more space may be inserted to promote readability:
<?php
$short = foo($bar);
$long_variable = foo($baz);
?>
To support readability, parameters in subsequent calls to the same function/method may be aligned by parameter name:
<?php
$this->callSomeFunction('param1', 'second', true);
$this->callSomeFunction('parameter2', 'third', false);
$this->callSomeFunction('3', 'verrrrrrylong', true);
?>
The CS require lines to have a maximum length of 80 chars. Calling functions or methods with many parameters while adhering to CS is impossible in that cases. It is allowed to split parameters in function calls onto several lines.
<?php
$this->someObject->subObject->callThisFunctionWithALongName(
$parameterOne, $parameterTwo,
$aVeryLongParameterThree
);
?>
Several parameters per line are allowed. Parameters need to be indented 4 spaces compared to the level of the function call. The opening parenthesis is to be put at the end of the function call line, the closing parenthesis gets its own line at the end of the parameters. This shows a visual end to the parameter indentations and follows the opening/closing brace rules for functions and conditionals.
The same applies not only for parameter variables, but also for nested function calls and for arrays.
<?php
$this->someObject->subObject->callThisFunctionWithALongName(
$this->someOtherFunc(
$this->someEvenOtherFunc(
'Help me!',
array(
'foo' => 'bar',
'spam' => 'eggs',
),
23
),
$this->someEvenOtherFunc()
),
$this->wowowowowow(12)
);
?>
Nesting those function parameters is allowed if it helps to make the code more readable, not only when it is necessary when the characters per line limit is reached.
Using fluent application programming interfaces often leads to many
concatenated function calls. Those calls may be split onto several lines.
When doing this, all subsequent lines are indented by 4 spaces and begin
with the "->
" arrow.
<?php
$someObject->someFunction("some", "parameter")
->someOtherFunc(23, 42)
->andAThirdFunction();
?>
To support readability, the equal signs may be aligned in block-related assignments:
<?php
$short = foo($bar);
$longer = foo($baz);
?>
The rule can be broken when the length of the variable name is at least 8 characters longer/shorter than the previous one:
<?php
$short = foo($bar);
$thisVariableNameIsVeeeeeeeeeeryLong = foo($baz);
?>
Assigments may be split onto several lines when the character/line limit would be exceeded. The equal sign has to be positioned onto the following line, and indented by 4 characters.
<?php
$GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName]
= $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax'));
?>
Class declarations have their opening brace on a new line:
<?php
class Foo_Bar
{
//... code goes here
}
?>
Function declarations follow the "K&R style":
<?php
function fooFunction($arg1, $arg2 = '')
{
if (condition) {
statement;
}
return $val;
}
?>
Arguments with default values go at the end of the argument list. Always attempt to return a meaningful value from a function if one is appropriate. Here is a slightly longer example:
<?php
function connect(&$dsn, $persistent = false)
{
if (is_array($dsn)) {
$dsninfo = &$dsn;
} else {
$dsninfo = DB::parseDSN($dsn);
}
if (!$dsninfo || !$dsninfo['phptype']) {
return $this->raiseError();
}
return true;
}
?>
Functions with many parameters may need to be split onto several lines
to keep the 80 characters/line limit. The first parameters may be put onto the
same line as the function name if there is enough space. Subsequent
parameters on following lines are to be indented 4 spaces. The closing
parenthesis and the opening brace are to be put onto the next line, on the
same indentation level as the "function
" keyword.
<?php
function someFunctionWithAVeryLongName($firstParameter = 'something', $secondParameter = 'booooo',
$third = null, $fourthParameter = false, $fifthParameter = 123.12,
$sixthParam = true
) {
//....
?>
Assignments in arrays may be aligned. When splitting array definitions onto several lines, the last value may also have a trailing comma. This is valid PHP syntax and helps to keep code diffs minimal:
<?php
$some_array = array(
'foo' => 'bar',
'spam' => 'ham',
);
?>
Complete inline documentation comment blocks (docblocks) must be provided. Please read the Sample File and Header Comment Blocks sections of the Coding Standards to learn the specifics of writing docblocks for PEAR packages. Further information can be found on the phpDocumentor website.
Non-documentation comments are strongly encouraged. A general rule of thumb is that if you look at a section of code and think "Wow, I don't want to try and describe that", you need to comment it before you forget how it works.
C style comments (/* */) and standard C++ comments (//) are both fine. Use of Perl/shell style comments (#) is discouraged.
Anywhere you are unconditionally including a class file, use require_once. Anywhere you are conditionally including a class file (for example, factory methods), use include_once. Either of these will ensure that class files are included only once. They share the same file list, so you don't need to worry about mixing them - a file included with require_once will not be included again by include_once.
include_once and require_once are statements, not functions. Parentheses should not surround the subject filename.
Always use <?php ?>
to
delimit PHP code, not the <? ?>
shorthand.
This is required for PEAR compliance and is also the most portable
way to include PHP code on differing operating systems and setups.
All source code files in the PEAR repository shall contain a "page-level" docblock at the top of each file and a "class-level" docblock immediately above each class. Below are examples of such docblocks.
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Short description for file
*
* Long description for file (if any)...
*
* PHP version 5
*
* LICENSE: This source file is subject to version 3.01 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_01.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category CategoryName
* @package PackageName
* @author Original Author <author@example.com>
* @author Another Author <another@example.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version SVN: $Id$
* @link http://pear.php.net/package/PackageName
* @see NetOther, Net_Sample::Net_Sample()
* @since File available since Release 1.2.0
* @deprecated File deprecated in Release 2.0.0
*/
/*
* Place includes, constant defines and $_GLOBAL settings here.
* Make sure they have appropriate docblocks to avoid phpDocumentor
* construing they are documented by the page-level docblock.
*/
/**
* Short description for class
*
* Long description for class (if any)...
*
* @category CategoryName
* @package PackageName
* @author Original Author <author@example.com>
* @author Another Author <another@example.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version Release: @package_version@
* @link http://pear.php.net/package/PackageName
* @see NetOther, Net_Sample::Net_Sample()
* @since Class available since Release 1.2.0
* @deprecated Class deprecated in Release 2.0.0
*/
class Foo_Bar
{
}
?>
Short descriptions must be provided for all docblocks. They should be a quick sentence, not the name of the item. Please read the Coding Standard's Sample File about how to write good descriptions.
One of the following must go in the page-level docblock:
* PHP version 4 * PHP version 5 * PHP version 7 * PHP versions 4 and 5 * PHP versions 5 and 7 * PHP versions 4, 5 and 7
There are several possible licenses. One of the following must be picked and placed in the page-level and class-level docblocks:
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 * @license http://www.freebsd.org/copyright/freebsd-license.html BSD License (2 Clause) * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause) * @license http://www.freebsd.org/copyright/license.html BSD License (4 Clause) * @license http://www.opensource.org/licenses/mit-license.html MIT License * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 * @license http://www.php.net/license/3_01.txt PHP License 3.01
For more information, see the PEAR Group's Licensing Announcement.
The following must be used in both the page-level and class-level docblocks. Of course, change "PackageName" to the name of your package. This ensures the generated documentation links back your package.
* @link http://pear.php.net/package/PackageName
There's no hard rule to determine when a new code contributor should be added to the list of authors for a given source file. In general, their changes should fall into the "substantial" category (meaning somewhere around 10% to 20% of code changes). Exceptions could be made for rewriting functions or contributing new logic.
Simple code reorganization or bug fixes would not justify the addition of a new individual to the list of authors.
This tag is required when a file or class is added after the package's initial release. Do not use it in an initial release.
This tag is required when a file or class is no longer used but has been left in place for backwards compatibility.
Feel free to apply whatever copyrights you desire. When formatting this tag, the year should be in four digit format and if a span of years is involved, use a hyphen between the earliest and latest year. The copyright holder can be you, a list of people, a company, the PHP Group, etc. Examples:
* @copyright 2003 John Doe and Jennifer Buck * @copyright 2001-2004 John Doe * @copyright 1997-2004 The PHP Group * @copyright 2001-2004 XYZ Corporation
If you are using the PHP License, use the summary text
provided above. If another license is being used,
please remove the PHP License summary. Feel free to
substitute it with text appropriate to your license,
though to keep things easy to locate, please preface
the text with LICENSE:
.
Add a @see tag when you want to refer users to other sections of the package's documentation. If you have multiple items, separate them with commas rather than adding multiple @see tags.
To ease long term readability of PEAR source code, the text and tags must conform to the order and spacing provided in the example above. This standard is adopted from the JavaDoc standard.
There are two ways to implement the @package_version@ replacements. The procedure depends on whether you write your own package.xml files or if you use the PackageFileManager.
For those authoring package.xml files directly, add a <replace> element for each file. The XML for such would look something like this:
<file name="Class.php"> <replace from="@package_version@" to="version" type="package-info" /> </file>
Maintainers using the PackageFileManager need to call addReplacement() for each file:
<?php
$pkg->addReplacement('filename.php', 'package-info',
'@package_version@', 'version');
?>
Existing packages that have only a few files are required to adopt these docblocks before the next release.
Existing packages with many files are encouraged to adopt the new headers as soon as possible. When such packages come out with a new major version upgrade, these docblocks must be implemented therein.
New packages and existing packages which have no releases yet must include these docblocks before their first release.
This section applies only to packages using SVN at svn.php.net.
Include the $Id$ SVN keyword in each file.
The rest of this section assumes that you have basic knowledge about SVN tags and branches.
SVN tags are used to label which revisions of the files in your package belong to a given release. Below is a list of the required and suggested SVN tags:
Only the RELEASE tag is required, the rest are recommended for your convenience.
Below is an example of how to tag the 1.2.0 release of the Money_Fast package:
$
svn copy https://svn.php.net/repository/pear/packages/Money_Fast/trunk https://svn.php.net/repository/pear/packages/Money_Fast/tags/RELEASE_1_2_0
By doing this you make it possible for the PEAR web site to take you through the rest of your release process.
Here's an example of how to create a QA branch:
$
svn copy https://svn.php.net/repository/pear/packages/Money_Fast/trunk https://svn.php.net/repository/pear/packages/Money_Fast/branches/QA_2_0_0 ...$
svn copy https://svn.php.net/repository/pear/packages/Money_Fast/branches/QA_2_0_0 https://svn.php.net/repository/pear/packages/Money_Fast/tags/RELEASE_2_0_0RC1 ...and then the actual release, from the same branch:$
svn copy https://svn.php.net/repository/pear/packages/Money_Fast/branches/QA_2_0_0 https://svn.php.net/repository/pear/packages/Money_Fast/tags/RELEASE_2_0_0
Use example.com
, example.org
and example.net
for all example URLs
and email addresses, per RFC 2606.
If your package needs to define global variables, their names should start with a single underscore followed by the package name and another underscore. For example, the PEAR package uses a global variable called $_PEAR_destructor_object_list.
Global functions should be named using the "studly caps" style (also referred to as "bumpy case" or "camel caps"). In addition, they should have the package name as a prefix, to avoid name collisions between packages. The initial letter of the name (after the prefix) is lowercase, and each letter that starts a new "word" is capitalized. An example:
XML_RPC_serializeData() |
Classes should be given descriptive names. Avoid using abbreviations where possible. Class names should always begin with an uppercase letter. The PEAR class hierarchy is also reflected in the class name, each level of the hierarchy separated with a single underscore. Examples of good class names are:
Log | Net_Finger | HTML_Upload_Error |
Class variables (a.k.a properties) and methods should be named using the "studly caps" style (also referred to as "bumpy case" or "camel caps"). Some examples (these would be "public" members):
$counter | connect() | getData() | buildSomeWidget() |
Private class members are preceded by a single underscore. For example:
$_status | _sort() | _initTree() |
The following applies to PHP5.
Protected class members are not preceded by a single underscore. For example:
protected $somevar | protected function initTree() |
Constants should always be all-uppercase, with underscores to separate words. Prefix constant names with the uppercased name of the class/package they are used in. Some examples:
DB_DATASOURCENAME | SERVICES_AMAZON_S3_LICENSEKEY |
Thetrue
,false
andnull
constants are excepted from the all-uppercase rule, and must always be lowercase.
All scripts contributed to PEAR must:
Be stored as ASCII text
Use ISO-8859-1 or UTF-8 character encoding. The encoding may be
declared using declare(encoding = 'utf-8');
at the
top of the file.
Be Unix formatted
"Unix formatted" means two things:
1) Lines must end only with a line feed (LF
).
Line feeds are represented as
ordinal 10
,
octal 012
and
hex 0A
.
Do not use carriage returns (CR
)
like Macintosh computers do
or the carriage return/line feed combination
(CRLF
) like Windows computers do.
2) There should be one line feed
after the closing PHP tag (?>
).
This means that when the cursor is at the very end
of the file, it should be one
line below the closing PHP tag.
E_STRICT
-compatible code
Starting on 01 January 2007, all new code that is suggested for
inclusion into PEAR must be
E_STRICT
-compatible. This means that it must
not produce any warnings or errors when PHP's error reporting level
is set to E_ALL | E_STRICT
.
The development of existing packages that are not
E_STRICT
-compatible can continue as usual. If
however a new major version of the package is
released, this major version must then be
E_STRICT
-compatible.
More details on this part of the Coding Standards can be found in the corresponding RFC.
This part of the Coding Standards describes how errors are handled in PEAR packages that are developed for PHP 5 and 6. It uses Exceptions, introduced in PHP 5.0 with Zend Engine 2, as the error handling mechanism.
An error is defined as an unexpected, invalid program state from which it is impossible to recover. For the sake of definition, recovery scope is defined as the method scope. Incomplete recovery is considered a recovery.
One pretty straightforward example for an error
<?php
/*
* Connect to Specified Database
*
* @throws Example_Datasource_Exception when it can't connect
* to specified DSN.
*/
function connectDB($dsn)
{
$this->db =& DB::connect($dsn);
if (DB::isError($this->db)) {
throw new Example_Datasource_Exception(
"Unable to connect to $dsn:" . $this->db->getMessage()
);
}
}
?>
In this example the objective of the method is to connect to the given DSN. Since it can't do anything but ask PEAR DB to do it, whenever DB returns an error, the only option is to bail out and launch the exception.
Error handling with recovery
<?php
/*
* Connect to one of the possible databases
*
* @throws Example_Datasource_Exception when it can't connect to
* any of the configured databases.
*
* @throws Example_Config_Exception when it can't find databases
* in the configuration.
*/
function connect(Config $conf)
{
$dsns =& $conf->searchPath(array('config', 'db'));
if ($dsns === FALSE) throw new Example_Config_Exception(
'Unable to find config/db section in configuration.'
);
$dsns =& $dsns->toArray();
foreach($dsns as $dsn) {
try {
$this->connectDB($dsn);
return;
} catch (Example_Datasource_Exception $e) {
// Some warning/logging code recording the failure
// to connect to one of the databases
}
}
throw new Example_Datasource_Exception(
'Unable to connect to any of the configured databases'
);
}
?>
This second example shows an exception being caught and recovered from. Although the lower level connectDB() method is unable to do anything but throw an error when one database connection fails, the upper level connect() method knows the object can go by with any one of the configured databases. Since the error was recovered from, the exception is silenced at this level and not rethrown.
Incomplete recovery
<?php
/*
* loadConfig parses the provided configuration. If the configuration
* is invalid, it will set the configuration to the default config.
*
*/
function loadConfig(Config $conf)
{
try {
$this->config = $conf->parse();
} catch (Config_Parse_Exception $e) {
// Warn/Log code goes here
// Perform incomplete recovery
$this->config = $this->defaultConfig;
}
}
?>
The recovery produces side effects, so it is considered incomplete. However, the program may proceed, so the exception is considered handled, and must not be rethrown. As in the previous example, when silencing the exception, logging or warning should occur.
Error conditions in PEAR packages written for PHP 5 must be signaled using exceptions. Usage of return codes or return PEAR_Error objects is deprecated in favor of exceptions. Naturally, packages providing compatibility with PHP 4 do not fall under these coding guidelines, and may thus use the error handling mechanisms defined in the PHP 4 PEAR coding guidelines.
An exception should be thrown whenever an error condition is met, according to the definition provided in the previous section. The thrown exception should contain enough information to debug the error and quickly identify the error cause. Note that, during production runs, no exception should reach the end-user, so there is no need for concern about technical complexity in the exception error messages.
The basic PEAR_Exception contains a textual error, describing the program state that led to the throw and, optionally, a wrapped lower level exception, containing more info on the lower level causes of the error.
The kind of information to be included in the exception is dependent on the error condition. From the point of view of exception throwing, there are three classes of error conditions:
Errors detected during precondition checks should contain a description of the failed check. If possible, the description should contain the violating value. Naturally, no wrapped exception can be included, as there isn't a lower level cause of the error. Example:
<?php
function divide($x, $y)
{
if ($y == 0) {
throw new Example_Aritmetic_Exception('Division by zero');
}
}
?>
Errors signaled via return codes by lower level libraries, if unrecoverable, should be turned into exceptions. The error description should try to convey all information contained in the original error. One example, is the connect method previously presented:
<?php
/*
* Connect to Specified Database
*
* @throws Example_Datasource_Exception when it can't connect to specified DSN.
*/
function connectDB($dsn)
{
$this->db =& DB::connect($dsn);
if (DB::isError($this->db)) {
throw new Example_Datasource_Exception(
"Unable to connect to $dsn:" . $this->db->getMessage()
);
}
}
?>
Lower library exceptions, if they can't be corrected, should either be rethrown or bubbled up. When rethrowing, the original exception must be wrapped inside the one being thrown. When letting the exception bubble up, the exception just isn't handled and will continue up the call stack in search of a handler.
Rethrowing an exception
<?php
function preTaxPrice($retailPrice, $taxRate)
{
try {
return $this->divide($retailPrice, 1 + $taxRate);
} catch (Example_Aritmetic_Exception $e) {
throw new Example_Tax_Exception('Invalid tax rate.', $e);
}
}
?>
Letting exceptions bubble up
<?php
function preTaxPrice($retailPrice, $taxRate)
{
return $this->divide($retailPrice, 1 + $taxRate);
}
?>
The case between rethrowing or bubbling up is one of software architecture: Exceptions should be bubbled up, except in these two cases:
Exceptions should never be used as normal program flow. If removing all exception handling logic (try-catch statements) from the program, the remaining code should represent the "One True Path" -- the flow that would be executed in the absence of errors.
This requirement is equivalent to requiring that exceptions be thrown only on error conditions, and never in normal program states.
One example of a method that wrongly uses the bubble up capability of exceptions to return a result from a deep recursion:
<?php
/**
* Recursively search a tree for string.
* @throws ResultException
*/
public function search(TreeNode $node, $data)
{
if ($node->data === $data) {
throw new ResultException( $node );
} else {
search( $node->leftChild, $data );
search( $node->rightChild, $data );
}
}
?>
In the example the ResultException is simply using the "eject!" qualities of exception handling to jump out of deeply nested recursion. When actually used to signify an error this is a very powerful feature, but in the example above this is simply lazy development.
All of PEAR packages exceptions must be descendant from PEAR_Exception. PEAR_Exception provides exception wrapping abilities, absent from the top level PHP Exception class, and needed to comply with the previous section requirements.
Additionally, each PEAR package must provide a top level exception, named <Package_Name>_Exception. It is considered best practice that the package never throws exceptions that aren't descendant from its top level exception.
Because PHP, unlike Java, does not require you to explicitly state which exceptions a method throws in the method signature, it is critical that exceptions be thoroughly documented in your method headers.
Exceptions should be documented using the
@throws
phpdoc keyword
<?php
/**
* This method searches for aliens.
*
* @return array Array of Aliens objects.
* @throws AntennaBrokenException If the impedence readings indicate
* that the antenna is broken.
*
* @throws AntennaInUseException If another process is using the
* antenna already.
*/
public function findAliens($color = 'green');
?>
In many cases middle layers of an application will rewrap any lower-level exceptions into more meaningful application exceptions. This also needs to be made clear:
<?php
/**
* Load session objects into shared memory.
*
* @throws LoadingException Any lower-level IOException will be wrapped
* and re-thrown as a LoadingException.
*/
public function loadSessionObjects();
?>
In other cases your method may simply be a conduit through which lower level exceptions can pass freely. As challenging as it may be, your method should also document which exceptions it is not catching.
<?php
/**
* Performs a batch of database queries (atomically, not in transaction).
* @throws SQLException Low-level SQL errors will bubble up through this method.
*/
public function batchExecute();
?>
Exceptions play a critical role in the API of your library. Developers using your library depend on accurate descriptions of where and why exceptions might be thrown from your package. Documentation is critical. Also maintaining the types of messages that are thrown is also an important requirement for maintaining backwards-compatibility.
Because Exceptions are critical to the API of your package, you must ensure that you don't break backwards compatibility (BC) by making changes to exceptions.
Things that break BC include:
Things that do not break BC:
There are other things not covered by PEAR Coding Standards which are mostly subject of personal preference and not directly related to readability of the code. Things like "single quotes vs double quotes" are features of PHP itself to make programming easier and there are no reasons not use one way in preference to another. Such best practices are left solely on developer to decide. The only recommendation could be made to keep consistency within package and respect personal style of other developers.
Related lines of code should be grouped into blocks, separated from each other to keep readability as high as possible. The definition of "related" depends on the code :)
For example:
<?php
if ($foo) {
$bar = 1;
}
if ($spam) {
$ham = 1;
}
if ($pinky) {
$brain = 1;
}
?>
is a lot easier to read when separated:
<?php
if ($foo) {
$bar = 1;
}
if ($spam) {
$ham = 1;
}
if ($pinky) {
$brain = 1;
}
?>
To keep readability in functions and methods, it is wise to return early if simple conditions apply that can be checked at the beginning of a method:
<?php
function foo($bar, $baz)
{
if ($foo) {
//assume
//that
//here
//is
//the
//whole
//logic
//of
//this
//method
return $calculated_value;
} else {
return null;
}
}
?>
It's better to return early, keeping indentation and brain power needed to follow the code low.
<?php
function foo($bar, $baz)
{
if (!$foo) {
return null;
}
//assume
//that
//here
//is
//the
//whole
//logic
//of
//this
//method
return $calculated_value;
}
?>
The source code of PEAR packages are read by thousands of people. Also, it is likely other people will become developers on your package at some point in the future. Therefore, it is important to make life easier for everyone by formatting the code and docblocks in standardized ways. People can then quickly find the information they are looking for because it is in the expected location. Your cooperation is appreciated.
Each docblock in the example contains many details about writing Docblock Comments. Following those instructions is important for two reasons. First, when docblocks are easy to read, users and developers can quickly ascertain what your code does. Second, the PEAR website now contains the phpDocumentor generated documentation for each release of each package, so keeping things straight here means the API docs on the website will be useful.
Please take note of the vertical and horizontal spacing. They are part of the standard.
The "fold markers" (// {{{
and
// }}}
) are optional.
If you aren't using fold markers,
remove foldmethod=marker
from the vim header.
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Short description for file
*
* Long description for file (if any)...
*
* PHP version 5
*
* LICENSE: This source file is subject to version 3.01 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_01.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category CategoryName
* @package PackageName
* @author Original Author <author@example.com>
* @author Another Author <another@example.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version SVN: $Id$
* @link http://pear.php.net/package/PackageName
* @see NetOther, Net_Sample::Net_Sample()
* @since File available since Release 1.2.0
* @deprecated File deprecated in Release 2.0.0
*/
/**
* This is a "Docblock Comment," also known as a "docblock." The class'
* docblock, below, contains a complete description of how to write these.
*/
require_once 'PEAR.php';
// {{{ constants
/**
* Methods return this if they succeed
*/
define('NET_SAMPLE_OK', 1);
// }}}
// {{{ GLOBALS
/**
* The number of objects created
* @global int $GLOBALS['_NET_SAMPLE_Count']
*/
$GLOBALS['_NET_SAMPLE_Count'] = 0;
// }}}
// {{{ Net_Sample
/**
* An example of how to write code to PEAR's standards
*
* Docblock comments start with "/**" at the top. Notice how the "/"
* lines up with the normal indenting and the asterisks on subsequent rows
* are in line with the first asterisk. The last line of comment text
* should be immediately followed on the next line by the closing asterisk
* and slash and then the item you are commenting on should be on the next
* line below that. Don't add extra lines. Please put a blank line
* between paragraphs as well as between the end of the description and
* the start of the @tags. Wrap comments before 80 columns in order to
* ease readability for a wide variety of users.
*
* Docblocks can only be used for programming constructs which allow them
* (classes, properties, methods, defines, includes, globals). See the
* phpDocumentor documentation for more information.
* http://phpdoc.org/docs/HTMLSmartyConverter/default/phpDocumentor/tutorial_phpDocumentor.howto.pkg.html
*
* The Javadoc Style Guide is an excellent resource for figuring out
* how to say what needs to be said in docblock comments. Much of what is
* written here is a summary of what is found there, though there are some
* cases where what's said here overrides what is said there.
* http://java.sun.com/j2se/javadoc/writingdoccomments/index.html#styleguide
*
* The first line of any docblock is the summary. Make them one short
* sentence, without a period at the end. Summaries for classes, properties
* and constants should omit the subject and simply state the object,
* because they are describing things rather than actions or behaviors.
*
* Below are the tags commonly used for classes. @category through @version
* are required. The remainder should only be used when necessary.
* Please use them in the order they appear here. phpDocumentor has
* several other tags available, feel free to use them.
*
* @category CategoryName
* @package PackageName
* @author Original Author <author@example.com>
* @author Another Author <another@example.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version Release: @package_version@
* @link http://pear.php.net/package/PackageName
* @see NetOther, Net_Sample::Net_Sample()
* @since Class available since Release 1.2.0
* @deprecated Class deprecated in Release 2.0.0
*/
class Net_Sample
{
// {{{ properties
/**
* The status of foo's universe
*
* Potential values are 'good', 'fair', 'poor' and 'unknown'.
*
* @var string
*/
var $foo = 'unknown';
/**
* The status of life
*
* Note that names of private properties or methods must be
* preceeded by an underscore.
*
* @var bool
* @access private
*/
var $_good = true;
// }}}
// {{{ setFoo()
/**
* Registers the status of foo's universe
*
* Summaries for methods should use 3rd person declarative rather
* than 2nd person imperative, beginning with a verb phrase.
*
* Summaries should add description beyond the method's name. The
* best method names are "self-documenting", meaning they tell you
* basically what the method does. If the summary merely repeats
* the method name in sentence form, it is not providing more
* information.
*
* Summary Examples:
* + Sets the label (preferred)
* + Set the label (avoid)
* + This method sets the label (avoid)
*
* Below are the tags commonly used for methods. A @param tag is
* required for each parameter the method has. The @return
* and @access tags are mandatory. The @throws tag is required if
* the method uses exceptions. @static is required if the method can
* be called statically. The remainder should only be used when
* necessary. Please use them in the order they appear here.
* phpDocumentor has several other tags available, feel free to use
* them.
*
* The @param tag contains the data type, then the parameter's
* name, followed by a description. By convention, the first noun in
* the description is the data type of the parameter. Articles like
* "a", "an", and "the" can precede the noun. The descriptions
* should start with a phrase. If further description is necessary,
* follow with sentences. Having two spaces between the name and the
* description aids readability.
*
* When writing a phrase, do not capitalize and do not end with a
* period:
* + the string to be tested
*
* When writing a phrase followed by a sentence, do not capitalize the
* phrase, but end it with a period to distinguish it from the start
* of the next sentence:
* + the string to be tested. Must use UTF-8 encoding.
*
* Return tags should contain the data type then a description of
* the data returned. The data type can be any of PHP's data types
* (int, float, bool, string, array, object, resource, mixed)
* and should contain the type primarily returned. For example, if
* a method returns an object when things work correctly but false
* when an error happens, say 'object' rather than 'mixed.' Use
* 'void' if nothing is returned.
*
* Here's an example of how to format examples:
* <code>
* require_once 'Net/Sample.php';
*
* $s = new Net_Sample();
* if (PEAR::isError($s)) {
* echo $s->getMessage() . "\n";
* }
* </code>
*
* Here is an example for non-php example or sample:
* <samp>
* pear install net_sample
* </samp>
*
* @param string $arg1 the string to quote
* @param int $arg2 an integer of how many problems happened.
* Indent to the description's starting point
* for long ones.
*
* @return int the integer of the set mode used. FALSE if foo
* foo could not be set.
* @throws exceptionclass [description]
*
* @access public
* @static
* @see Net_Sample::$foo, Net_Other::someMethod()
* @since Method available since Release 1.2.0
* @deprecated Method deprecated in Release 2.0.0
*/
function setFoo($arg1, $arg2 = 0)
{
/*
* This is a "Block Comment." The format is the same as
* Docblock Comments except there is only one asterisk at the
* top. phpDocumentor doesn't parse these.
*/
if ($arg1 == 'good' || $arg1 == 'fair') {
$this->foo = $arg1;
return 1;
} elseif ($arg1 == 'poor' && $arg2 > 1) {
$this->foo = 'poor';
return 2;
} else {
return false;
}
}
// }}}
}
// }}}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* c-hanging-comment-ender-p: nil
* End:
*/
?>
PEAR provides some tools to help developers keep their code clean and free of coding standards related errors.
For one there is PHP_CodeSniffer which can be used to detect coding standard errors in your scripts. Further, whole PEAR SVN repository is checked each night for violations - the results can be found at PEAR QA results overview page as well as on the linked subpages which explain the errors in detail.
Coding Standards to be used in PEAR2 (version 1)
2007-12-24
This document cannot be considered complete (e.g.: the future of namespaces is uncertain).
PEAR 1.x is very successful at managing the universe of PEAR-installable code. The new Pyrus installer is designed to expand that universe to include code that can also be easily embedded in non-PEAR applications and that runs identically when simply unzipped and when installed. The PEAR2 repository must adhere to different coding conventions than the PEAR repository to make this possible. This document itemizes all the changes to existing rules and coding standards found here. Any conflict between these standards and the existing standards resolves in favor of the new standards. These standards do not affect the coding standards for PEAR packages hosted at pear.php.net, only PEAR2 packages hosted at pear2.php.net.
require_once
introduces a rigidity to package
structure that limits the possible uses of a PEAR package.
Some of the problems:
require_once
can introduce up to a 10%
performance penalty on high-volume sites using multi-processor web
servers due to increased latency. However, most users would experience
at most 2% performance penalty on single-processor systems
(as measured by Yahoo! engineer Gopal Vijayaraghavan)
include_path
is required in order to use a package.
This makes it difficult to bundle a PEAR package within another
application with its own include_path
, to create a
single file containing needed classes, to move a PEAR package to a phar
archive without extensive source code modification.
require_once
is mixed with
conditional require_once
, this can result in
code that is uncacheable by opcode caches such as APC (to be
bundled with PHP 6).
require_once
requires that
include_path
already be set up to the correct value,
making it impossible to use a package without proper
include_path
.
Some of the benefits of require_once
:
__autoload()
with PEAR2_Autoload()
).
__autoload()
with
PEAR2_Autoload()
).
The removal of require_once
necessitates another method for loading
internal dependencies, both files within a package and external files.
This proposal introduces 2 possible methods for doing this:
use __autoload()
in conjunction with PEAR2's custom autoload solution
(found
here in svn)
In all cases, the onus of loading needed files is shifted to the end
user. However, for beginning users, the only required step is to
load PEAR2/Autoload.php
, which will be always bundled with new packages,
but only extracted if used as unzip-and-go (pyrus would simply install
the dependency on PEAR2, which would contain the needed base files
PEAR2_Exception and PEAR2_Autoload).
<?php
require '/full/path/to/PEAR2/Autoload.php';
// now you can start using all PEAR2 packages
?>
PEAR2/Autoload.php
automatically sets up include_path if it does not
contain the correct value, and also automatically declares an autoloader
either using __autoload()
or spl_autoload_register()
, preserving existing autoloaders set up by
the user.
We will describe the list of rules that form the standards
All classes and functions must have a namespace of at the minimum PEAR2. An example:
<?php
namespace PEAR2;
class MyClass {}
?>
Classes may use longer namespaces, for instance an HTTP_Request class may instead choose to use this declarative syntax:
<?php
namespace PEAR2\HTTP;
class Request {}
?>
As such, underscores are no longer required of any classes if there is a namespace. Class PEAR2_HTTP_Request instead becomes PEAR2\HTTP\Request. Package names, however, will use underscores, making PEAR2_HTTP_Request the package name.
No Exceptions to this rule
include/require/require_once/include_once is not allowed for loading class
files. Users will be expected to
load files either with __autoload()
or a customized
solution for more advanced users.
Instead, classes should simply be used. import with a comment describing
the class's location must be used to document all internal
dependencies (as written below). Instead of:
<?php
require_once 'PEAR2/OtherPackage.php';
$class = new PEAR2\OtherPackage;
?>
this class should simply be used:
<?php
$class = new PEAR2\OtherPackage;
?>
This allows packages to work without modification no matter how they are structured on-disk, including running out of a single large file, inside a phar archive, and provides much-needed flexibility.
No Exceptions to this rule
Follows the directory structure in the PEAR2 Subversion repository:
PEAR2/Package_Name/ src/ <-- all role="php" data/ <-- all role="data" tests/ <-- all role="tests" docs/ <-- all role="doc" www/ <-- all role="www" examples/ <-- role="doc" example files (php executable files that exemplify package usage)
Note that all package.xml files must specify a baseinstalldir of "/" for the src/ directory:
<contents> <dir name="/"> <dir name="src" baseinstalldir="/"> ... </contents>
Exceptions may be made to this rule with approval from the PEAR Group
All public classes must be in their own file with underscores (_) or
namespace separators (\) replaced by directory separator, so that
PEAR2_PackageName_Base class or
PEAR2\PackageName\Base class is always
located in PEAR2/PackageName/Base.php
(this is required to make autoload
work)
Exceptions may be made to this rule only with explicit approval from the PEAR Group via a public vote
PEAR2\Exception is used as base class for all exception classes. Each
package must define a base class that is packagename_Exception. For
example, the PEAR2\PackageName class defines an exception as follows in
PEAR2/PackageName/Exception.php
:
<?php
namespace PEAR2\PackageName;
class Exception extends PEAR2\Exception {}
?>
'PEAR2\Exception will be its own package'
No Exceptions to this rule
package.xml
replacement tasks should not be used to retrieve path
locations for php, data, or www files. Replacements
are still allowed in doc and test files.
The installation structure of a package has implicit
php_dir/src
installation location, and data files are always located in
php_dir/data/channel/PackageName/
. To retrieve a data
file within PEAR2/PackageName/Subfile.php
, you could
use code like this example
<?php
...
// retrieve data from info.txt
$info = file_get_contents(dirname(__FILE__) .
'../../../data/pear2.php.net/PEAR2_PackageName/info.txt');
?>
No Exceptions to this rule
Inside optional component loading methods (like factory or driver
loading, etc.)
class_exists
($classname, true) should be used where a "class not found"
fatal error
would be confusing. For example, when loading a driver, a graceful exit
via exception
with helpful error message is preferrable to the fatal error:
<?php
if (!class_exists("PEAR2_PackageName_Driver_$class", true)) {
throw new PEAR2\PackageName\Exception('Unknown driver ' .
$class . ', be sure the driver exists and is loaded
prior to use');
}
?>
This rule is optional and is a suggested coding practice
Policies in use in PEAR2 (version 0.2.0)
2008-04-07
This document cannot be considered complete.
List of rules that apply to PEAR2
All collective members have full commit access and can commit minor bugfixes without requesting permission or notifying lead developers. However, as a courtesy, it is expected that all developers will communicate with a package's lead developers prior to taking action. If a dispute over a commit arises, it is also expected that the commit in question will be reverted pending a review.
This rule is intended to encourage open and friendly collaboration. For an example of this commit model, the PHP project developers often commit minor fixes for thread-safe errors, spelling mistakes, and other obvious mistakes without consultation. Exceptions can be made to this rule with explicit PEAR Group approval documented on the website.
All design decisions should be discussed freely in a Collective, and each Collective is responsible for enforcing these decisions in their own way. Some Collectives may require packages to adhere to basic interfaces, others may require common data storage formats, but individual package maintainers must adhere to these decisions or discuss ways of improving them. No cowboy coding! Ultimately, individual package developers have the full freedom to innovate without expecting too much interference, but are expected to learn how to both give and receive constructive criticism as a part of the privilege of having their code hosted at pear.php.net.
All collective members can pull a release within 24 hours if there is a major regression or security bug. If at all possible, the primary lead maintainers should perform this action, but Collectives function as a unit, and have the role of policing huge problems.
Competing packages are allowed at the alpha level in PEAR2.
All alpha/devel stability packages have no claim to a package name. If a better package comes along that does the same thing, wants your package's name and achieves beta status, you have to either join forces or change the name of your package. The limit is that newer packages have to be unarguably better in implementation. It is up to the Collective to define "unarguably better"
Any package that is alpha (in svn.pear.php.net/PEAR2/sandbox) and inactive for more than a year will be deleted
To become beta, a package must undergo extensive review.
Packages can be proposed either as 1) A completed, or relatively complete package to Pepr 2) A concept, or proof-of-concept to svn.pear.php.net/PEAR2/sandbox For proof-of-concept packages they should
Only packages with a status of beta or stable are added to the public channel
In order to submit code to PEAR2 you will need to use the provided svn repository.
2004-02-29
The following best practises describe topics which were discussed and agreed upon by the PEAR developers on the developers mailinglist. They aren't strict rules, which you need to follow (like Coding Standards), but are intended as guidelines for a common API scheme and easier package interoperability. Please consider following them in your packages where possible.
In case a package has to deal with colors, it is recommended that developers store and use an array representation of the color values in their PEAR packages. All values are in the range of 0..255.
The following snippet defines an array that holds the RGB definition of the green coloring that is used in the official PEAR logo.
<?php
$color = array(0x33, 0x99, 0x00);
?>
or
<?php
$color = array(51, 153, 0);
?>
Please note that the internal, numerical representation of both forms of the example are equal. The values represent the RGB (red, green, blue) values. It was decided not to use "r", "g" and "b" as indices for the array, because numerical indices allow easier creation of color arrays in the sourcecode, make handling less complicated and are an already commonly used array representation for colors.
Since this an extension of the RGB representation above, it is recommended to use a fourth value for alpha-channel-presentation. An example of the "PEAR-green" with approximately 80% intensity would be:
<?php
$color = array(0x33, 0x99, 0x00, 0xCC);
?>
For consistency the alpha-value is also from the range 0..255. A value of 255 means fully opaque (full color-intensity, same as in RGB- notation), 0 means fully transparent.
Please note this is in contrast to the alpha-value used by imagecolorallocate(). The alpha-representation used in PEAR was chosen for consistency with the other RGB values and because it is commonly accepted practice in many other applications (image-processing tools, etc.).
Since RGB/RGBA will used for graphic generation on a computer we decided to leave out an optional type-identifier classifying colors as RGB/RGBA. However for different color-representations an identifier is needed. The optional identifier "type" can be added to the color array.
<?php
$color = array(0x33, 0x99, 0x00, 0xCC, "type" => "RGB");
?>
Currently the following types are defined, with array-indices from 0..n following the color names listed beside them:
Please note that also the RGBA representation has the type "RGB" since it's closely related.
The PEAR-package Image_Color will (hopefully soon) contain all needed functions to convert arbitrary color formats to the internal RGBA representation and convert different color representations.
Status: Currently Image_Color already offers functions to convert color-names (e.g. "green") and hex-representations-strings (e.g. "#339900") to the PEAR-RGBA-format. We are working to get alpha- channel support added hopefully soon and will later add functions for CYMK-conversion as well.
In driver-based packages you should check if the driver exists before loading it. A simple file_exists() does not work since it does not check the include path. fopen()'s third parameter does that, so we use it.
<?php
$driver = 'SomeDriver';
$class = 'My_Package_Driver_' . $driver;
$file = str_replace('_', '/', $class) . '.php';
//check if it exists and can be loaded
if (!@fclose(@fopen($file, 'r', true))) {
throw new My_Package_Driver_Exception(
'Driver ' . $driver . ' cannot be loaded.'
);
}
//continue with including the driver
require_once $file;
//...
?>
Before releasing a new package, you need to write the release notes in
package.xml
.
The release notes should clearly say what has changed in comparison to the
last release, or - when going to the next
stablility level,
like from beta to stable - list the changes since the last release of the same
stability.
You probably want to outline new features, as well as list the bugs which have been fixed. It is important not only to plainly write down the bug numbers, but also the bug titles. People tend to recall words better than remembering the issues associated with a number. The PEAR web interface automatically links structures like "bug #<number>". In case several people fixed patches, the user's handle can be appended to the message.
Bad release notes
- Fixed bug #11495
Good release notes
Changes since the last stable version 1.2.3: - Added foo and bar feature - Blubber is far more efficient Fixed bugs: - Removed deprecated cURL option CURLOPT_MUTE (Bug #13489) [ashnazg] - Changed the license to the New BSD License (Bug #13831) [helgi] Minor issues: - Converted package.xml to version 2.0 - Cleaned up code to (mostly) comply with phpcs
Every now and then your unit tests depend on external services - be it a database or LDAP server or a company's web service. Access to those services and servers require creditentials, be it username and password combinations or API keys.
Such confidential information may often not be distributed, and
your unit tests should not have them coded into.
Instead a separate config file template,
config.php.dist
, should be shipped.
To run the tests, the user creates a copy of that configuration
template, saves it as config.php
and adjusts it.
Exemplary directory layout
tests/
config.php.dist
config.php
Exemplary configuration template
<?php
$GLOBALS['My_Package_UnittestConfig'] = array(
'host' => 'FIXME',
'username' => 'FIXME',
'password' => 'FIXME',
'host' => 'FIXME',
);
?>
You should not die() if no config file is found but let the unit test continue gracefully - this is important in combined suites, when several packages are unit tested in a row.
Instead, you should check if it exists first:
Checking if the configuration file exists
<?php
//...
class My_Package_ClassTest extends PHPUnit_Framework_TestCase
{
protected $configExists = null;
//...
public function __construct($name = null)
{
parent::__construct($name);
$configFile = dirname(__FILE__) . '/config.php';
$this->configExists = file_exists($configFile);
if ($this->configExists) {
include_once $configFile;
}
}
//...
public function setUp()
{
if (!$this->configExists) {
$this->markTestSkipped('Unit test configuration is missing.');
}
//...
}
//...
}
?>
The Core section provides information about the base classes in PEAR
PEAR provides functions for handling errors and sets the behaviour in case of error. And, it gives package developers a set of functions to make their lives easier.
The PEAR base class provides standard functionality that is used by most PEAR classes. Normally you never make an instance of the PEAR class directly, you use it by subclassing it.
Its key features are:
If you inherit PEAR in a class called ClassName, you can define a method in it called _ClassName (the class name with an underscore prepended) that will be invoked when the request is over. This is not a destructor in the sense that you can "delete" an object and have the destructor called, but in the sense that PHP gives you a callback in the object when PHP is done executing. See the example below.
Important!
In order for destructors to work properly, you must instantiate your class with the "=& new" operator like this:
<?php
$obj =& new MyClass();
?>If you only use "= new", the object registered in PEAR's shutdown list will be a copy of the object at the time the constructor is called, and it will be this copy's "destructor" that will be called upon request shutdown.
PEAR's base class also provides a way of passing around more complex errors than a true/false value or a numeric code. A PEAR error is an object that is either an instance of the class PEAR_Error, or some class inheriting PEAR_Error.
One of the design criteria of PEAR's errors is that it should not force a particular type of output on the user, it should be possible to handle errors without any output at all if that is desirable. This makes it possible to handle errors gracefully, also when your output format is different from HTML (for example WML or some other XML format).
The error object can be configured to do a number of things when it is created, such as printing an error message, printing the message and exiting, raising an error with PHP's trigger_error() function, invoke a callback, or none of the above. This is typically specified in PEAR_Error's constructor, but all of the parameters are optional, and you can set up defaults for errors generated from each object based on the PEAR class. See the PEAR error examples for how to use it and the PEAR_Error reference for the full details.
The example below shows how to use the PEAR's "poor man's kinda emulated destructors" to implement a simple class that holds the contents of a file, lets you append data to the object and flushes the data back to the file at the end of the request:
PEAR: emulated destructors
<?php
require_once "PEAR.php";
class FileContainer extends PEAR
{
var $file = '';
var $contents = '';
var $modified = 0;
function FileContainer($file)
{
$this->PEAR(); // this calls the parent class constructor
$fp = fopen($file, "r");
if (!is_resource($fp)) {
return;
}
$this->file = $file;
while ($data = fread($fp, 2048)) {
$this->contents .= $data;
}
fclose($fp);
}
function append($str)
{
$this->contents .= $str;
$this->modified++;
}
// The "destructor" is named like the constructor
// but with an underscore in front.
function _FileContainer()
{
if ($this->modified) {
$fp = fopen($this->file, "w");
if (!is_resource($fp)) {
return;
}
fwrite($fp, $this->contents);
fclose($fp);
}
}
}
$fileobj =& new FileContainer("testfile");
$fileobj->append("this ends up at the end of the file\n");
// When the request is done and PHP shuts down, $fileobj's
// "destructor" is called and updates the file on disk.
?>
PEAR "destructors" use PHP's shutdown callbacks (register_shutdown_function()), and in PHP < 4.1, you can't output anything from these when PHP is running in a web server. So anything printed in a "destructor" gets lost except when PHP is used in command-line mode. In PHP 4.1 and higher, output can be also generated in the destructor. Also, see the warning about how to instantiate objects if you want to use the destructor.
The next examples illustrate different ways of using PEAR's error handling mechanism.
PEAR error example (1)
<?php
function mysockopen($host = "localhost", $port = 8090)
{
$fp = fsockopen($host, $port, $errno, $errstr);
if (!is_resource($fp)) {
return new PEAR_Error($errstr, $errno);
}
return $fp;
}
$sock = mysockopen();
if (PEAR::isError($sock)) {
print "mysockopen error: ".$sock->getMessage()."\n";
}
?>
This example shows a wrapper to fsockopen() that delivers the error code and message (if any) returned by fsockopen in a PEAR error object. Notice that PEAR::isError() is used to detect whether a value is a PEAR error.
PEAR_Error's mode of operation in this example is simply returning the error object and leaving the rest to the user (programmer). This is the default error mode.
In the next example we're showing how to use default error modes:
PEAR error example (2)
<?php
class TCP_Socket extends PEAR
{
var $sock;
function TCP_Socket()
{
$this->PEAR();
}
function connect($host, $port)
{
$sock = fsockopen($host, $port, $errno, $errstr);
if (!is_resource($sock)) {
return $this->raiseError($errstr, $errno);
}
}
}
$sock = new TCP_Socket;
$sock->setErrorHandling(PEAR_ERROR_DIE);
$sock->connect('localhost', 8090);
print "still alive\n";
?>
Here, we set the default error mode to PEAR_ERROR_DIE, and since we don't specify any error mode in the raiseError call (that'd be the third parameter), raiseError uses the default error mode and exits if fsockopen fails.
The PEAR class uses some global variables to register global
defaults, and an object list used by the "destructors". All of
the global variables associated with the PEAR class have a
_PEAR_
name prefix.
Don't set this variable directly, call PEAR::setErrorHandling() as a static method like this:
<?php
PEAR::setErrorHandling(PEAR_ERROR_DIE);
?>
Don't set this variable directly, call PEAR::setErrorHandling() as a static method like this:
<?php
PEAR::setErrorHandling(PEAR_ERROR_TRIGGER, E_USER_ERROR);
?>
Again, don't set this variable directly, call PEAR::setErrorHandling() as a static method like this:
<?php
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, "my_error_handler");
?>
Here is an example of how you can switch back and forth without specifying the callback function again:
<?php
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, "my_function_handler");
do_some_stuff();
PEAR::setErrorHandling(PEAR_ERROR_DIE);
do_some_critical_stuff();
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK);
// now we're back to using my_function_handler again
?>
void PEAR::PEAR (
string
$errorClass = PEAR_Error
)
If you want to use the deconstructor functionality provide by PEAR, you have to call $this->PEAR() in the constructor of your class.
string $errorClass
- the name of the error class
to use.
This function can be called statically.
void PEAR::_PEAR (
)
Does nothing right now, but is included for forward compatibility, so subclass destructors should always call it.
mixed &PEAR::getStaticProperty (
string $class
,
string $var
)
If you have a class that's mostly/entirely static, and you need static properties, you can use this method to simulate them. Eg. in your method(s) do this:
<?php
$myVar = &PEAR::getStaticProperty('myVar');
?>
You must use a reference, or they will not persist!
string $class
- the name of your class, where you
call getStaticProperty()
string $var
the variable to retrieve.
mixed
-
A reference to the variable. If not set, it will be
auto initialised to NULL.
Using getStaticProperty()
<?php
require_once 'PEAR.php';
class myClass {
function setValue( $set)
{
$foo = &PEAR::getStaticProperty('myClass', "foo");
$foo = $set;
}
function view()
{
print PEAR::getStaticProperty('myClass', "foo");
}
}
myClass::setValue('value = foo');
myClass::view();
?>
This would print
value = foo
void PEAR::registerShutdownFunc (
array $func
,
array $var = array()
)
The indicated function is called, before the PHP interpreter will be finished.
array $func
- the name of the class and of the
function to ccore.
array $var
- possible required function
parameters. The parameters are handed over to the function in accordance with their
succession of the array.
Using registerShutdownFunc()
<?php
require_once 'PEAR.php';
class myClass {
function myClass()
{
PEAR::registerShutdownFunc(array('myClass', 'shutdown'),
array('param1', 'param2'));
}
function shutdown( $param1, $param2)
{
// do something before we will die
}
}
?>
boolean PEAR::isError (
mixed $data
,
mixed $msgcode
)
isError() examines whether a variable is a PEAR_Error object and - optional - contains a specific error message or code.
$data
variable to check
$msgcode
additional error message or error code to check
mixed
- returns TRUE, if the variable was a
PEAR_Error and, if given, contains
$msgcode
PEAR_Error PEAR::raiseError (
mixed $message
,
int $code
,
int $mode
,
int|array $options
,
mixed $userinfo
,
string $error_class
,
boolean $skipmsg
)
raiseError()
$message
Error message string or PEAR_Error object.
The default message is unknown error
if left blank.
$code
Error code. It is recommended to use an error code for even the simplest errors, in order to simplify error handling and processing.
$mode
Error mode. This is one of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, PEAR_ERROR_CALLBACK, or PEAR_ERROR_EXCEPTION. See setErrorHandling() for detailed information and examples of the meaning of these constants.
$options
Error options. This depends on the value of $mode
,
and is documented in setErrorHandling().
$userinfo
Optional user information. This can be used to store any error-specific information, and has an unspecified format.
$error_class
Error class name to use as the error object. The default error class is PEAR_Error. Use this parameter to specify another class to use, such as a custom class extending PEAR_Error
$skipmsg
Use this parameter if you are using a custom class that does not
accept an error message in its constructor. Never
use this parameter without the $error_class
parameter - it will not work.
A PEAR_Error object is returned, unless PEAR_ERROR_DIE terminates execution or a PEAR_ERROR_EXCEPTION is never handled.
void PEAR::setErrorHandling (
integer $mode
= null
,
mixed $options
= null
)
setErrorHandling() can be invoked as both a standard object method ($obj->setErrorHandling) and as a static method (PEAR::setErrorHandling). If called statically, PEAR::setErrorHandling() sets the default error handling behaviour for all PEAR objects (global error handling behaviour). If called as an object method, $obj->setErrorHandling() sets the default error handling for only that object (local error handling behaviour).
integer $mode
- one of the following constants
PEAR_ERROR_RETURN If an error occurs, a PEAR_Error is returned from the error-generation method (normally raiseError().)
PEAR_ERROR_PRINT Like PEAR_ERROR_RETURN, but an error message will be printed additionally.
PEAR_ERROR_TRIGGER Like PEAR_ERROR_RETURN, but the PHP builtin-function trigger_error() will be called in PEAR_Error's constructor with the error message.
PEAR_ERROR_DIE The script will terminate and an error message will be printed on instantiation of a PEAR_Error.
PEAR_ERROR_CALLBACK If a error occurs, the
callback passed to $options
is called.
PEAR_ERROR_EXCEPTION If Zend Engine 2 is present, then an exception will be thrown using the PEAR_Error object.
mixed $options
- the value for $options
depends on $mode
PEAR_ERROR_PRINT and PEAR_ERROR_DIE support an optional printf() format string used when printing the error message. This format string should contain a single %s, which will be used to insert the error message into the string. Use the string to enclose the error message with other useful information not included in the error message prefix or error message.
PEAR_ERROR_TRIGGER requires an user error level constant used by trigger_error() (possible constants: E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). Note that if the error constant is not one of these valid error constants, a PHP warning will be triggered.
PEAR_ERROR_CALLBACK The callback must be a function name in the format described in the Pseudo-Type section of the PHP manual (either a string, or an array). The callback must accept a single parameter, the PEAR_Error object generated by an error condition. Note that if the callback is not a valid callback, a PHP warning will be triggered.
Here's an example of a few ways to use setErrorHandling:
<?php
require_once 'PEAR.php';
// dummy error constant for this example
define('MYCLASS_ERROR_CODE', 1);
// demonstration of default global error handling
// in this case, all PEAR Errors will trigger a PHP warning
PEAR::setErrorHandling(PEAR_ERROR_TRIGGER, E_USER_WARNING);
// Note that the file and line number will be in the constructor of PEAR_Error
// in PEAR.php
PEAR::raiseError('test warning', MYCLASS_ERROR_CODE);
// this error specifies a mode, and overrides the default global error handling
$e = PEAR::raiseError('return only', MYCLASS_ERROR_CODE, PEAR_ERROR_RETURN);
PEAR::setErrorHandling(PEAR_ERROR_PRINT, "Gronk error: %s<br />\n");
// prints "Gronk error: test warning<br />\n"
PEAR::raiseError('test warning', MYCLASS_ERROR_CODE);
/**
* Fake class to demonstrate error handling
* @package myClass
*/
class myClass extends PEAR {
/**
* Demonstration of default local error handling
*/
function myClass()
{
// object method callback
$this->setErrorHandling(PEAR_ERROR_CALLBACK, array(&$this, 'handleErr'));
// prints "custom handler...is working"
PEAR::raiseError('custom handler', MYCLASS_ERROR_CODE);
// static class method callback
$this->setErrorHandling(PEAR_ERROR_CALLBACK,
array('myClass', 'handleErrStatic'));
PEAR::raiseError('custom handler', MYCLASS_ERROR_CODE);
// function callback
$this->setErrorHandling(PEAR_ERROR_CALLBACK, 'standardCallback');
PEAR::raiseError('custom handler', MYCLASS_ERROR_CODE);
}
/**
* Callback set by the constructor
* @param PEAR_Error The error object
*/
function handleErr($error)
{
$this->lastError = $error->getMessage();
print $error->getMessage() . "...is working\n";
}
/**
* Static callback set by the constructor
*
* Note that in PHP 5, $this is not set if the method is declared with
* the "static" access modifier. In PHP 4, $this is set, but is not
* set to the myClass object, so don't use it!
* @param PEAR_Error The error object
* @static
*/
function handleErrStatic($error)
{
print 'static ' . $error->getMessage() . "...is working\n";
}
}
/**
* @param PEAR_Error The error object
*/
function standardCallback($error)
{
print 'normal function callback: ' . $error->getMessage();
}
// This causes the printing of three messages through error callbacks:
// "custom handler...is working"
// "static custom handler... is working"
// "normal function callback: custom handler"
$mine = new myClass;
PEAR::setErrorHandling(PEAR_ERROR_DIE);
// terminates the script with the error message "oops"
PEAR::raiseError('oops', MYCLASS_ERROR_CODE);
?>
integer PEAR::expectError (
mixed $errorCode = '*'
)
This method is used to tell which errors you expect to get. Expected errors are always returned with error mode PEAR_ERROR_RETURN. Expected error codes are stored in a stack, and this method pushes a new element onto it. The list of expected errors are in effect until they are popped off the stack with the popExpect() method.
mixed $errorCode
-
the expected
PEAR_Error error code or
an array of error codes. If set to '*'
every error is expected.
integer
- the size of the error code stack.
This function can not be called statically.
mixed PEAR::popExpect (
)
This method pops one element off the expected error codes stack.
mixed
-
the removed error code or array of error
codes
This function can not be called statically.
boolean PEAR::loadExtension (
string $ext
)
Loads an extension by name
$ext
The case-sensitive name of the PHP extension
without filename suffix or php_
prefix.
boolean
- returns TRUE, if
extension could be loaded
This function can be called statically.
Loading the domxml-extension
<?php
if(!PEAR::loadExtension("domxml")) {
echo 'Could not load DomXML-Extension!';
exit();
}
?>
PEAR_Exception is the recommended error handling solution for PHP 5-based packages in PEAR. PEAR_Exception is a lightweight wrapper above the built-in Exception class in PHP 5 that provides the ability to specify causes for errors, register observers, and many more features.
Exceptions in general should be used only for exceptional circumstances - for error conditions that require termination of execution. PEAR_Exception should mainly be used for transmitting error information outside the existing package, and not for normal flow control. Use Control Structures in favor of exceptions wherever possible.
Introduction to the usage of PEAR_Exception
This class is available as part of the PEAR package. Features include:
Nestable exceptions (
<?php throw new PEAR_Exception($msg, $prev_exception); ?>
)
Subject/Observer pattern, triggered when an exception is instantiated
Clear, detailed and attractively formatted error messages
Extra context information available compared to built-in Exception. For instance, a cause of the exception (PEAR_Error/PEAR_ErrorStack/another Exception).
Exception cause can be a PEAR_Error object, PEAR_Exception object or an array of mixed PEAR_Exceptions/PEAR_ErrorStack warnings
callbacks for specific exception classes and their children
Usage example:
<?php
require_once 'PEAR/Exception.php';
class Test {
function foo() {
throw new PEAR_Exception('Error Message', ERROR_CODE);
}
}
function myLogger($pear_exception) {
echo $pear_exception->getMessage();
}
// each time a exception is thrown the 'myLogger' will be called
// (its use is completely optional)
PEAR_Exception::addObserver('myLogger');
$test = new Test;
try {
$test->foo();
} catch (PEAR_Exception $e) {
print $e;
}
?>
API documentation is documented in the documentation for the PEAR package generated by phpDocumentor. The class is very simple, examine the source in the PEAR package to get a better idea of how it works.
PEAR_ErrorStack is an experimental error raising and handling implementation for PEAR that is designed to replace PEAR_Error when it has stabilized. PEAR_ErrorStack is both backwards compatible with PEAR_Error and forward compatible with PHP 5's PEAR_Exception class. There are many other features, all described in the Introduction.
Usage:
1 // global error stack
2 $global_stack = &PEAR_ErrorStack::
singleton('MyPackage');
3 // local error stack
4 $local_stack = new
PEAR_ErrorStack('MyPackage');
Introduction to the usage of PEAR_ErrorStack
This class is available as part of the PEAR package. Features include:
Fully unit-tested and documented
blazingly fast - blows PEAR_Error out of the water
Package-specific errors
Error levels (notice/warning/error/exception)
Error context data is saved separate from error message
Error cascading - parent errors can be specified
Dynamic error message generation allows generation of multiple and distinct error messages from the same error object
Sophisticated callbacks are available for error message generation, error context generation, and error handling functionality, see Error Context Display, Custom Error Message Generation, and controlling error generation
PEAR_ErrorStack implements error raising and handling using a stack pattern. This has tremendous advantages over the PEAR_Error Implementation. PEAR_Error centralizes all error creation and handling in the constructor of the PEAR_Error object. Once an object has been created, all handling must have been completed, either through checking the return value of a method, or through a single global callback. In addition, it is nearly impossible to determine the source of an error, and the baggage of all of the PEAR base class's bulky, slow methods accompanies every error creation.
<?php
// traditional PEAR_Error usage
require_once 'PEAR.php';
class myobj
{
// there is no way to know where $err comes from
function errorCallback($err)
{
$this->display($err->getMessage());
$this->log($err->getMessage());
}
function log($msg)
{
error_log($msg, 3, 'somefile.log')
}
function display($msg)
{
echo $msg . '<br />';
}
}
$myobj = new myobj;
// using a callback
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array(&$myobj, 'errorCallback'));
$ret = SomePackage::doSomething();
if (PEAR::isError($ret)) {
// do some handling - this error is also displayed and logged
}
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
$ret = SomePackage::doSomething();
if (PEAR::isError($ret)) {
// do some handling - this error is not displayed or logged
}
PEAR::popErrorHandling();
?>
The PEAR_ErrorStack class has built in knowledge of the Log package, can easily differentiate and even automatically re-package errors with little to no difficulty.
<?php
// PEAR_ErrorStack error handling
require_once 'PEAR/ErrorStack.php';
require_once 'Log.php';
define('MYPACKAGE_ERROR_DBERROR', 1);
class myobj
{
var $_stack;
function myobj()
{
$this->_stack = &PEAR_ErrorStack::singleton('MyPackage');
}
function errorCallback($err)
{
switch($err['package']){
case 'MyPackage':
// tell the error stack to log the error only
// it will not be pushed onto the stack
return PEAR_ERRORSTACK_LOG;
break;
case 'InternalDbPackage':
// re-package these errors as a mypackage error fit
// for enduser consumption
$this->_stack->push(MYPACKAGE_ERROR_DBERROR, 'error',
array('dbmessage' => $err['message'],
'dbcode' => $err['code'],
'We are having Connection problems, please' .
'try again in a few moments'),
'', $err); // include the error as re-packaged
// tell the internal DB error stack to ignore this error,
// as if it never happened
return PEAR_ERRORSTACK_IGNORE;
break;
} // switch
}
}
$myobj = &new myobj;
// separate error stacks for my package, and the internal DB package
$dbstack = &PEAR_ErrorStack::singleton('InternalDbPackage');
$mystack = &PEAR_ErrorStack::singleton('MyPackage');
// set up a file log using PEAR::Log
$log = &Log::Factory('file', 'somefile.log', 'MyPackage error log');
$mystack->setLogger($log);
// set up a default log to use for all error stacks
PEAR_ErrorStack::setDefaultLogger($log);
// any errors returned by MyPackage are logged
$ret = SomePackage::doSomething();
// Note that $ret need not be checked for any error condition - errors are
// totally separate from code
if ($dbstack->hasErrors()) {
var_dump($dbstack->getErrors();
}
// sets a default callback for all errors
PEAR_ErrorStack::setDefaultCallback(array(&$myobj, 'errorCallback'));
// any db errors are transparently repackaged as
// user-friendly MyPackage errors now
$ret = SomePackage::doSomething();
?>
Why write a new error-handling routine when PEAR_Error already exists? There are several problems with PEAR_Error. Although an error message is present in an error class, processing this error message automatically is excessively difficult for computers. In addition, the error message cannot easily be translated once it has been placed into the PEAR_Error. There is also no standard facility for storing error-related data in the error class. On top of error message-related issues, there is no way to automatically determine which package a PEAR_Error object comes from, or the severity of an error. Fatal errors look exactly the same as non-fatal errors.
The largest flaw with PEAR_Error object is the single-error type design. Every PEAR_Error object is just a PEAR_Error object. There is no differentiating between the severity of an error, or its origin. The only way to determine the severity is to use PEAR_ERROR_TRIGGER and E_USER_NOTICE/E_USER_WARNING/E_USER_ERROR constants from php's trigger_error. But using this functionality does not justify 900 lines of code, simply because trigger_error() is built into PHP itself!
Now, to start using your newly created error objects, change all of your PEAR::raiseError() or PEAR::throwError() calls from this...
<?php
require_once 'PEAR.php';
// old way:
$error_specific_info = 'bad';
$e = PEAR::raiseError("error message - very " . $error_specific_info .
" way to do things", MYPACKAGE_ERROR_FOO);
// another old way:
$e = PEAR::throwError("error message - very " . $error_specific_info .
" way to do things", MYPACKAGE_ERROR_FOO);
?>
...to something like this:
<?php
require_once 'PEAR/ErrorStack.php';
// new way
// version 1: stack instance access
$stack = &PEAR_ErrorStack::singleton('MyPackage');
$stack->push(MYPACKAGE_ERROR_DBERROR, 'error',
array('query' => $query, 'dsn' => $dsn),
'Critical Database Error: Contact Administrator immediately');
// version 2: static singleton access (slightly slower)
PEAR_ErrorStack::staticPush('MyPackage', MYPACKAGE_ERROR_DBERROR, 'error',
array('query' => $query, 'dsn' => $dsn),
'Critical Database Error: Contact Administrator immediately');
?>
For basic use, this is all that is needed to use the PEAR_ErrorStack package in place of PEAR_Error.
In some cases, you may want to customize error generation. For instance, for many exceptions, it is useful to include file, line number, and class/function context information in order to trace an error. A default option is available which will be sufficient for most cases, and that is PEAR_ErrorStack::getFileLine().
Not all package errors occur in the PHP source file. For instance, compiling template engines errors can occur in the template source files. Database errors can occur in the text of a query, or internal to the database server. Internet package errors can occur on another server. All of this information can be included in an error message using a context grabbing callback.
<?php
require_once 'PEAR/ErrorStack.php';
class DatabaseClass
{
var $_dbError;
var $_dbErrorMsg;
var $_dbQuery;
var $_dbPos;
/**
* Context grabber for the Database package
* @param integer Error Code
* @param array Error parameters passed into {@link PEAR_ErrorStack::push()}
* @param array Output of debug_backtrace() (not used in this callback)
*/
function getErrorContext($code, $params, $backtrace)
{
$context = array(
'errorcode' => $this->_dbError,
'errormsg' => $this->_dbErrorMsg,
'query' => $this->_dbQuery,
'pos' => $this->_dbPos,
);
return $context;
}
}
$db = new DatabaseClass;
PEAR_ErrorStack::staticSetContextCallback('Database', array(&$db, 'getErrorContext'));
?>
The context information is formatted to be easily processed by an external application. If you wish context information to be in the error message, the error message callback should be used to add the information in a human-readable format to the error message, as described in the next section.
There are three methods of PEAR_ErrorStack designed for use with generating error messages efficiently. To use them, you must do one of three things:
Call PEAR_ErrorStack::setErrorMessageTemplate(), and set an array mapping error codes to error message templates, like so:
<?php
define('ERROR_ONE', 1);
define('ERROR_TWO', 2);
define('ERROR_THREE', 3);
define('ERROR_FOUR', 4);
require_once 'PEAR/ErrorStack.php';
$stack = &PEAR_ErrorStack::singleton('mypackage');
$messages = array(
ERROR_ONE => 'The gronk number %num% dropped a %thing%',
ERROR_TWO => 'The %list% items were missing',
ERROR_THREE => 'I like chocolate, how about %you%?',
ERROR_FOUR => 'and a %partridge% in a pear %tree%',
);
$stack->setErrorMessageTemplate($messages);
?>
Substitution is done using str_replace, and is very simple. Basically, if a variable name is enclosed in percent signs (%), it will be replaced with the value passed in the associative array. If an array
<?php
array('varname' => 'value');
?>
is passed to either method, all occurrences of %varname% will be replaced by value.
In addition, if values are objects, the methods will search for a method named "__toString()" and if found, will use that to convert the object to a string. If an array of strings is passed in, they will be joined by commas.
<?php
array('varname' => array('first', 'second', 'third'));
// this will become 'first, second, third'
?>
Call PEAR_ErrorStack::setMessageCallback(), and set a custom error message generating function or method. This is probably the best option for the majority of complex situations, as it allows users to override or even extend the existing error message callback using PEAR_ErrorStack::getMessageCallback(). For example:
<?php
require_once 'PEAR/ErrorStack.php';
class foo
{
var $_oldcallback;
function callback(&$stack, $err)
{
$message = call_user_func_array($this->_oldcallback, array(&$stack, $err));
$message .= "File " . $err['context']['file'];
return $message;
}
}
$a = new foo;
$stack = &PEAR_ErrorStack::singleton('otherpackage');
$a->_oldcallback = $stack->getMessageCallback('otherpackage');
$stack->setMessageCallback(array(&$a, 'callback'));
?>
Extend PEAR_ErrorStack with your own class, and override PEAR_ErrorStack::getErrorMessageTemplate() or PEAR_ErrorStack::getErrorMessage(). To guarantee that this class will be used by other packages/applications, use this code right after the class declaration:
<?php
PEAR_ErrorStack::singleton('mypackage', false, null, 'MyPEAR_ErrorStack');
?>
There are many scenarios in which fine-grained control over error raising is absolutely necessary. A generic error handling callback means that every single error raised will be handled in the same error callback. Although PEAR_ErrorStack is designed to operate with independent callbacks for each package, generic error handling is possible through the PEAR_ErrorStack::staticPushCallback() method. This is no different from PEAR_Error's PEAR_ERROR_CALLBACK error handling mode.
PEAR_ErrorStack's real strength comes from the callback itself. PEAR_Error's callback has no actual effect on the error message - all error handling must happen in the callback method or function itself. PEAR_ErrorStack's callback can influence the error through the use of three constants:
PEAR_ERRORSTACK_IGNORE informs the stack to ignore the error, as if it never occurred. The error will be neither logged, nor pushed on the stack. It will, however, be returned from PEAR_ErrorStack::push()
PEAR_ERRORSTACK_PUSH informs the stack to push the error onto the error stack, but not to log the error.
PEAR_ERRORSTACK_LOG informs the stack not to push the error onto the error stack, but only to log the error.
<?php
define('ERROR_CODE_ONE',1);
define('ERROR_CODE_TWO',2);
define('ERROR_CODE_THREE',3);
require_once 'PEAR/ErrorStack.php';
require_once 'Log.php';
function somecallback($err)
{
switch($err['code']){
case ERROR_CODE_ONE:
return PEAR_ERRORSTACK_IGNORE;
break;
case ERROR_CODE_TWO:
return PEAR_ERRORSTACK_PUSH;
break;
case ERROR_CODE_THREE:
return PEAR_ERRORSTACK_LOG;
break;
} // switch
}
$log = &Log::factory('display');
$stack = &PEAR_ErrorStack::singleton('mypackage');
$stack->setLogger($log);
$stack->pushCallback('somecallback');
$stack->push(ERROR_CODE_ONE);
$stack->push(ERROR_CODE_TWO);
$stack->push(ERROR_CODE_THREE);
var_dump(PEAR_ErrorStack::staticGetErrors());
// simulate PEAR_ERROR_CALLBACK, with specific callback for mypackage
// every other package will only log errors, only mypackage's errors
// are pushed on the stack, conditionally
class myclass {
function acallback($err)
{
return PEAR_ERRORSTACK_LOG;
}
}
$stack2 = PEAR_ErrorStack::singleton('anotherpackage');
$stack3 = &PEAR_ErrorStack::singleton('thirdpackage');
PEAR_ErrorStack::setDefaultCallback(array('myclass', 'acallback'));
?>
The most obvious usage for an error callback involves a common scenario in many user-level applications that use system-level packages. If you write a Content Management System (CMS) with the PEAR DB package, it is usually a bad idea to display database-level errors when a user clicks on a link to add a message to a forum. PEAR_ErrorStack can be used to repackage this error as a MyPackage error.
<?php
define('MYPACKAGE_ERROR_DBDOWN',1);
require_once 'PEAR/ErrorStack.php';
function repackage($err)
{
if ($err['package'] == 'DB') {
$mystack = &PEAR_ErrorStack::singleton('mypackage');
$mystack->push(MYPACKAGE_ERROR_DBDOWN, 'error', array('olderror' => $err));
// ignore the DB error, but save it in the mypackage error, for logging
return PEAR_ERRORSTACK_IGNORE;
}
}
?>
One of the difficult-to-use strengths of PEAR_Error involves the PEAR::expectError() method. With regular PHP errors, it is possible to silence them using the @ operator like so:
<?php
@file_get_contents();
?>
Emulating this behavior with PEAR_ErrorStack is simple.
<?php
define('ERROR_CODE_SOMETHING', 1);
require_once 'PEAR/ErrorStack.php';
function silence($err)
{
// ignore all errors
return PEAR_ERRORSTACK_IGNORE;
}
$stack = &PEAR_ErrorStack::singleton('mypackage');
$stack->pushCallback('silence');
$stack->push(ERROR_CODE_SOMETHING);
?>
PEAR_ErrorStack can take this one step further, and only log errors or only put errors on the error stack, using the other two constants. Finally, particular errors can be singled out, and all others ignored.
<?php
define('SOMEPACKAGE_ERROR_THING', 1);
require_once 'PEAR/ErrorStack.php';
function silenceSome($err)
{
if ($err['package'] != 'somepackage') {
// ignore all errors from other packages
return PEAR_ERROR_IGNORE;
}
if ($err['code'] != SOMEPACKAGE_ERROR_THING) {
// ignore all other error codes
return PEAR_ERRORSTACK_IGNORE;
}
}
$stack = &PEAR_ErrorStack::singleton('mypackage');
$stack->pushCallback('silenceSome');
$stack->push(ERROR_CODE_SOMETHING);
?>
PEAR_ErrorStack can also be programmed to automatically raise a PEAR_Error using PEAR::raiseError(), simply pass in true to the PEAR_Error compatibility like so:
<?php
require_once 'PEAR/ErrorStack.php';
$stack = &PEAR_ErrorStack::singleton('mypackage', false, false, true);
?>
PEAR_ErrorStack can coordinate with the new PEAR_Exception class to convert into an exception with this code: You can set the exception class name that will be returned using the following code:
<?php
require_once 'PEAR/ErrorStack.php';
require_once 'PEAR/Exception.php';
$stack = &PEAR_ErrorStack::singleton('mypackage');
$stack->push(1, 'test error');
throw new PEAR_Exception('did not work', $stack->pop());
?>
void constructor PEAR_ErrorStack::PEAR_ErrorStack (
string $package
, callback $msgCallback
= false
, callback $contextCallback
= false
, boolean $throwPEAR_Error
= false
)
As of PEAR 1.3.2, PEAR_ErrorStack no longer instantiates and returns an Exception object in PHP5, and the last parameter has been removed. Code that relies upon this behavior will break.
Creates a new private PEAR_ErrorStack. To access a universal stack, use PEAR_ErrorStack::singleton()
$package
name of the package this error stack represents
$msgCallback
callback used for error message generation
$contextCallback
callback used for context generation, defaults to getFileLine()
$throwPEAR_Error
$exceptionClass
exception class to instantiate if in PHP 5
throws no exceptions thrown
This function can not be called statically.
string PEAR_ErrorStack::getErrorMessage (
PEAR_ErrorStack &$stack
, array $err
, string|false $template
= false
)
This method may also be called by a custom error message generator to fill in template values from the params array, simply set the third parameter to the error message template string to use
The special variable %__msg% is reserved: use it only to specify where a message passed in by the user should be placed in the template, like so:
Error message: %msg% - internal error
If the message passed like so:
The returned error message will be "Error message: server error 500 - internal error"
&$stack
$err
$template
Pre-generated error message template
throws no exceptions thrown
This function should be called statically.
string PEAR_ErrorStack::getErrorMessageTemplate (
mixed $code
)
Standard Error Message Template generator from error code
$code
throws no exceptions thrown
This function can not be called statically.
array PEAR_ErrorStack::getErrors (
boolean $purge
= false
, string|false $level
= false
)
Retrieve all errors since last purge, or filter all errors and only return errors of a particular error level, leaving the rest on the stack.
$purge
set in order to empty the error stack
$level
Level name to check for a particular severity. Use this to determine whether only a particular class of errors has occurred, such as whether any warnings have occurred (errors will be ignored)
throws no exceptions thrown
This function can not be called statically.
array|false PEAR_ErrorStack::getFileLine (
array $code
, unused $params
, integer $backtrace
= null
)
This function uses a backtrace generated from debug_backtrace and so will not work at all in PHP < 4.3.0. The frame should reference the frame that contains the source of the error.
$code
Results of debug_backtrace()
$params
$backtrace
backtrace frame.
returns either array('file' => file, 'line' => line, 'function' => function name, 'class' => class name) or if this doesn't work, then false
throws no exceptions thrown
This function should be called statically.
array|string|false PEAR_ErrorStack::getMessageCallback (
)
This method returns the current callback that can be used to generate error messages
returns Callback function/method or false if none
throws no exceptions thrown
This function can not be called statically.
boolean PEAR_ErrorStack::hasErrors (
string $level = false
)
Determine whether there are any errors on the stack
$level
Level name to check for a particular severity. Use this to determine whether only a particular class of errors has occurred, such as whether any warnings have occurred (errors will be ignored)
throws no exceptions thrown
This function can not be called statically.
false|array PEAR_ErrorStack::pop (
)
Pop an error off of the error stack
throws no exceptions thrown
since 0.4alpha it is no longer possible to specify a specific error level to return - the last error pushed will be returned instead.
This function can not be called statically.
array|string|false PEAR_ErrorStack::popCallback (
)
Remove a callback from the error callback stack
throws no exceptions thrown
This function can not be called statically.
PEAR_Error|array PEAR_ErrorStack::push (
integer
$code
,
string
$level = 'error'
,
array
$params = array()
,
string
$msg
= false
,
array
$repackage
= false
,
array
$backtrace
= false
)
As of PEAR 1.3.2, PEAR_ErrorStack no longer instantiates and returns an Exception object in PHP5. Code that relies upon this behavior will break.
If the message generator exists, it is called with 2 parameters.
the current Error Stack object
an array that is in the same format as an error. Available indices are 'code', 'package', 'time', 'params', 'level', and 'context'
Next, if the error should contain context information, this is handled by the context grabbing method. Finally, the error is pushed onto the proper error stack
$code
Package-specific error code
$level
Error level. This is NOT spell-checked
$params
associative array of error parameters
$msg
Error message, or a portion of it if the message is to be generated
$repackage
If this error re-packages an error pushed by another package, place the array returned from pop() in this parameter
$backtrace
Protected parameter: use this to pass in the http://www.php.net/manual-lookup.php?pattern=debug_backtrace that should be used to find error context.
returns if compatibility mode is on, a PEAR_Error is also thrown. If the class Exception exists, then one is returned to allow code like:
The errorData property of the exception class will be set to the array that would normally be returned. If a PEAR_Error is returned, the userinfo property is set to the array
Otherwise, an array is returned in this format:
<?php
1 array(
2 'code' => $code,
3 'params' => $params,
4 'package' => $this->_package,
5 'level' => $level,
6 'time' =>
time(),
7 'context' => $context,
8 'message' => $msg,
9 //['repackage' => $err] repackaged error array
10 );
?>
No exceptions thrown.
This function can not be called statically.
void PEAR_ErrorStack::pushCallback (
string|array $cb
)
The return value must be one of the PEAR_ERRORSTACK_* constants.
This functionality can be used to emulate PEAR's pushErrorHandling, and the PEAR_ERROR_CALLBACK mode, without affecting the integrity of the error stack or logging
$cb
throws no exceptions thrown
see PEAR_ErrorStack::popCallback()
see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
This function can not be called statically.
PEAR_Error
PEAR_ErrorStack::raiseError
(
)
No exceptions thrown.
This function can be called statically.
void
PEAR_ErrorStack::setContextCallback
(
array|string|null
$contextCallback
)
This method sets the callback that can be used to generate context information for an error. Passing in NULL will disable context generation and remove the expensive call to debug_backtrace()
No exceptions thrown.
This function can not be called statically.
void
PEAR_ErrorStack::setDefaultCallback
(
array|string
$callback
= false
)
This method sets the callback that can be used to generate error messages for a singleton
$callback
Callback function/method
No exceptions thrown.
This function should be called statically.
void
PEAR_ErrorStack::setDefaultLogger
(
Log &$log
)
Set up a PEAR::Log object for all error stacks that don't have one
&$log
No exceptions thrown.
This function should be called statically.
string
PEAR_ErrorStack::setErrorMessageTemplate
(
mixed
$template
)
The array format must be: array(error code => 'message template',...)
Error message parameters passed into push() will be used as input for the error message. If the template is 'message %foo% was %bar%', and the parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will be 'message one was six'
$template
No exceptions thrown.
This function can not be called statically.
void
PEAR_ErrorStack::setLogger
(
Log
&$log
)
Set a PEAR::Log object to use for a specific error stack instance
&$log
No exceptions thrown.
This function can not be called statically.
void
PEAR_ErrorStack::setMessageCallback
(
mixed
$msgCallback
)
This method sets the callback that can be used to generate error messages for any PEAR_ErrorStack instance
$msgCallback
No exceptions thrown.
This function can not be called statically.
PEAR_ErrorStack
&
PEAR_ErrorStack::singleton
(
string
$package
,
callback
$msgCallback
= false
,
callback
$contextCallback
= false
,
boolean
$throwPEAR_Error
= false
,
string
$stackClass = 'PEAR_ErrorStack'
)
As of PEAR 1.3.2, PEAR_ErrorStack no longer instantiates and returns an Exception object in PHP5, and the second-to-last parameter has been removed. Code that relies upon this behavior will break.
Note that all parameters are ignored if the stack for package $package has already been instantiated
$package
name of the package this error stack represents
$msgCallback
callback used for error message generation
$contextCallback
callback used for context generation, defaults to getFileLine()
$throwPEAR_Error
If TRUE, then PEAR::raiseError() will be called and a PEAR_Error object will be returned from calls to PEAR_ErrorStack::push()
$stackClass
class to instantiate
No exceptions thrown.
This function should be called statically.
array
PEAR_ErrorStack::staticGetErrors
(
boolean
$purge
= false
,
string|false
$level
= false
,
boolean
$merge
= false
,
array
$sortfunc = array('PEAR_ErrorStack', '_sortErrors')
)
As of PEAR 1.3.2, the second parameter is a new parameter
$level
. Any code that relies on $merge being the second parameter will break.
Static version of PEAR_ErrorStack::getErrors() , returns all errors from all singleton stacks.
$purge
Set to purge the error stack of existing errors
$level
Level name to check for a particular severity. Use this to determine whether only a particular class of errors has occurred, such as whether any warnings have occurred (errors will be ignored)
$merge
Set to return a flat array, not organized by package
$sortfunc
Function used to sort a merged array - default sorts by time, and should be good for most cases
No exceptions thrown.
This function should be called statically.
boolean
PEAR_ErrorStack::staticHasErrors
(
string|false
$package = false
,
string
$level = false
)
Static version of
PEAR_ErrorStack::hasErrors().
Returns TRUE if any singleton stack has any errors pending.
Since PEAR 1.3.2, If $package
is specified, it will call PEAR_ErrorStack::hasErrors
for the singleton error stack of that package.
If level is specified, hasErrors will ignore
any errors not conforming to the error level specified.
Use this to simulate error_reporting(E_NOTICE), for example
$package
Package name to retrieve error information from, or false to retrieve error information from all singleton stacks
$level
Level name to check for a particular severity. Use this to determine whether only a particular class of errors has occurred, such as whether any warnings have occurred (errors will be ignored)
No exceptions thrown.
This function should be called statically.
false|array
PEAR_ErrorStack::staticPop
(
string
$package
)
Pop an error off of the error stack, static method
$package
Package name to retrieve error message from
returns if compatibility mode is on, a PEAR_Error is also thrown.
since PEAR 1.5.0a1
No exceptions thrown.
This function should be called statically.
array|string|false PEAR_ErrorStack::staticPopCallback
(
)
Pop a callback from every Error Stack. No check is made to determine whether this is a good idea, so use with discretion.
No exceptions thrown.
This function should be called statically.
PEAR_Error|array
PEAR_ErrorStack::staticPush
(
string
$package
,
integer
$code
,
string
$level = 'error'
,
array
$params = array()
,
string
$msg
= false
,
array
$repackage
= false
,
array
$backtrace
= false
)
$package
Package name this error belongs to
$code
Package-specific error code
$level
Error level. This is NOT spell-checked
$params
associative array of error parameters
$msg
Error message, or a portion of it if the message is to be generated
$repackage
If this error re-packages an error pushed by another package, place the array returned from pop() in this parameter
$backtrace
Protected parameter: use this to pass in the debug_backtrace that should be used to find error context
returns if compatibility mode is on, a PEAR_Error is also thrown.
No exceptions thrown.
This function should be called statically.
void
PEAR_ErrorStack::staticPushCallback
(
string|array
$cb
)
Set an error callback for every package's singleton error stack.
$cb
No exceptions thrown.
see PEAR_ErrorStack::staticPopCallback() , PEAR_ErrorStack::pushCallback()
see PEAR_ERRORSTACK_PUSHANDLOG , PEAR_ERRORSTACK_PUSH , PEAR_ERRORSTACK_LOG
This function should be called statically.
void PEAR_ErrorStack::_log (
array $err
, array $levels = array(
'exception' => PEAR_LOG_CRIT,
'alert' => PEAR_LOG_ALERT,
'critical' => PEAR_LOG_CRIT,
'error' => PEAR_LOG_ERR,
'warning' => PEAR_LOG_WARNING,
'notice' => PEAR_LOG_NOTICE,
'info' => PEAR_LOG_INFO,
'debug' => PEAR_LOG_DEBUG)
)
This is a protected function, and should only be called by child classes that extend PEAR_ErrorStack
$err
Error array
$levels
Error level => Log constant map
throws no exceptions thrown
This function can not be called statically.
Name | Value | Line Number |
---|---|---|
PEAR_ERRORSTACK_ERR_NONCLASS | 1 | 110 |
PEAR_ERRORSTACK_ERR_OBJTOSTRING | 2 | 116 |
PEAR_ERRORSTACK_IGNORE | 4 | 103 |
PEAR_ERRORSTACK_LOG | 3 | 99 |
PEAR_ERRORSTACK_PUSH | 2 | 94 |
PEAR_ERRORSTACK_PUSHANDLOG | 1 | 89 |
PEAR_Error is an object created by every function in PEAR in case of a failure. It provides information on why a function fails.
How you get the object depends on PEAR::SetErrorHandling()
void PEAR_Error::addUserInfo (
string $info
)
Adds an user information to the error object.
string
-
user info
This function can not be called statically.
string PEAR_Error::getCallback (
)
Returns the name of the function called in case of throwing a PEAR_Error object.
string
-
function name
This function can not be called statically.
integer PEAR_Error::getCode (
)
Returns the error code coming with the error object.
integer
-
error number
This function can not be called statically.
string PEAR_Error::getMessage (
)
Returns the error message coming with the error object.
string
-
error message
This function can not be called statically.
integer PEAR_Error::getMode (
)
Returns the error mode used for throwing the error object.
integer
-
error mode
This function can not be called statically.
string PEAR_Error::getDebugInfo (
)
Returns debug information about an error
string
-
error debug information
This function can not be called statically.
integer PEAR_Error::getType (
)
Returns the name of the error class of the object.
string
-
error class name
This function can not be called statically.
string PEAR_Error::getUserInfo (
)
Returns additional information about an error
string
-
error information
This function can not be called statically.
void PEAR_Error::PEAR_Error (
string
$message = 'unknown error'
,
integer
$code
= null
,
integer
$mode
= null
,
mixed
$options
= null
,
string
$userinfo
= null
)
Constructor
string $message
-
the error message. A brief description of the occured
failure or problem.
string $code
-
the error code. An error specific number.
integer $mode
-
the error mode
mixed $options
-
error mode specific options
string $userinfo
-
a string for additional user or debug info
This function can be called statically.
string PEAR_Error::toString (
)
Makes a string representation of the error object.
string
-
object representation
This function can not be called statically.
Commandline functions
System provides an API for cross platform compatible commandline programs.
The System functions are called like there commandline pendants
<?php
if (!System::rm('-r file1 dir1')) {
print "Could not delete all the files";
}
?>
The arguments of the functions can be introduced as string or array:
<?php
System::rm(array('-r', 'file1', 'dir1'));
?>
System works like any other PHP function and will return
FALSE
,
when the operation could not be completed entirely or partially. System
won't stop when a error is found, it will try to continue. For example,
if you are trying to delete three files and the first one can't be deleted,
the next two files will be deleted but the function will return
FALSE
.
Errors will be printed out using the PHP function trigger_error()() so all the System methods can be silenced prefix a '@' sign before the function call (for example: @System::mkdir('-p dir1/dir2/dir3');).
System provides file system functions. They are named like the file system commands on Unix systems and supports the same options independent of your operation system.
At the moment the functions are tested under Linux and Windows. Further reports about compatibility on other systems are welcome.
In earlier versions of PHP 4, unlink() may fail on Windows. This bug is already fixed in up-to-date versions.
This manual describes the parameters of the System functions, most only a string. The arguments and options of the specific command are not documented in the manual. Please take a look on the man-pages on unix-like systems
commandname
or when man-pages not available on your system, visit the On-line UNIX manual pages
mixed System::rm (
string $args
)
The rm command for removing files. Supports multiple files and dirs and also recursive deletes.
string $args
-
the arguments for rm
mixed
- TRUE for success
This function can be called statically.
bool System::mkDir (
string $args
)
Creates directories.
string $args
-
the name of the directory/-ies to create
bool
- TRUE for success
This function can be called statically.
boolean System::&cat (
string $args
)
Concatenate files. The method uses fopen(), URLs should work too.
string $args
-
the arguments
boolean
- TRUE on success
This function can be called statically.
Using &cat()
<?php
$var = System::cat('sample.txt test.txt');
System::cat('sample.txt test.txt > final.txt');
System::cat('sample.txt test.txt >> final.txt');
?>
mixed System::mktemp (
string $args
= null
)
Creates temporary files or directories. This function will remove the created files when the scripts finish its execution.
string $args
-
The arguments
prefix
-
The string that will be prepended to the temp name
(defaults to tmp
)
-d
-
A temporary dir will be created instead of a file.
-t
-
The target dir where the temporary file or directory
will be created. If this parameter is missing, by default
the environment vars TMP
on
Windows or TMPDIR
on
Unix will be used. If these vars are also missing
c:\windows\temp
or
/tmp
will be used.
mixed
-
the full path of the created file or dir, or FALSE
This function can be called statically.
Using mktemp()
<?php
$tempfile = System::mktemp("prefix");
$tempdir = System::mktemp("-d prefix");
$tempfile = System::mktemp();
$tempfile = System::mktemp("-t /var/tmp prefix");
?>
string System::tmpdir (
)
Get the path of the temporal directory set in the system by looking in its environments variables.
string
- The temporal directory on the system
This function can be called statically.
php.ini-recommended removes the E
from
the variables_order setting, making unavailable the $_ENV
array
mixed System::which (
string $program
,
boolean $fallback
= false
)
The command shows the full path of a command.
string $program
-
the command to search for
boolean $fallback
-
value to return in case of program not found
mixed
-
A string with the full path or FALSE if not found
This function can be called statically.
The PEAR Installer classes provides an API for the administration and management of PEAR Packages.
Documentation is not fully up-to-date with PEAR version 1.4.0.
This class is for objects where you want to separate the code for some methods into separate classes. This is useful if you have a class with not-frequently-used methods that contain lots of code that you would like to avoid always parsing.
Class Trees for PEAR_Autoloader
PEAR
PEAR_Autoloader
void PEAR_Autoloader::addAggregateObject (
string $classname
)
Add an aggregate object to this object. If the specified class is not defined, loading it will be attempted following PEAR's file naming scheme. All the methods in the class will be aggregated, except private ones (name starting with an underscore) and constructors.
$classname
what class to instantiate for the object.
This function can not be called statically.
void PEAR_Autoloader::addAutoload (
string $method
, string ''$classname
= null
)
Add one or more autoload entries.
$method
which method to autoload
$classname
which class to find the method in.
If the $method
parameter is an array, this
parameter may be omitted (and will be ignored
if not), and the $method
parameter will be
treated as an associative array with method
names as keys and class names as values.
This function can not be called statically.
bool PEAR_Autoloader::removeAggregateObject (
string $classname
)
Remove an aggregate object.
$classname
the class of the object to remove
bool
Returns TRUE on success, FALSE on failure.
This function can not be called statically.
bool PEAR_Autoloader::removeAutoload (
string $method
)
Remove an autoload entry.
$method
which method to remove the autoload entry for
bool
Returns TRUE on success, FALSE on failure.
This function can not be called statically.
mixed PEAR_Autoloader::__call (
string $method
, string $args
, mixed &$retval
)
Overloaded object call handler, called each time an undefined/aggregated method is invoked. This method repeats the call in the right aggregate object and passes on the return value.
$method
which method that was called
$args
An array of the parameters passed in the original call
&$retval
mixed
-
The return value from the aggregated method, or a
PEAR_Error if the called method was unknown.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Class to handle building (compiling) extensions.
Class Trees for PEAR_Builder
PEAR_Common
void PEAR_Builder::PEAR_Builder (
object &$ui
)
PEAR_Builder constructor
&$ui
user interface object (instance of PEAR_Frontend_*)
This function can be called statically.
array PEAR_Builder::build (
string $descfile
, mixed $callback
= null
)
Build an extension from source. Runs phpize in the source directory, but compiles in a temporary directory (/var/tmp/pear-build-USER/PACKAGE-VERSION).
$descfile
path to XML package description file
$callback
callback function used to report output
array
-
an array of associative arrays with built files, format:
<?php
array( array( 'file' => '/path/to/ext.so',
'php_api' => YYYYMMDD,
'zend_mod_api' => YYYYMMDD,
'zend_ext_api' =>; YYYYMMDD ),
... )
?>
PEAR_Common::infoFromDescriptionFile
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
void PEAR_Builder::log (
mixed $level
, mixed $msg
)
This package is not documented yet.
$level
$msg
This function can not be called statically.
void PEAR_Builder::phpizeCallback (
string $what
, mixed $data
)
Message callback function used when running the phpize program. Extracts the API numbers used. Ignores other message types than "cmdoutput".
$what
the type of message
$data
the message
This function can not be called statically.
Represents channel.xml
PEAR_ChannelFile
void constructor
PEAR_ChannelFile::PEAR_ChannelFile
(
)
Simply creates the local error stack for the PEAR_ChannelFile object.
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_ChannelFile::addFunction
(
string
$type
,
string
$version
,
string
$name = ''
,
string
$mirror = false
)
Adds a supported xml-rpc or SOAP function to a channel definition file.
The type should be xmlrpc
or soap
in
lower-cased letters. No validation is performed on insert. For example:
<?php
require_once 'PEAR/ChannelFile.php';
$chan = new PEAR_ChannelFile;
$chan->setName('foo.example.com');
$chan->setSummary('demonstrate addFunction');
$chan->addFunction('xmlrpc', '1.0', 'people.list');
$chan->addFunction('oops', '1.0', 'bad.type');
?>
The oops
protocol will be successfully created, but
will fail upon validation.
Adding a function to a mirror simply validated to ensure that the mirror already exists.
<?php
require_once 'PEAR/ChannelFile.php';
$chan = new PEAR_ChannelFile;
$chan->setName('foo.example.com');
$chan->setSummary('demonstrate addFunction');
// fails: mirror not found
$chan->addFunction('soap', '1.0', 'people.list', 'mirror.example.com');
$chan->addMirror('mirror.example.com');
// succeeds
$chan->addFunction('soap', '1.0', 'people.list', 'mirror.example.com');
?>
$type
protocol type
$version
protocol version
$name
protocol name, if any
$mirror
mirror name, if this is a mirror's protocol
throws no exceptions thrown
This function can not be called statically.
boolean
PEAR_ChannelFile::addMirror
(
string
$server
,
int
$port = null
)
Add a mirror server. Note that mirrors must be added to a channel.xml in order for users to use them. Unofficial mirrors are not allowed without user intervention.
$server
mirror server
$port
mirror http port
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_ChannelFile::addMirrorFunction
(
string
$mirror
,
string
$type
,
string
$version
,
string
$name = ''
)
This is a direct way to add a xmlrpc
or soap
function to a mirror. See docs for
addFunction().
$mirror
mirror name (server)
$type
protocol type
$version
protocol version
$name
protocol name, if any
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_ChannelFile::fromAny
(
string
$info
)
Parse data from either a string or a channel.xml file.
$info
Filename of the source, or the source itself
throws no exceptions thrown
This function can not be called statically.
PEAR_ChannelFile
|false
PEAR_ChannelFile::fromArray
(
array
$data
,
mixed
$compatibility
= false
,
mixed
$stackClass = 'PEAR_ErrorStack'
)
Use this method with caution. This is intended to allow easy import of a pre-parsed channel.xml from another PEAR_ChannelFile class. It also makes possible the registry storage of channel.xml.
$data
The pre-parsed channel data
$compatibility
$stackClass
returns false if invalid
throws no exceptions thrown
This function should be called statically.
bool
PEAR_ChannelFile::fromXmlFile
(
string
$descfile
)
Parse the contents of a channel.xml and store in the current PEAR_ChannelFile object.
$descfile
name of channel xml file
returns success of parsing
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_ChannelFile::fromXmlString
(
string
$data
)
Parse the contents of a channel.xml and store in the current PEAR_ChannelFile object.
$data
contents of package.xml file
returns success of parsing
throws no exceptions thrown
This function can not be called statically.
string
PEAR_ChannelFile::getAlias
(
)
This returns the channel alias. For instance, channel pear.php.net
's
alias is pear
, channel pecl.php.net
's alias is
pecl
.
throws no exceptions thrown
This function can not be called statically.
string
PEAR_ChannelFile::getBaseURL
(
string
$resourceType
,
string|false
$mirror
= false
)
Hyperlinks in the returned xml will be used to retrieve the proper information needed. This allows extreme extensibility and flexibility in implementation
$resourceType
Resource Type to retrieve
$mirror
Mirror name, or false for primary server.
throws no exceptions thrown
This function can not be called statically.
array
PEAR_ChannelFile::getErrors
(
boolean
$purge
= false
)
Retrieve any errors from the last validation attempt.
$purge
determines whether to purge the error stack after retrieving
throws no exceptions thrown
This function can not be called statically.
array
PEAR_ChannelFile::getFunction
(
string
$type
,
string
$name
= null
,
string
$mirror
= false
)
Retrieve the xml representation of a function. If found, the array is of format:
<?php
array(
'_content' => 'functionname',
'attribs' => array('version' => 'version.number')
);
?>
$type
Protocol type
$name
Function name (NULL to return the first protocol of the type requested)
$mirror
Mirror name, if any
throws no exceptions thrown
This function can not be called statically.
array|false
PEAR_ChannelFile::getFunctions
(
string
$protocol
,
string
$mirror
= false
)
This retrieves an array of all defined xmlrpc
and soap
functions. Use getBaseURL()
to access rest
base URLs.
The format of each function is the same as is returned by getFunction()
$protocol
protocol type (xmlrpc, soap)
$mirror
Mirror name
throws no exceptions thrown
This function can not be called statically.
array|false
PEAR_ChannelFile::getMirror
(
mixed
$server
)
This returns the entire contents of the <mirror> tag as unserialized by the parser.
$server
throws no exceptions thrown
This function can not be called statically.
array
PEAR_ChannelFile::getMirrors
(
)
This returns an array of mirror information in the format defined by getMirror()
throws no exceptions thrown
This function can not be called statically.
string|false
PEAR_ChannelFile::getName
(
)
Note that the channel name is the channel server.
throws no exceptions thrown
This function can not be called statically.
string
PEAR_ChannelFile::getPath
(
string
$protocol
,
string|false
$mirror
= false
)
Retrieve the relative path to access the protocol desired. If the channel is named
foo.example.com
and xmlrpc is accessed at foo.example.com/xml/rpc
then path will be xml/rpc
.
$protocol
xmlrpc
or soap
$mirror
mirror name or FALSE for primary server
throws no exceptions thrown
This function can not be called statically.
int|80
PEAR_ChannelFile::getPort
(
mixed
$mirror
= false
)
web services are served through http servers, and so most servers use port 80. This function can be used to determine if a non-standard port is in use at a channel.
$mirror
Mirror name or FALSE for primary server
returns port number to connect to
throws no exceptions thrown
This function can not be called statically.
string|false
PEAR_ChannelFile::getServer
(
)
This is an alias for getName().
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_ChannelFile::getSSL
(
mixed
$mirror
= false
)
This function can be used to determine whether a channel or mirror requires a secure connection through SSL in order to access the packages.
$mirror
Mirror name or false for primary server
returns Determines whether secure sockets layer (SSL) is used to connect to this channel
throws no exceptions thrown
This function can not be called statically.
string|false
PEAR_ChannelFile::getSummary
(
)
Return the brief description of a channel's purpose.
throws no exceptions thrown
This function can not be called statically.
PEAR_Validate|false
&
PEAR_ChannelFile::getValidationObject
(
string|false
$package
= false
)
Retrieve the channel validation object. The package parameter is intended to notify getValidationObject() of the package to be validated. If the package is in fact the channel validation package, then PEAR_Validate will be used for validation. This prevents an endless loop that would otherwise occur.
$package
The name of the package to validate. If the package is the channel validation package, PEAR_Validate is returned.
returns false is returned if the validation package cannot be located
throws no exceptions thrown
This function can not be called statically.
string|false
PEAR_ChannelFile::getValidationPackage
(
)
Retrieve the name of the channel's validation package as defined in channel.xml
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_ChannelFile::isIncludeable
(
string
$path
)
Determine whether a file exists in the include_path and is readable
$path
Relative path to file.
throws no exceptions thrown
This function can not be called statically.
string
PEAR_ChannelFile::lastModified
(
)
\
This method is used by the channel-update
command in order to
determine whether the local copy of channel.xml is up-to-date.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_ChannelFile::resetFunctions
(
string
$type
,
string|false
$mirror
= false
)
Clear all functions defined in the channel.xml, in order to start adding a new list.
$type
protocol type (xmlrpc, soap)
$mirror
mirror name, if any
throws no exceptions thrown
This function can not be called statically.
void
PEAR_ChannelFile::resetREST
(
string|false
$mirror
= false
)
Similar to resetFunctions(), resetREST() removes all REST base URLs in order to start adding new REST functions to a channel.
$mirror
mirror name, if any
throws no exceptions thrown
This function can not be called statically.
boolean
PEAR_ChannelFile::setAlias
(
string
$alias
,
boolean
$local
= false
)
Set the suggested alias that users can use on the command-line as a shortcut access to this
channel. For instance, channel pear.php.net
's alias is pear
.
$alias
The alias
$local
determines whether the alias is in channel.xml or local. If local, then
this is the result of a pear channel-alias
command.
returns success
throws no exceptions thrown
This function can not be called statically.
void
PEAR_ChannelFile::setBaseURL
(
string
$resourceType
,
string
$url
,
string|false
$mirror
= false
)
Set the base URL that users should use to access a REST resource or set of REST resources.
$resourceType
Resource Type this url links to
$url
URL
$mirror
mirror name, if this is not a primary server REST base URL
throws no exceptions thrown
This function can not be called statically.
void
PEAR_ChannelFile::setDefaultPEARProtocols
(
string
$version = '1.0'
,
string|false
$mirror
= false
)
This method sets up a channel's xmlrpc
protocols to match
that of pearweb (pear.php.net). Note that it does not configure REST resources,
that must be done manually through setBaseURL().
$version
version of pearweb protocols to use.
$mirror
Mirror name or false for primary server.
throws no exceptions thrown
This function can not be called statically.
string|false
PEAR_ChannelFile::setName
(
string
$name
)
Set the primary server (and name) of the channel.
$name
Channel name.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_ChannelFile::setPath
(
string
$protocol
,
string|false
$path
,
string|false
$mirror
= false
)
Set the relative location to the channel that should be used to connect to the
selected protocol. Defaults are xmlrpc.php
for xml-rpc and
soap.php
for SOAP.
$protocol
xmlrpc
or soap
$path
name of the mirror server, or FALSE for the primary
$mirror
Mirror name or false for primary server.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_ChannelFile::setPort
(
integer
$port
,
string|false
$mirror
= false
)
As web services connect to channels through web servers, this can be used to set the port that should be used to connect. Default is 80 for regular webservers, and 443 for SSL secure servers.
$port
port number
$mirror
name of the mirror server, or FALSE for the primary server
throws no exceptions thrown
This function can not be called statically.
string|false
PEAR_ChannelFile::setServer
(
string
$server
,
string|false
$mirror
= false
)
This is an alias to setName().
$server
Server name
$mirror
Mirror server name or FALSE for primary server.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_ChannelFile::setSSL
(
bool
$ssl
= true
,
string|false
$mirror
= false
)
This function is used to inform the PEAR installer to use SSL when connecting to this channel server.
$ssl
Determines whether to turn on SSL support or turn it off
$mirror
name of the mirror server, or FALSE for the primary server
throws no exceptions thrown
This function can not be called statically.
boolean
PEAR_ChannelFile::setSummary
(
string
$summary
)
This sets a human-readable description of a channel's purpose.
$summary
The channel summary
returns success
throws no exceptions thrown
This function can not be called statically.
boolean
PEAR_ChannelFile::setValidationPackage
(
string|false
$validateclass
,
string
$version
)
The class must be either in memory (PEAR_Validate or
PEAR_Validate_PECL) or be available for include_once()
via a simple formula: Transform the underscores (_
) into
directory separators (/
), append .php
and
include.
A validation class named Foo_Bar_Baz should be available for inclusion via this code:
In addition, the validation package must be available for installation from the channel
with the version number to install specified by the $version
parameter.
$validateclass
pass in FALSE to reset to the default packagename regex
$version
The package version that must be installed for this channel to validate properly.
returns success
throws no exceptions thrown
This function can not be called statically.
boolean
PEAR_ChannelFile::supports
(
string
$type
,
string
$name
= null
,
string|false
$mirror
= false
,
string
$version = '1.0'
)
This is used in the installer to determine whether a protocol is supported before attempting to connect to a remote server and invoke the function needed.
$type
protocol type (xmlrpc
or soap
)
$name
function name (like package.info
)
$mirror
Mirror server name or FALSE for primary server
$version
function version
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_ChannelFile::supportsREST
(
string
$mirror
= false
)
This does not check to see which REST resources are supported, use getBaseURL() to check specific REST resource or resource group support.
$mirror
Mirror server name or FALSE for the primary server.
throws no exceptions thrown
This function can not be called statically.
array
PEAR_ChannelFile::toArray
(
)
This function should not be used to directly manipulate data, but instead as a means to serialize and transport the data in a more efficient manner than as the original xml.
throws no exceptions thrown
This function can not be called statically.
string
PEAR_ChannelFile::toXml
(
)
This should be used to generate a channel.xml
based on the data
stored in the PEAR_ChannelFile class.
returns XML data
throws no exceptions thrown
This function can not be called statically.
boolean
PEAR_ChannelFile::validate
(
)
Specific errors can be retrieved post-validation with the getErrors() method. Errors are stored by the PEAR_ErrorStack class, more information on the format of the array can be found in the documentation for PEAR_ErrorStack.
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_ChannelFile::validChannelServer
(
string
$server
)
determines whether a channel server is a valid servername.
$server
Server name. May contain subpaths as in foo.example.com/path/to/channel
throws no exceptions thrown
This function can be called statically.
Name | Value | Line Number |
---|---|---|
PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER | 4 | 48 |
PEAR_CHANNELFILE_ERROR_EMPTY_REGEX | 24 | 97 |
PEAR_CHANNELFILE_ERROR_INVALID | 23 | 93 |
PEAR_CHANNELFILE_ERROR_INVALID_HOST | 11 | 81 |
PEAR_CHANNELFILE_ERROR_INVALID_MIRROR | 21 | 85 |
PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE | 22 | 89 |
PEAR_CHANNELFILE_ERROR_INVALID_NAME | 7 | 65 |
PEAR_CHANNELFILE_ERROR_INVALID_PORT | 33 | 122 |
PEAR_CHANNELFILE_ERROR_INVALID_SSL | 37 | 138 |
PEAR_CHANNELFILE_ERROR_INVALID_VERSION | 2 | 38 |
PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND | 32 | 118 |
PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY | 9 | 73 |
PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE | 35 | 130 |
PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME | 27 | 109 |
PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION | 28 | 113 |
PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME | 26 | 105 |
PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION | 25 | 101 |
PEAR_CHANNELFILE_ERROR_NO_HOST | 10 | 77 |
PEAR_CHANNELFILE_ERROR_NO_NAME | 6 | 61 |
PEAR_CHANNELFILE_ERROR_NO_STATICVERSION | 34 | 126 |
PEAR_CHANNELFILE_ERROR_NO_SUMMARY | 8 | 69 |
PEAR_CHANNELFILE_ERROR_NO_VERSION | 1 | 33 |
PEAR_CHANNELFILE_ERROR_NO_XML_EXT | 3 | 43 |
PEAR_CHANNELFILE_ERROR_PARSER_ERROR | 5 | 53 |
PEAR_CHANNELFILE_URI_CANT_MIRROR | 36 | 134 |
PEAR command class, a simple factory class for administrative commands.
object PEAR_Command::factory (
string $command
, object &$config
)
Get the right object for executing a command.
$command
The name of the command
&$config
Instance of PEAR_Config object
object
the command object
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function should be called statically.
array PEAR_Command::getCommands (
void
)
Get the list of currently supported commands, and what classes implement them.
array
command => implementing class
This function should be called statically.
string PEAR_Command::getDescription (
string $command
)
Get description for a command.
$command
Name of the command
string
command description
This function should be called statically.
void PEAR_Command::getGetoptArgs (
string $command
, string &$short_args
, array &$long_args
)
Compiles arguments for getopt.
$command
command to get optstring for
&$short_args
(reference) short getopt format
&$long_args
(reference) long getopt format
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function should be called statically.
string PEAR_Command::getHelp (
string $command
)
Get help for command.
$command
Name of the command to return help for
string
help text
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function should be called statically.
array PEAR_Command::getShortcuts (
void
)
Get the list of command shortcuts.
array
shortcut => command
This function should be called statically.
bool PEAR_Command::registerCommands (
bool $merge
= false
, string $dir
= null
)
Scan through the Command directory looking for classes and see what commands they implement.
$merge
if FALSE (default), the new list of commands should replace the current one. If TRUE, new entries will be merged with old.
$dir
where (what directory) to look for classes, defaults to the Command subdirectory of the directory from where this file (__FILE__) is included.
bool
TRUE on success
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function should be called statically.
Class providing common functionality for PEAR administration classes.
Class Trees for PEAR_Common()
PEAR
void
PEAR_Common::PEAR_Common (
void
)
PEAR_Common constructor
This function can be called statically.
void
PEAR_Common::addTempFile (
string $file
)
Register a temporary file or directory. When the destructor is executed, all registered temporary files and directories are removed.
$file
name of file or directory
This function can be called statically.
array
PEAR_Common::analyzeSourceCode (
string $file
)
Analyze the source code of the given PHP file
$file
Filename of the PHP file
array
- hash list of
declared_classes
,
declared_methods
,
declared_functions
and
used_classes
This function can be called statically.
void
PEAR_Common::buildProvidesArray (
array $srcinfo
)
Build a "provides" array from data returned by analyzeSourceCode(). The format of the built array is like this:
<?php
array(
'class;MyClass' =>
array(
'type' => 'class',
'name' => 'MyClass'
),
...
)
?>
$srcinfo
array with information about a source file as returned by the analyzeSourceCode() method.
This function can not be called statically.
string PEAR_Common::downloadHttp (
string $url
, object &$ui
, string $save_dir = '.'
, mixed $callback
= null
)
Download a file through HTTP. Considers suggested file name in Content-disposition: header and can run a callback function for different events. The callback will be called with two parameters: the callback type, and parameters. The implemented callback types are:
'setup'
-
called at the very beginning, parameter is a
UI object that should be used for all output
'message'
-
the parameter is a string with an informational message
'saveas'
-
may be used to save with a different file name, the
parameter is the filename that is about to be used.
If a 'saveas'
callback
returns a non-empty string, that file name will be
used as the filename instead.
Note that $save_dir
will not
be affected by this, only the basename of the file.
'start'
-
download is starting, parameter is number of bytes
that are expected, or -1 if unknown
'bytesread'
-
parameter is the number of bytes read so far
'done'
- download is
complete, parameter is the total number
of bytes read
'connfailed'
-
if the TCP connection fails, this callback is called
with
<?php
array(host,port,errno,errmsg)
?>
'writefailed'
-
if writing to disk fails, this callback is called
with
<?php
array(destfile,errmsg)
?>
If an HTTP proxy has been configured
(http_proxy
PEAR_Config setting),
the proxy will be used.
$url
the URL to download
&$ui
PEAR_Frontend_* instance
$save_dir
directory to save file in
$callback
function/method to call for status updates
$config
PEAR_Config instance
string
-
Returns the full path of the downloaded file or a
PEAR error on failure. If the error is caused by
socket-related errors, the error object will
have the fsockopen error code available through
getCode().
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
string PEAR_Common::infoFromAny (
string $info
)
Returns package information from different sources. This method is able to extract information about a package from a .tgz archive or from a XML package definition file.
$info
Filename of the source ('package.xml'
,
'<package>.tgz'
)
string
-
Package information
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
array
PEAR_Common::infoFromDescriptionFile (
string $descfile
)
Returns information about a package file. Expects the name of a package xml file as input.
$descfile
name of package xml file
array
-
array with package information
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
array
PEAR_Common::infoFromString (
string $data
)
Returns information about a package file. Expects the contents of a package xml file as input.
$data
content of package xml file
array
-
array with package information
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
array
PEAR_Common::infoFromTgzFile (
string $file
)
Returns information about a package file. Expects the name of a gzipped tar file as input.
$file
name of .tgz file
array
-
array with package information
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
void
PEAR_Common::log (
int $level
, string $msg
)
Logging method.
$level
log level (0 is quiet, higher is noisier)
$msg
message to write to the log
This function can not be called statically.
bool
PEAR_Common::mkDirHier (
string $dir
)
Wrapper to System::mkDir(), creates a directory as well as any necessary parent directories.
$dir
directory name
bool
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
string
PEAR_Common::mkTempDir (
string $tmpdir = ''
)
Create and register a temporary directory.
$tmpdir
Directory to use as tmpdir.
Will use system defaults (for example
/tmp
or
c:\windows\temp
)
if not specified.
string
name of created directory
This function can not be called statically.
void
PEAR_Common::setFrontendObject (
object &$ui
)
Set object that represents the frontend to be used.
$ui
Reference of the frontend object
This function can not be called statically.
boolean PEAR_Common::validatePackageInfo (
string $info
, array &$errors
, array &$warnings
, string $dir_prefix = ''
)
Validate XML package definition file.
$info
Filename of the package archive or of the package definition file
$errors
Array that will contain the errors
$warnings
Array that will contain the warnings
$dir_prefix
directory where source files may be found, or empty if they are not available
bool
-
TRUE if valid
This function can not be called statically.
bool
PEAR_Common::validPackageName (
string $name
)
Test whether a string contains a valid package name.
$name
the package name to test
bool
-
Returns TRUE on success, FALSE on failure.
This function can not be called statically.
string
PEAR_Common::xmlFromInfo (
array $pkginfo
)
Return an XML document based on the package info (as returned by the PEAR_Common::infoFrom*() methods).
$pkginfo
package info
string
-
XML data
This function can not be called statically.
Class Trees for PEAR_Config()
PEAR
void PEAR_Config::PEAR_Config (
string $user_file = ''
, string $system_file = ''
)
Constructor
$user_file
file to read user-defined options from
$system_file
file to read system-wide defaults from
This function can not be called statically.
void PEAR_Config::apiVersion (
)
Returns API version (1.1 as of PEAR 1.4.0).
throws no exceptions thrown
This function can not be called statically.
string PEAR_Config::definedBy (
string $key
)
Tells what config layer that gets to define a key.
$key
config key
string
- the config layer, or an empty string if not found
This function can not be called statically.
void PEAR_Config::deleteChannel (
string $channel
)
remove a channel's configuration entirely.
$channel
Channel name to delete (channel server).
throws no exceptions thrown
This function can not be called statically.
mixed PEAR_Config::get (
string $key
, mixed $layer
= null
)
Returns a configuration value, prioritizing layers as per the layers property.
$key
config key
$layer
layer key
mixed
the config value, or NULL if not found
This function can not be called statically.
void PEAR_Config::getConfFile (
string $layer
)
Use this to retrieve the name of the configuration file that provides values for a particular configuration layer.
$layer
user
, system
, or ftp
throws no exceptions thrown
This function can not be called statically.
string|false PEAR_Config::getDefaultChannel (
string $layer
= null
)
On startup, channels are not initialized, so if the default channel is not pear.php.net, then initialize the config.
$layer
layer from which to retrieve the registry, or null for the first to match
throws no exceptions thrown
This function can not be called statically.
string PEAR_Config::getDocs (
string $key
)
Get the documentation for a config value.
$key
config key
string
- documentation string
This function can not be called statically.
PEAR_FTP|false& PEAR_Config::getFTP (
)
returns the object that can be used for accessing the remote FTP server, or false if none should be used.
throws no exceptions thrown
This function can not be called statically.
string PEAR_Config::getGroup (
string $key
)
Get the parameter group for a config key.
$key
config key
string
- parameter group
This function can not be called statically.
array PEAR_Config::getGroupKeys (
string $group
)
Get the list of the parameters in a group.
$group
parameter group
array
list of parameters in
$group
This function can not be called statically.
array PEAR_Config::getGroups (
void
)
Get the list of parameter groups.
array
- list of parameter groups
This function can not be called statically.
array PEAR_Config::getKeys (
void
)
Get all the current config keys
array
- simple array of config keys
This function can not be called statically.
array PEAR_Config::getLayers (
void
)
Returns the layers defined, except the 'default'
one
array
the defined layers
This function can not be called statically.
string PEAR_Config::getPrompt (
string $key
)
Get the short documentation for a config value.
$key
config key
string
short documentation string
This function can not be called statically.
PEAR_Registry|false& PEAR_Config::getRegistry (
string $use
= null
)
This package is not documented yet.
$use
This parameter determines which layer will be used to retrieve a registry based
on the php_dir
configuration variable for that layer. The
default value of null will use the first layer that contains a valid php_dir,
whereas if specified as user
, system
or
ftp
, it will attempt to return the registry for that layer.
On failure to retrieve a registry, it returns false
throws no exceptions thrown
This function can not be called statically.
PEAR_Remote& PEAR_Config::getRemote (
)
This returns a PEAR_Remote based on the current PEAR_Config object.
throws no exceptions thrown
This function can not be called statically.
PEAR_REST& PEAR_Config::getREST (
string $version
, array $options = array()
)
This package is not documented yet.
$version
This should be 1.0
in PEAR 1.4.0.
$options
options for the PEAR_REST constructor
throws no exceptions thrown
This function can not be called statically.
array PEAR_Config::getSetValues (
string $key
)
Get the list of allowed set values for a config value. Returns NULL for config values that are not sets.
$key
config key
array
-
enumerated array of set values, or NULL if the config key
is unknown or not a set
This function can not be called statically.
string PEAR_Config::getType (
string $key
)
Get the type of a config value.
$key
config key
string
-
type, one of "string",
"integer",
"file",
"directory",
"set" or
"password".
This function can not be called statically.
bool PEAR_Config::isDefined (
string $key
)
Tells whether a given key exists as a config value.
$key
config key
bool
- whether $key
exists in this object
This function can not be called statically.
bool PEAR_Config::isDefinedLayer (
string $layer
)
Tells whether a given config layer exists.
$layer
config layer
bool
- whether $layer
exists in this object
This function can not be called statically.
bool PEAR_Config::mergeConfigFile (
string $file
, bool $override
= true
, string $layer = 'user'
)
Merges data into a config layer from a file. Does the same thing as readConfigFile(), except it does not replace all existing values in the config layer.
$file
file to read from
$override
whether to overwrite existing data
$layer
config layer to insert data into ('user'
or
'system'
)
bool
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
void PEAR_Config::noRegistry (
)
Use this method to disable automatic creation of PEAR_Registry objects when reading from a configuration file or changing php_dir configuration variable.
throws no exceptions thrown
This function can not be called statically.
bool PEAR_Config::readConfigFile (
string $file
= null
, string $layer = 'user'
)
Reads configuration data from a file. All existing values in the config layer are discarded and replaced with data from the file.
$file
file to read from, if NULL or not specified, the last-used file for the same layer (second param) is used
$layer
config layer to insert data into ('user'
or 'system'
)
bool
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
true|PEAR_Error PEAR_Config::readFTPConfigFile (
string $path
)
Process a ftp configuration file by connecting to the server, retrieving the configuration file and parsing it normally. This function uses Net_FTP through the PEAR_FTP class.
$path
url to the remote config file, like ftp://www.example.com/pear/config.ini
throws no exceptions thrown
This function can not be called statically.
bool PEAR_Config::remove (
string $key
, string $layer = 'user'
)
Remove a config key from a specific config layer.
$key
config key
$layer
config layer
bool
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
bool PEAR_Config::removeLayer (
string $layer
)
Temporarily remove an entire config layer. USE WITH CARE!
$layer
config key
bool
-
Returns TRUE on success, FALSE on failure.
This function can not be called statically.
bool PEAR_Config::set (
string $key
, string $value
, string $layer = 'user'
)
Set a config value in a specific layer (defaults to
'user'
). Enforces the types defined
in the configuration_info array. An integer config
variable will be cast to int, and a set config
variable will be validated against its legal values.
$key
config key
$value
config value
$layer
config layer
bool
-
Returns TRUE on success, FALSE on failure.
This function can not be called statically.
bool PEAR_Config::setChannels (
array $channels
, bool $merge
= false
)
This should be set via a call to PEAR_Registry::listChannels(). A call to this function sets up empty arrays for each channel in configurations.
$channels
a simple list of channel names.
$merge
if true, then the list of channel is merged with the existing list, otherwise it replaces the existing list.
returns success of operation
throws no exceptions thrown
This function can not be called statically.
void PEAR_Config::setInstallRoot (
string|false $root
)
This is used to implement the --installroot option for installation. In earlier PEAR versions, this was implemented in PEAR_Installer::install(), but this makes it more difficult to track with the introduction of channels, and to satisfy better encapsulation, it has been moved to PEAR_Config.
Pass in a full path. On retrieving any directory configuration variable, the
value will be prepended with the installroot specified in this method. For example,
if php_dir
is /usr/lib/php
, and
setInstallRoot() is used with /hoopla/boo
,
the value returned from get() would be
/hoopla/boo/usr/lib/php
. Use an empty string ''
to reset an installroot.
$root
installation directory to prepend to all _dir variables, or FALSE to disable
throws no exceptions thrown
This function can not be called statically.
bool PEAR_Config::setRegistry (
PEAR_Registry|false &$reg
, string $layer = 'user'
)
Use this to override the automatic registry creation performed whenever the
php_dir
configuration variable is modified.
If noRegistry()
has been called, this call will be ignored and FALSE returned. In addition, it is
not possible to set the registry for the ftp
layer.
&$reg
The PEAR_Registry that will be used, or false to reset the registry.
$layer
This must be either user
or system
throws no exceptions thrown
This function can not be called statically.
object &PEAR_Config::singleton (
string $user_file = ''
, string $system_file = ''
)
If you want to keep only one instance of this class in use, this method will give you a reference to the last created PEAR_Config object if one exists, or create a new object.
$user_file
file to read user-defined options from
$system_file
file to read system-wide defaults from
object
-
an existing or new PEAR_Config instance
This function should be called statically.
bool PEAR_Config::store (
string $layer = 'user'
)
Stores configuration data in a layer.
$layer
config layer to store
bool - Returns TRUE on success, PEAR_Error on failure.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
bool PEAR_Config::toDefault (
string $key
)
Unset the user-defined value of a config key, reverting the value to the system-defined one.
$key
config key
bool
-
Returns TRUE on success, FALSE on failure.
This function can not be called statically.
bool PEAR_Config::validConfiguration (
)
This method can be used to ward off any real problems with the default configuration.
throws no exceptions thrown
This function can not be called statically.
bool PEAR_Config::writeConfigFile (
string $file
= null
, bool $layer = 'user'
)
Writes data into a config layer from a file.
$file
file to read from
$layer
whether to overwrite existing data
bool
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
This class is deprecated by PEAR_Dependency2 in PEAR 1.4.0
void PEAR_Dependency::PEAR_Dependency (
object &$registry
)
Constructor
This function can not be called statically.
void PEAR_Dependency::callCheckMethod (
mixed &$errmsg
, array $opts
)
This method maps the xml dependency definition to the PEAR_Dependency one.
&$errmsg
this variable will contains an error message, if check fail
$opts
An Array with all Dependency entries from the parsed XML package definition, ie:
<?php
$opts => Array
(
['type'] => 'pkg',
['rel'] => 'ge',
['version'] => '3.4',
['name'] => 'HTML_Common'
);
?>
mixed
-
FALSE if all dependencies could be resolved
successfully(!); or an
PEAR_DEPENDENCY_* constant
in case of unresolved dependencies.
This function can not be called statically.
mixed PEAR_Dependency::checkExtension (
mixed &$errmsg
, string $name
, string $req
= null
, string $relation = 'has'
)
Extension dependencies check method
&$errmsg
this variable will contains an error message, if check fail
$name
Name of the extension to test
$req
Required extension version to compare with
$relation
How to compare versions with eachother
mixed
-
FALSE if dependency could be resolved
successfully(!); or an
PEAR_DEPENDENCY_* constant
in case of an unresolved dependency.
This function can not be called statically.
mixed PEAR_Dependency::checkOS (
string &$errmsg
, string $os
)
Operating system dependencies check method
&$errmsg
this variable will contains an error message, if check fail
$os
Name of the operating system
mixed
-
FALSE if dependency could be resolved
successfully(!); or an
PEAR_DEPENDENCY_* constant
in case of unresolved dependency.
This function can not be called statically.
mixed PEAR_Dependency::checkPackage (
mixed &$errmsg
, string $name
, string $req
= null
, string $relation = 'has'
)
Package dependencies check method
&$errmsg
this variable will contains an error message, if check fail
$name
Name of the package to test
$req
Required package version to compare with
$relation
How to compare versions with eachother
mixed
-
FALSE if dependency could be resolved
successfully(!); or an
PEAR_DEPENDENCY_* constant
in case of unresolved dependency.
This function can not be called statically.
mixed PEAR_Dependency::checkPHP (
mixed &$errmsg
, string $req
, string $relation = 'ge'
)
PHP version check method
&$errmsg
this variable will contains an error message, if check fail
$req
which version to compare
$relation
how to compare the version
mixed
-
FALSE if dependency could be resolved
successfully(!); or an
PEAR_DEPENDENCY_* constant
in case of unresolved dependency.
This function can not be called statically.
mixed PEAR_Dependency::checkProgram (
mixed &$errmsg
, string $program
)
External program check method. Looks for executable files in directories listed in the PATH environment variable.
&$errmsg
this variable will contains an error message, if check fail
$program
which program to look for
mixed
-
FALSE if dependency could be resolved
successfully(!); or an
PEAR_DEPENDENCY_* constant
in case of unresolved dependency.
This function can not be called statically.
mixed PEAR_Dependency::checkSAPI (
mixed &$errmsg
, string $name
, string $req
= null
, string $relation = 'has'
)
SAPI backend check method. Version comparison is not yet available here.
&$errmsg
this variable will contains an error message, if check fail
$name
name of SAPI backend
$req
which version to compare (currently unused)
$relation
how to compare versions (currently hardcoded to 'has')
mixed
-
FALSE if dependency could be resolved
successfully(!); or an
PEAR_DEPENDENCY_* constant
in case of unresolved dependency.
This function can not be called statically.
mixed PEAR_Dependency::checkZend (
mixed &$errmsg
, string $req
, string ''}$relation = 'ge'
)
Zend version check method
&$errmsg
this variable will contains an error message, if check fail
$req
which version to compare
$relation
how to compare the version
mixed
-
FALSE if dependency could be resolved
successfully(!); or an
PEAR_DEPENDENCY_* constant
in case of unresolved dependency.
This function can not be called statically.
This class handles both version 1.0 and 2.0 dependencies and supersedes PEAR_Dependency since PEAR 1.4.0
PEAR_Dependency2
void constructor
PEAR_Dependency2::PEAR_Dependency2
(
PEAR_Config
&$config
,
array
$installoptions
,
array
$package
,
int
$state = PEAR_VALIDATE_INSTALLING
)
This package is not documented yet.
&$config
Current configuration object.
$installoptions
installation options
$package
The current package. For installation dependencies, this is the package that contains dependencies. For uninstallation dependencies, this is the package that is going to be uninstalled.
format of PEAR_Registry::parsedPackageName()
$state
installation state (one of PEAR_VALIDATE_*)
throws no exceptions thrown
This function can not be called statically.
array
PEAR_Dependency2::normalizeDep
(
array
$dep
)
This converts an old-style package.xml 1.0 <dep> tag into the format used by package.xml 2.0. package.xml 2.0 can represent every kind of <dep> tag in its new syntax.
$dep
The package.xml 1.0 <dep> as parsed from xml.
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error|array
PEAR_Dependency2::validateArchDependency
(
array
$dep
)
This is the only dependency that accepts an ereg()able pattern. The pattern
will be matched against the php_uname() output parsed by OS_Guess
As with all dependency validation, true is returned on success, PEAR_Error on failure
for required dependencies (and the arch dependency is required). If the
soft
, force
or ignore-errors
options are specified, an array containing the error message will be returned instead.
$dep
Contents of the dependency, as parsed from xml.
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error|array
PEAR_Dependency2::validateDependency1
(
array
$dep
,
array
$params = array()
)
Validate a package.xml version 1.0 <dep> dependency.
$dep
The contents of the <dep> tag as parsed from xml
$params
The list of PEAR_Downloader_Package objects that will be installed.
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error|array
PEAR_Dependency2::validateExtensionDependency
(
array
$dep
,
bool
$required = true
)
This validates against actual in-memory extensions, and will not attempt to locate extensions on disk. To do this, a dependency should be a package dependency with the <providesextension> tag.
As with all dependency validation, true is returned on success, PEAR_Error on failure
for required dependencies. If the soft
, force
or ignore-errors
options are specified, an array containing the
error message will be returned instead.
$dep
dependency contents as parsed from xml
$required
Whether this is a required or optional dependency
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error|array
PEAR_Dependency2::validateOsDependency
(
array
$dep
)
There are two generic OS dependencies that will be the most common, unix and windows.
Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix.
As with all dependency validation, true is returned on success, PEAR_Error on failure
for required dependencies (and the OS dependency is required). If the
soft
, force
or ignore-errors
options are specified, an array containing the error message will be returned instead.
$dep
Dependency contents as parsed from xml
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error|array
PEAR_Dependency2::validatePackage
(
array|PEAR_PackageFile_v2|PEAR_Downloader_Package
$pkg
,
PEAR_Common
&$dl
)
As with all dependency validation, true is returned on success, PEAR_Error on failure
for required dependencies (and the PEAR installer dependency is required). If the
soft
, force
or ignore-errors
options are specified, an array containing the error message will be returned instead.
$pkg
Either an array of format array('channel' => channelname, 'package' => package)
or one of these objects.
&$dl
Any object with a log() method that matches the signature of PEAR_Common
throws no exceptions thrown
This function can not be called statically.
array|true|PEAR_Error
PEAR_Dependency2::validatePackageDependency
(
array
$dep
,
boolean
$required
,
array
$params
,
bool
$depv1
= false
)
Validate a package-style dependency. Validation is performed in this sequence:
If the dependency package provides an extension through the <providesextension> tag, then see if it passes the extension validation test first and return if so.
If the list of packages to be installed contains a match for the dependency, use that to validate the dependency and return.
If the dependency package is already installed, make sure the installed version passes the conditions.
At this point, the dependency has failed. If the dependency is required, return a PEAR_Error containing the failure error message, otherwise return an array containing the error message.
$dep
dependency array as defined by package.xml 2.0
$required
whether this is a required or optional dependency
$params
array of PEAR_Downloader_Package objects representing packages to be downloaded that can be used to validate dependencies
$depv1
if TRUE, then deps on pear.php.net that fail will also check against pecl.php.net packages to accommodate extensions that have moved to pecl.php.net from pear.php.net
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error|array
PEAR_Dependency2::validatePackageUninstall
(
PEAR_Installer
&$dl
)
As with all dependency validation, true is returned on success, PEAR_Error on failure
for required dependencies (and the PEAR installer dependency is required). If the
soft
, force
or ignore-errors
options are specified, an array containing the error message will be returned instead.
&$dl
This method retrieves the list of packages to be uninstalled from the PEAR_Installer object, and makes sure no dependencies with existing packages would be broken if the current package were to be uninstalled.
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error|array
PEAR_Dependency2::validatePearinstallerDependency
(
array
$dep
)
Validate the running PEAR version against the dependency. This dependency is designed
to prevent PEAR versions incompatible with the current package.xml from attempting to install
it. As with all dependency validation, true is returned on success, PEAR_Error on failure
for required dependencies (and the PEAR installer dependency is required). If the
soft
, force
or ignore-errors
options are specified, an array containing the error message will be returned instead.
$dep
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error|array
PEAR_Dependency2::validatePhpDependency
(
array
$dep
)
Validate the running PHP version against the dependency. The implicit assumption
is that the installer's PHP version is the same as the final script's PHP version.
As with all dependency validation, true is returned on success, PEAR_Error on failure
for required dependencies (and the PHP dependency is required). If the
soft
, force
or ignore-errors
options are specified, an array containing the error message will be returned instead.
$dep
The dependency xml as represented in parsing
throws no exceptions thrown
This function can not be called statically.
array|true|PEAR_Error
PEAR_Dependency2::validateSubpackageDependency
(
array
$dep
,
bool
$required
,
array
$params
)
Validate a subpackage-style dependency. This is identical to a package dependency from a validation perspective, and so documentation for validatePackageDependency() should be referenced for details.
$dep
dependency array as defined by package.xml 2.0
$required
whether this is a required or optional dependency
$params
array of PEAR_Downloader_Package objects representing packages to be downloaded that can be used to validate dependencies
throws no exceptions thrown
This function can not be called statically.
This provides sophisticated dependency handling relationships between installed packages and downloaded to-be-installed packages.
PEAR_DependencyDB
void|PEAR_Error
PEAR_DependencyDB::assertDepsDB
(
)
Assert that the dependency database exists, and attempt to create it if it doesn't.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_DependencyDB::dependsOn
(
array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
$parent
,
array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
$child
)
This method is the central method of DependencyDB. Through the dependency database, it is possible to determine whether any two packages share a dependency relationship independent of how tightly bound the two packages are. In other words, if package A depends on package B depends on package C, this method can be used to determine that package A indirectly depends on package C.
$parent
The parent package (as in package A in the example above)
This parameter, if an array, should be in format:
$child
The child package (as in package B or package C in the example above)
This parameter, if an array, should be in format:
throws no exceptions thrown
This function can not be called statically.
array|false
PEAR_DependencyDB::getDependencies
(
PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
&$pkg
)
get an array of all immediate package dependencies of an installed package.
&$pkg
This parameter, if an array, should be in format:
throws no exceptions thrown
This function can not be called statically.
array|false
PEAR_DependencyDB::getDependentPackageDependencies
(
PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
&$pkg
)
This returns the complete tree of extended dependencies of a single installed package.
For instance, a real-world example. package SOAP depends on Mail_Mime, HTTP_Request, Net_URL, Net_DIME. package HTTP_Request depends on Net_URL, Net_Socket. This method will return an array similar to:
Note that this should not be relied upon for exact dependencies. In the example above, the returned dependency will be that of HTTP_Request upon Net_URL, which is stricter than SOAP's dependency upon Net_URL. In other words, if there are two similar dependencies, the last one encountered will be returned.
&$pkg
This parameter, if an array, should be in format:
throws no exceptions thrown
This function can not be called statically.
array|false
PEAR_DependencyDB::getDependentPackages
(
PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
&$pkg
)
This is most useful at uninstall-time. A list of installed packages that depend upon the package can be used to prevent uninstallation, and to emit warnings for optional dependencies.
&$pkg
This parameter, if an array, should be in format:
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_DependencyDB::hasWriteAccess
(
)
This method is used by the installer to prevent attempts to create/modify the
dependency DB if the current user does not have write access to the registry.
Without this method, simple read-only commands like pear info
would not work.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_DependencyDB::installPackage
(
PEAR_PackageFile_v2|PEAR_PackageFile_v2
&$package
)
This method is used by the registry when a package is installed or upgraded to register the package's dependencies in the dependency database.
&$package
This parameter, if an array, should be in format:
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error
PEAR_DependencyDB::rebuildDB
(
)
This is used to create or re-create the dependency database by reading registry entries for each installed file to extract dependencies and save them in the dependency database.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_DependencyDB::setConfig
(
PEAR_Config|false
&$config
,
string|false
$depdb
= false
)
This crucial method is used to set the PEAR_Config object that should be used to retrieve both configuration information and a PEAR_Registry class for internal manipulation.
&$config
$depdb
full path to the dependency database, or FALSE to use default
throws no exceptions thrown
This function can not be called statically.
PEAR_DependencyDB
|PEAR_Error
&
PEAR_DependencyDB::singleton
(
PEAR_Config
&$config
,
string|false
$depdb
= false
)
Return a single dependency database based on the location of the database on disk. In other words, 1 dependency database is created for each registry location.
&$config
PEAR_Config object
$depdb
full path to the dependency database, or FALSE to use default
throws no exceptions thrown
This function should be called statically.
void
PEAR_DependencyDB::uninstallPackage
(
PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
&$pkg
)
This method is used by the registry when a package is uninstalled or upgraded to remove the package's dependencies from the dependency database.
Upgraded packages first uninstall, then install
&$pkg
This parameter, if an array, should be in format:
throws no exceptions thrown
This function can not be called statically.
This is also the base class for all frontends.
PEAR
PEAR_Frontend
void
PEAR_Frontend::addTempFile
(
mixed
$file
)
By default, all files are deleted at the end of a session. The web installer needs to be able to sustain a list over many sessions in order to support user interaction with install scripts
$file
throws no exceptions thrown
This function can not be called statically.
bool
PEAR_Frontend::isIncludeable
(
string
$path
)
Simple utility class used to determine whether a file can be included via include_path
$path
relative or absolute include path
throws no exceptions thrown
This function can be called statically.
void
PEAR_Frontend::log
(
int
$level
,
string
$msg
,
bool
$append_crlf
= true
)
Log a message in a frontend-specific way. By default, nothing is done.
$level
$msg
$append_crlf
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Frontend::setConfig
(
PEAR_Config
&$config
)
Set the configuration object used by this frontend.
&$config
throws no exceptions thrown
This function can not be called statically.
void&
PEAR_Frontend::setFrontendClass
(
string
$uiclass
)
Set the kind of frontend that should be retrieved from the singleton()
method. If the class does not exist, the method changes all underscores (_
) into directory
separators (like PEAR_Frontend_CLI to PEAR/Frontend/CLI
) and appends
.php
and then checks to see if the file can be included. If the
class does exist after all of this, a simple check is made to see if the
userConfirm() method exists, and then a new frontend object is
returned. Any failure causes a PEAR_Error to be returned.
$uiclass
the full classname (like PEAR_Frontend_Web
)
throws PEAR_Error on any problem
This function can not be called statically.
PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk&
PEAR_Frontend::singleton
(
mixed
$type = null
)
Get a single unique copy of the current PEAR frontend object.
$type
if being called for the first time, the user can specify the kind of frontend to return. Otherwise, this parameter is ignored, and the existing single frontend is returned.
throws no exceptions thrown
This function can be called statically.
Administration class used to install PEAR packages and maintain the installed package database.
Class Trees for PEAR_Installer()
PEAR_Common
void constructor PEAR_Installer::constructor PEAR_Installer (
object &$ui
)
PEAR_Installer constructor.
&$ui
user interface object (instance of PEAR_Frontend_*)
This function can not be called statically.
array PEAR_Installer::install (
string $pkgfile
, array $options = array()
)
Installs the files within the package file specified.
$pkgfile
path to the package file
$options
installating options, to enable an option
use the option name as array key and TRUE or
1
as value.
$options['force'] = 1
-
force installation
$options['register-only'] = 1
-
update registry but don't install files
$options['upgrade'] = 1
-
upgrade existing install
$options['soft'] = 1
-
fail silently
array
package info if successful
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Parse and retrieve a package.xml object from various sources.
PEAR_PackageFile
void constructor
PEAR_PackageFile::PEAR_PackageFile
(
PEAR_Config
&$config
,
bool
$debug
= false
,
string
$tmpdir
= false
)
Prepare for parsing one or more package.xml files.
&$config
The configuration to use for parsing and channel registry (needed for channel-specific validators).
$debug
The debug logging level (this is usually the value of the verbose
configuration variable).
$tmpdir
The temporary directory to extract files in. By default, a new temporary directory is created using System::mktemp().
throws no exceptions thrown
This function can not be called statically.
PEAR_PackageFile_v1|PEAR_PackageFile_v2&
PEAR_PackageFile::factory
(
string
$version
)
Retrieve a raw PEAR_PackageFile_vX object where X is either 1 or 2.
$version
The package.xml version represented (1 or 2)
throws no exceptions thrown
This function can not be called statically.
string&
PEAR_PackageFile::fromAnyFile
(
string
$info
,
int
$state
)
This method is able to extract information about a package from a .tgz archive or from a XML package definition file.
$info
path to a file containing a package.xml (package archive or package.xml)
$state
package state (one of
PEAR_VALIDATE_*
constants)
throws no exceptions thrown
This function should be called statically.
PEAR_PackageFile_v1|PEAR_PackageFile_v2&
PEAR_PackageFile::fromArray
(
array
$arr
)
WARNING: no validation is performed, the array is assumed to be valid, always parse from xml if you want validation.
$arr
Contents of a previously parsed package.xml object.
throws no exceptions thrown
This function can not be called statically.
array&
PEAR_PackageFile::fromPackageFile
(
string
$descfile
,
int
$state
,
string|false
$archive
= false
)
parse and return a package.xml object from a package.xml file, or a PEAR_Error object upon error.
$descfile
name of package.xml file
$state
package state (one of
PEAR_VALIDATE_*
constants)
$archive
full path to the full package .tgz file containing the package.xml, or false for none.
returns array with package information
throws no exceptions thrown
This function should be called statically.
PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error&
PEAR_PackageFile::fromTgzFile
(
string
$file
,
int
$state
)
parse and return a package.xml object from a packaged archive, or a PEAR_Error object upon error.
$file
name of packaged .tgz or .tar to extract and parse package.xml from
$state
package state (one of
PEAR_VALIDATE_*
constants)
returns success of parsing
throws no exceptions thrown
This function can not be called statically.
PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error&
PEAR_PackageFile::fromXmlString
(
string
$data
,
int
$state
,
string
$file
,
string|false
$archive
= false
)
parse and return a package.xml object, or a PEAR_Error object upon error.
$data
contents of package.xml file
$state
package state (one of
PEAR_VALIDATE_*
constants)
$file
full path to the package.xml file (and the files it references)
$archive
full path to the full package .tgz file containing the package.xml, or false for none.
throws no exceptions thrown
This function can not be called statically.
PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v2&
PEAR_PackageFile::parserFactory
(
string
$version
)
Return a package.xml parsing object appropriate for the selected package.xml version.
$version
The package.xml version represented (1 or 2)
throws no exceptions thrown
This function can not be called statically.
void
PEAR_PackageFile::rawReturn
(
)
This is used by the package-validate command
throws no exceptions thrown
This function can not be called statically.
void
PEAR_PackageFile::setLogger
(
PEAR_Common
&$l
)
set a logging class that matches the signature of PEAR_Common's log() method.
throws no exceptions thrown
This function can not be called statically.
Name | Value | Line Number |
---|---|---|
PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION | 2 | 35 |
PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION | 1 | 30 |
Administration class used to make a PEAR release tarball.
Class Trees for PEAR_Packager
PEAR_Common
void PEAR_Packager:: PEAR_Packager (
void
)
constructor
This function can be called statically.
string PEAR_Packager::package (
string $pkgfile
= null
, boolean $compress
= true
)
Creates a Package archive from package definition file and package files
$pkgfile
path to package definition file
$compress
if TRUE package archive will be compessed using gzip
string
-
name of the created package archive
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Administration class used to maintain the installed package database.
Class Trees for PEAR_Registry
PEAR
void PEAR_Registry::PEAR_Registry (
string $pear_install_dir = PEAR_INSTALL_DIR
)
constructor
$pear_install_dir
PEAR install directory (for .php files)
This function can not be called statically.
boolean PEAR_Registry::addPackage (
string $package
, array $info
)
Adds a Package entry to the registry
$package
Package name
$info
additional information for the package entry
boolean
-
FALSE if Package is already registered;
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
string PEAR_Registry::checkFileMap (
string $path
)
Test whether a file belongs to a package.
$path
file path, absolute or relative to the pear install dir
string
-
name of the package the file belongs to, or an empty
string if the file does not belong to an installed package
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
boolean PEAR_Registry::deletePackage (
string $package
)
Removes a Package entry from the registry.
$package
Package name
boolean
-
Returns TRUE on success, FALSE on failure.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
array PEAR_Registry::listPackages (
void
)
List all Packages registered in the registry.
array
-
list of package names
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
boolean PEAR_Registry::packageExists (
string $package
)
Checks wether a Package is registered in the registry or not.
$package
Package name
boolean
-
TRUE if package is registered
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
mixed PEAR_Registry::packageInfo (
string $package
= null
, string $key
= null
)
Returns (specific) information stored in the registry about a Package.
$package
Package name
$key
the name of a specific information to get
mixed
-
an array with all information, or a key specific information,
if $key
was used; NULL if Package
or specific information does not exist
This function can not be called statically.
void PEAR_Registry::rebuildDepsFile (
void
)
Rebuilds the dependencies file of the registry. Use this function if had trouble while installing Packages or a damaged registry.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
void PEAR_Registry::rebuildFileMap (
void
)
Rebuilds the registry filemap. Use this function if had trouble while installing Packages or a damaged registry.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
mixed PEAR_Registry::removePackageDep (
string $package
)
Removes the dependency information of a Package from the registry.
$package
Package name
mixed - TRUE if successful; or an array with a list of Packages depending on this Package
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
mixed PEAR_Registry::setPackageDep (
string $package
, string $new_version
, array $rel_deps = array()
)
Update or insert a the dependencies of a package,
prechecking that the package won't break any
dependency in the process. (Dependencies of
type 'pkg'
only.
$package
Package name
$new_version
Version of the Package
$rel_deps
Package dependencies
mixed
-
TRUE if no dependencies found; or
array with names of missing or outdated Packages
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
boolean PEAR_Registry::updatePackage (
string $package
, array $info
, bool $merge
= true
)
Updates the existing information of a Package in the registry.
$package
Package name
$info
information to update
$merge
if FALSE the old informations will be deleted completly an replaced with the new; if TRUE update only - unchanged values will remain.
boolean
-
Returns TRUE on success, FALSE on failure.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
This is a class for doing remote operations against a PEAR Package Server.
Class Trees for PEAR_Remote()
PEAR
void PEAR_Remote::PEAR_Remote (
object &$config
)
constructor
&$config
an instance of PEAR_Config
This function can not be called statically.
mixed PEAR_Remote::call (
string $method
, mixed $args,...
)
Sends a remote procedure call to a package server and returns the result.
$method
Name of the server method
$args,...
server method specific parameters
mixed
-
result of the executed server method
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
as well
PEAR_REST
void constructor
PEAR_REST::PEAR_REST
(
PEAR_Config
&$config
,
array
$options = array()
)
This package is not documented yet.
&$config
Configuration object
$options
options passed to commands like install/download. The only option that affects
PEAR_REST is offline
, which causes all retrievals to look in
the local cache without trying to retrieve a remote resource.
This function can not be called statically.
string|array
PEAR_REST::downloadHttp
(
string
$url
,
false|string|array
$lastmodified = null
,
false|array
$accept = false
,
string
$save_dir
)
If an HTTP proxy has been configured (http_proxy PEAR_Config setting), the proxy will be used.
$url
the URL to download
$lastmodified
header values to check against for caching use FALSE to return the header values from this download
$accept
Accept headers to send. This should be a list of MIME types like
text/xml
, frog/legs
, etc.
$save_dir
directory to save file in
returns Returns the contents of the downloaded file or a PEAR error on failure. If the error is caused by socket-related errors, the error object will have the fsockopen error code available through getCode(). If caching is requested, then return the header values.
throws no exceptions thrown
This function can not be called statically.
mixed|false
PEAR_REST::getCache
(
string
$url
)
Retrieve the cache contents for a remote resource, or a PEAR_Error if the resource was not cached.
$url
REST resource URL
throws PEAR_Error object returned on error.
This function can not be called statically.
array|false
PEAR_REST::getCacheId
(
string
$url
)
Retrieve the HTTP caching information for a REST resource, or FALSE if no cache is found.
$url
REST resource URL
throws no exceptions thrown
This function can not be called statically.
string|array
PEAR_REST::retrieveCacheFirst
(
string
$url
,
array|false
$accept = false
,
boolean
$forcestring = false
)
This is useful for elements that should never change, such as information on a particular release
$url
full URL to this resource
$accept
contents of the accept-encoding header
$forcestring
if TRUE, xml will be returned as a string, otherwise, xml will be parsed using PEAR_XMLParser
throws no exceptions thrown
This function can not be called statically.
string|array
PEAR_REST::retrieveData
(
string
$url
,
array|false
$accept = false
,
bool
$forcestring = false
)
retrieve the contents of a remote resource.
$url
full URL to this resource
$accept
contents of the accept-encoding header
$forcestring
if TRUE, xml will be returned as a string, otherwise, xml will be parsed using PEAR_XMLParser
throws PEAR_Error objects are returned on error.
This function can not be called statically.
void
PEAR_REST::saveCache
(
string
$url
,
mixed
$contents
,
array
$lastmodified
,
bool
$nochange = false
)
Use this to save a resource after retrieving. Since the cache_ttl
configuration variable is used in determining when to check the remote server, and
HTTP caching is used as well, it is possible for this scenario to arise:
retrieve REST resource
cache the resource
a few days later, retrieve the REST resource again
HTTP caching returns 304 not modified
In this situation, it doesn't make much sense to save the resource contents redundantly. Instead, the last access time can be saved in the cache id by passing true into the last parameter.
$url
The REST resource's URL
$contents
Contents retrieved from the REST resource (ignored if the last parameter is true)
$lastmodified
The ETag and LastModified headers retrieved from the remote server, used for HTTP caching.
$nochange
If false, the cache is saved normally. If true, only the $lastmodified
parameter is saved in the cache id file, registering an HTTP cache hit.
throws no exceptions thrown
This function can not be called statically.
mixed
PEAR_REST::useLocalCache
(
bool
$url
)
Retrieve the contents of the local cached copy of a remote URL. FALSE is returned if there are any problems, under the assumption that REST contents will always be larger than a simple boolean due to HTTP overhead.
$url
The URL to retrieve data for.
throws no exceptions thrown
This function can not be called statically.
Try it with:
$ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; \
$o=$t->run("./pear_system.phpt");print_r($o);'
PEAR_RunTest
void constructor
PEAR_RunTest::PEAR_RunTest
(
PEAR_Common|null
$logger
= null
,
array
$options = array()
)
If no logger is specified, a new PEAR_Common object will be instantiated and used to print output to the screen.
$logger
A class that contains a log() method matching the signature of PEAR_Common.
$options
Currently supported options are simple
and quiet
.
The simple
option causes tests to simply print the title of the
test and not the full path to the test file. The quiet
option
causes output of only failed tests.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_RunTest::generate_diff
(
string
$wanted
,
string
$output
,
array|false
$return_value
)
Returns differences by line between the expected output ($wanted
)
and the actual output ($output
). In addition, the value
returned from the script can also be tested. The test should be performed outside
generate_diff(). If the return value is as expected, pass in
FALSE, otherwise pass in an array where the first element is the expected return
value and the second is the actual return value.
$wanted
Expected output
$output
Actual output
$return_value
False if return value was correct, otherwise an array of format:
<?php
array(
1, // expected return
2, // actual return value
);
?>
throws no exceptions thrown
This function can not be called statically.
string|PEAR_Error
PEAR_RunTest::run
(
string
$file
,
string
$ini_settings = ''
)
Run a unit test. The return value is one of:
PASSED
SKIPPED
WARNED
FAILED
$file
Full path to the test file to run.
$ini_settings
Additional customized settings to pass on the command-line to the PHP instance used for testing. For example, requesting disabling use of php.ini or a testing php.ini can be specified. For a full list of possible settings, type:
$ php -h
throws no exceptions thrown
This function can not be called statically.
This package is not documented yet.
PEAR_Validate
void
PEAR_Validate::_addFailure
(
string
$field
,
string
$reason
)
add a validation warning from one of the channel validation functions. Use this for a non-fatal warning of a potential problem situation.
$field
The package.xml section being validated (such as version
or
dependencies
)
$reason
A human-readable reason that validation failed.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::_addWarning
(
string
$field
,
string
$reason
)
add a validation warning from one of the channel validation functions. Use this for a non-fatal warning of a potential problem situation.
$field
The package.xml section being validated (such as version
or
dependencies
)
$reason
A human-readable reason that validation failed.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::getFailures
(
)
Returns a list of failures recorded by the last validation attempt. The list is in format:
<?php
array(
'error' =>
array(
array('field' => 'name', 'name must contain only letters, numbers or underscore'),
),
'warning' =>
array(
array('field' => 'version', 'version should be less than 2.0'),
)
);
?>
throws no exceptions thrown
This function can not be called statically.
array
PEAR_Validate::getValidStates
(
)
Utility function for future extensibility of the list of valid stability levels.
throws no exceptions thrown
final - this method must not be overridden
This function should be called statically.
void
PEAR_Validate::setPackageFile
(
PEAR_PackageFile_v1|PEAR_PackageFile_v2
&$pf
)
Sets the packagefile object that will be used to retrieve data for validation.
&$pf
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validate
(
int
$state
= null
)
Validate the package.xml passed into
setPackageFile().
The parameter passed in is the installer state that should be used, and is one of
PEAR_VALIDATE_INSTALLING
, PEAR_VALIDATE_DOWNLOADING
,
PEAR_VALIDATE_NORMAL
, PEAR_VALIDATE_UNINSTALLING
,
or PEAR_VALIDATE_PACKAGING
.
$state
one of the PEAR_VALIDATE_* constants
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateChangelog
(
)
Override this in a channel-specific validator to validate the contents of the <changelog> tag.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateDate
(
)
Override this in a channel-specific validator to validate the contents of the <date> tag.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateDependencies
(
)
Override this in a channel-specific validator to validate the <dependencies> tag of a package.xml 2.0-based release.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateDeps
(
)
Override this in a channel-specific validator to validate the contents of <deps> in a package.xml 1.0-based release.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateDescription
(
)
Override this in a channel-specific validator to validate the contents of the <description> tag.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateFilelist
(
)
Override this in a channel-specific validator to validate the contents of the <filelist> tag in a package.xml 1.0-based release.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateLicense
(
)
Override this method in a channel-specific validator to validate the contents of the <license> tag.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateMainFilelist
(
)
Override this in a channel-specific validator to validate the contents of the <contents> tag in package.xml 2.0-based releases.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateMaintainers
(
)
Override this in a channel-specific validator to validate the contents of <maintainers> in a package.xml 1.0-based release.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateNotes
(
)
Override this in a channel-specific validator to validate the contents of release notes in a <notes> tag.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validatePackageName
(
)
Override this in a channel-specific validator to validate a package name.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateReleaseFilelist
(
)
Use this to validate the contents of a <filelist> tag in a package.xml 2.0-based package.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateStability
(
)
Override this in a channel-specific validator to validate the <stability> tag of a package.xml 2.0-based release.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateState
(
)
Override this in a channel-specific validator to validate the contents of a <state> tag in a package.xml 1.0-based release.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateSummary
(
)
Override this in a channel-specific validator to validate the <summary> tag.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateTime
(
)
Override this method to validate the <time> tag in a channel-specific validator.
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validateVersion
(
)
Override this function in a channel validator in order to apply a different version validation scheme. An example of this use is in the PEAR_Validate_PECL class, which overrides validateVersion() to be less strict than the default PEAR_Validate::validateVersion().
Errors should be reported using _addFailure() method, and non-fatal errors (warnings) should be reported using the _addWarning() method.
throws no exceptions thrown
This function can not be called statically.
void
PEAR_Validate::validGroupName
(
string
$name
)
Dependency groups are documented here
$name
Dependency group name to validate
throws no exceptions thrown
final - this method should not be overridden.
This function should be called statically.
void
PEAR_Validate::validPackageName
(
string
$name
,
string
$validatepackagename
= false
)
Validate a package name string. The second parameter should be the name of the channel validation package, as defined by channel.xml for the current channel. If the package name being validated is the same as the validation package (case-insensitive), then it will be validated using the default rules for PEAR packages.
$name
package name to validate
$validatepackagename
name of channel-specific validation package
throws no exceptions thrown
final - this method should not be overridden.
This function can not be called statically.
bool
PEAR_Validate::validState
(
string
$state
)
This utility method can be used to determine whether a string is a valid state.
Currently, states must be one of snapshot
, devel
,
alpha
, beta
, and stable
.
$state
State string to validate.
throws no exceptions thrown
final - this method should not be overridden.
This function should be called statically.
bool
PEAR_Validate::validVersion
(
string
$ver
)
Use this method to test the validity of a version number string. All versions must be testable with PHP's version_compare().
$ver
Version string
throws no exceptions thrown
final - this method should not be overridden.
This function should be called statically.
bool
PEAR_Validate::_validPackageName
(
string
$name
)
This protected method can be used to change the normal package validation scheme. By default, all packages must begin with a letter and contain only letters, numbers and underscores. Using this method, it is possible to change this entirely to enforce another scheme.
For instance, enforcing java-style com.blah.package package names can be done simply by this code:
<?php
require_once 'PEAR/Validate.php';
class MyChannel_Validate extends PEAR_Validate
{
function _validPackageName($name)
{
return preg_match('/[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z0-9_]+)*/', $name);
}
}
?>
Then, by using a customized channel validation package, the installer will enforce java-style package names for your channel.
$name
package name string to test for validity.
throws no exceptions thrown
This function can not be called statically.
Name | Value | Line Number |
---|---|---|
PEAR_VALIDATE_DOWNLOADING | 4 | 28 |
PEAR_VALIDATE_INSTALLING | 1 | 25 |
PEAR_VALIDATE_NORMAL | 3 | 27 |
PEAR_VALIDATE_PACKAGING | 7 | 29 |
PEAR_VALIDATE_UNINSTALLING | 2 | 26 |
This package is not documented yet.
PEAR_XMLParser
array
PEAR_XMLParser::getData
(
)
Return the array representation of XML as parsed by parse().
throws no exceptions thrown
This function can not be called statically.
true|PEAR_Error
PEAR_XMLParser::parse
(
string
$data
)
Return an array that matches the XML parsed. This code is lifted from
Stephan Schmidt's XML_Unserializer
class in the XML_Serializer package. As such, tags are represented by an associative
array. Multiple tags are represented with a 0-based array of tag contents, and
attributes are represented by an array index named attribs
.
If attributes are present, the array index _contents
is used to
hold the contents of the xml tag.
$data
xml content
throws no exceptions thrown
This function can not be called statically.
string
PEAR_XMLParser::preProcessStupidSaxon
(
string
$data
)
This method examines xml data prior to parsing and replaces all entities like à with their equivalent (à in this case). It also scans the file for any non-ascii characters like à and replaces them with their entity equivalent (à).
This prevents saxon in PHP 4 and PHP 5 from choking on non-ISO-8859-1 characters.
$data
The xml data.
throws no exceptions thrown
This function can not be called statically.
$Date: 2009-06-26 05:22:00 $
Extending the PEAR Installer is made possible through custom file roles, file tasks, post-installation scripts and custom commands.
Custom file installation roles in package.xml
Custom file tasks in package.xml
$Date: 2008-10-09 15:16:18 $
As of PEAR version 1.4.3, the required format for custom file roles has been changed due to a minor security vulnerability. All custom file roles must now define a
Role.xml
file in order to properly declare a custom file role. See the example below for more information.
One of the programming features that the PEAR installer enforces is the separation of files into separate categories, and the important idea that files of a similar category are always installed into the same location, or at least handled the same way, as in the case of role="src" for PECL packages.
This has been quite successful for smaller library-style packages, but complete applications cannot function without customized installation locations. For instance, some files may be intended for use in a public web frontend, others for library location. PEAR 1.4 introduces the possibility of defining custom installation roles to fill this void.
To use a custom installation role that another programmer has written, there are three steps that are necessary. First, the <usesrole> tag should be used to define each custom role that is used in the package.xml. Next, the role should simply be used for the files it pertains to. Finally, a dependency on the package that provides the custom role should be added to the package.xml, just for completeness.
Pretty simple!
To define a custom role, you need to create a package containing a single file. Here is a sample package.xml that could be a custom role:
<?xml version="1.0"?> <package version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> <name>Chiarafoo</name> <channel>pear.chiaraquartet.net</channel> <summary>Chiarafoo role</summary> <description> The chiarafoo role installs files into your customized Foo directory </description> <lead> <name>Greg Beaver</name> <user>cellog</user> <email>cellog@php.net</email> <active>yes</active> </lead> <date>2005-03-21</date> <version> <release>1.0.0</release> <api>1.0.0</api> </version> <stability> <release>stable</release> <api>stable</api> </stability> <license uri="http://www.php.net/license">PHP License</license> <notes> Provides the chiarafoo file role </notes> <contents> <dir name="/" baseinstalldir="PEAR/Installer/Role"> <file name="Chiarafoo.xml" role="php"/> <file name="Chiarafoo.php" role="php"> <tasks:replace from="@package_version@" to="version" type="package-info"/> </file> </dir> <!-- / --> </contents> <dependencies> <required> <php> <min>4.2.0</min> </php> <pearinstaller> <min>1.4.3</min> </pearinstaller> </required> </dependencies> <phprelease/> </package>
The XML file Chiarafoo.xml should be similar to this file:
<role version="1.0"> <releasetypes>php</releasetypes> <installable>1</installable> <locationconfig>foo_dir</locationconfig> <honorsbaseinstall>1</honorsbaseinstall> <unusualbaseinstall /> <phpfile>1</phpfile> <executable /> <phpextension /> <config_vars> <foo_dir> <type>directory</type> <default><php_dir/><constant>DIRECTORY_SEPARATOR</constant><text>Foo</text></default> <doc>directory where foo files are installed</doc> <prompt>PHP foo directory</prompt> <group>File Locations</group> </foo_dir> </config_vars> </role>
The script in Chiarafoo.php is incredibly simple:
<?php
/**
* PEAR_Installer_Role_Chiarafoo
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category pear
* @package Chiarafoo
* @author Greg Beaver <cellog@php.net>
* @copyright 2005 Greg Beaver
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version SVN: $Id: customroles.xml,v 1.6 2008-10-09 15:16:18 cweiske Exp $
* @link http://pear.chiaraquartet.net/index.php?package=Chiarafoo
* @since File available since Release 0.1.0
*/
/**
* @category pear
* @package Chiarafoo
* @author Greg Beaver <cellog@php.net>
* @copyright 2005 Greg Beaver
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: @package_version@
* @link http://pear.chiaraquartet.net/index.php?package=Chiarafoo
* @since Class available since Release 0.1.0
*/
class PEAR_Installer_Role_Chiarafoo extends PEAR_Installer_Role_Common
{
/**
* @param PEAR_Installer
* @param PEAR_PackageFile_v2
* @param array file attributes
* @param string relative path to file in package.xml
*/
function setup(&$installer, $pkg, $atts, $file)
{
// do something special with the installer
}
}
?>
Since PEAR 1.4.3, nothing else is necessary for a successful implementation of a file role.
The contents of the role's XML file must contain these tags:
This tag can only contain one of the following values:
php
extsrc
extbin
In order to specify compatibility with multiple release types, use multiple <releasetypes> tags as in:
<releasetypes>php</releasetypes> <releasetypes>extsrc</releasetypes> <releasetypes>extbin</releasetypes>
releasetypes defines the kind of releases that this role can be used in. For instance, the "src" role is reserved for extsrc packages, and cannot be used in regular PEAR-style php releases. The "data" role can be used in any release, and would define <releasetypes> as:
<?php
<releasetypes>php</releasetypes>
<releasetypes>extsrc</releasetypes>
<releasetypes>extbin</releasetypes>
?>
This tag must be either <installable>1</installable> or empty (<installable/>) and determines whether files utilizing this custom role can be installed. Any file that should be installed must set this to 1. Only roles such as the "src" role that is processed and used to create the files that are eventually installed should set this to an empty tag.
This tag is used for all installable files to determine which configuration variable contains the directory in which the file should be installed. For instance, the php role uses the "php_dir" locationconfig, the data role uses "data_dir".
Our example role, chiarafoo, uses the "foo_dir" configuration variable, and then through the definition of <config_vars> tells the installer what foo_dir means. Note that once a custom role is installed, the user can config-set/ config-get/config-show the variable just like any other configuration variable!
<config_vars> can define more than one configuration variable, but note that more than 3 variables will trigger an error, disallowing any of them, as a security precaution.
This tag controls whether a role reacts to the "baseinstalldir" attribute of a <file> or <dir> tag. Any role that honors baseinstalldir can potentially allow conflicting files from different packages, and so a check has to be made. A file role that does not honor baseinstalldir is always installed into:
Packagename/full/path/from/contents/file.ext
To specify this value, use <honorsbaseinstall>1</honorsbaseinstall> For all other cases, it should be an empty tag (<honorsbaseinstall/>)
This tag controls whether a role that would normally ignore the
"baseinstalldir" attribute of a <file> or <dir> tag would honor it,
but still use the Packagename/baseinstalldir/full/path/from/contents/file.ext
instead of
baseinstalldir/full/path/from/contents/file.ext
. Any role that
supports the unusual baseinstalldir type cannot conflict with other files because the
package name is always the parent directory. To specify this value, use
<unusualbaseinstall>1</unusualbaseinstall>
For all other cases, it should be an empty tag (<unusualbaseinstall/>)
This tag should be set to 1 (<phpfile>1</phpfile>) for any roles that contain php script files. These files are analyzed at package time, possibly catching errors before release. For all other cases, it should be an empty tag (<phpfile/>)
This tag should be set to 1 (<executable>1</executable>) for roles like the "script" role that should have their executable attribute set via chmod() on install. For all other cases, it should be an empty tag (<executable/>)
This tag should be used for roles like the "ext" role that provide a binary PHP extension. To specify this value, use <phpextension>1</phpextension> For all other cases, it should be an empty tag (<phpextension/>)
This tag is used to define configuration variables that should be added to the installer by this custom file role. Note that the installer will not allow a custom file role to create more than 3 configuration variables. To define configuration variables, create tags with the name of the configuration variable, and then sub-tags defining information about the configuration variable.
These tags are transformed into the PHP array format expected by the PEAR_Config class using an adapted version of Stephan Schmidt's excellent XML_Unserializer class (from the XML_Serializer package). As such, it is easiest to understand the XML format by examining existing configuration variables.
<?php
array(
'password' => array(
'type' => 'password',
'default' => '',
'doc' => '(maintainers) your PEAR account password',
'prompt' => 'PEAR password (for maintainers)',
'group' => 'Maintainers',
),
// Advanced
'verbose' => array(
'type' => 'integer',
'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
'doc' => 'verbosity level
0: really quiet
1: somewhat quiet
2: verbose
3: debug',
'prompt' => 'Debug Log Level',
'group' => 'Advanced',
),
'preferred_state' => array(
'type' => 'set',
'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
'valid_set' => array(
'stable', 'beta', 'alpha', 'devel', 'snapshot'),
'prompt' => 'Preferred Package State',
'group' => 'Advanced',
),
);
?>
These sample configuration values from the actual PEAR_Config class would translate into this XML:
<config_vars> <password> <type>password</type> <default /> <doc>(maintainers) your PEAR account password'</doc> <prompt>PEAR password (for maintainers)</prompt> <group>Maintainers</group> </password> <verbose> <type>integer</type> <default><constant>PEAR_CONFIG_DEFAULT_VERBOSE</constant></default> <doc>verbosity level 0: really quiet 1: somewhat quiet 2: verbose 3: debug</doc> <prompt>Debug Log Level</prompt> <group>Advanced</group> </verbose> <preferred_state> <type>set</type> <default><constant>PEAR_CONFIG_DEFAULT_PREFERRED_STATE</constant></default> <doc>the installer will prefer releases with this state when installing packages without a version or state specified</doc> <valid_set>stable</valid_set> <valid_set>beta</valid_set> <valid_set>alpha</valid_set> <valid_set>devel</valid_set> <valid_set>snapshot</valid_set> <prompt>Preferred Package State</prompt> <group>Advanced</group> </preferred_state> </config_vars>
Note that the simple array defining the set converts each value into a separate <valid_set> tag for the preferred_state configuration variable.
The <default> tag's value can accept either a simple string, or three different kinds of tags:
<text> - this will be converted into a PHP string
<constant> - the contents of this sub-tag will be used to retrieve the value of a pre-defined PHP constant, or a pre-defined PEAR constant (as defined in PEAR_Config or PEAR_Common) and substitute the value for the <constant> tag.
any default configuration variable. These include default_channel, preferred_mirror, remote_config, auto_discover, master_server, http_proxy, php_dir, ext_dir, doc_dir, bin_dir, data_dir, test_dir, cache_dir, php_bin, username, password, verbose, preferred_state, umask, cache_ttl, sig_type, sig_bin, sig_keyid, and sig_keydir.
when used, the configuration variable should simply be an empty tag like <php_dir/>. The tag will be replaced with the default value of the configuration variable, not the currently assigned value.
For instance, this example default value:
<default><text0>hi there </text0><constant>PHP_OS</constant><text1> user, your default php_dir is </text1><php_dir/></default>
might convert into something like "hi there Linux user, your default php_dir is /usr/local/lib/php/pear".
Our example Chiarafoo role's foo_dir default value:
<default><php_dir/><constant>DIRECTORY_SEPARATOR</constant><text>Foo</text></default>
might convert into something like "/usr/local/lib/php/Foo" or "C:\php5\pear\Foo".
Note that in order to use multiple <constant> or <text> tags, you must append a numbered suffix as in the <text0> <text1> example above. Only one PEAR configuration variable may be used per default value.
Note that if you use <type>integer</type>, no matter what default value is specified, it will be casted into an integer by PEAR_Config.
$Date: 2008-10-09 15:16:18 $
For many small library packages, very little customization is needed. Just install and go. package.xml 1.0 is very good at this task. As packages grow in size and complexity, it is often necessary to make slight changes to the contents of files, and occasionally to external components such as databases.
package.xml 1.0 provides a single undocumented system of customizing file contents through the <replace> tag, like so:
<file name="blah.php" role="php"> <replace from="@token@" to="version" type="package-info"/> <replace from="@anothertoken@" to="php_dir" type="pear-config"/> </file>
This example above would scan the blah.php file at installation, and then use
str_replace()
to replace all occurrences of the string @token@
with the package's
version number. Then, it would replace all occurrences of the string
@anothertoken@
with the value of the user's php_dir configuration
variable.
Although powerful, the replace tag is the only customization tag available in package.xml version 1.0. When developing package.xml version 2.0, the replace tag and innovative work of other projects such as Phing became the inspiration for an expanded kind customization called a "task".
Tasks are defined by xml children of a <file> tag. Tasks are implemented by extending the PEAR_Task_Common task.
Tasks are defined in package.xml through the tasks namespace, which is currently
http://pear.php.net/dtd/tasks-1.0
. The current tasks
namespace is defined by http://pear.php.net/dtd/tasks-1.0.xsd.
Custom tasks bundled with PEAR include:
The xml for each of these tasks is documented here.
Creating a custom task involves creating a class that validates xml, and performs the task when called upon. Tasks can be invoked in two situations: at package-time and at install-time. Each task can control whether it should be called at package-time, install-time, or at both times.
There are two kinds of tasks: simple and multiple. Most tasks are simple tasks. Simple tasks are self-contained: all customization is limited to that file. Multiple tasks are collected during installation and processed fully as a unit after files have been committed to disk, allowing more complex processing.
All simple tasks must define 3 methods, all multiple tasks must define 4 methods. These are:
run() (multiple tasks only)
true|array validateXml (
PEAR_PackageFile_v2 $pkg
, string|array $xml
, PEAR_Config &$config
, array $fileAttributes
)
This method is called upon package.xml validation and should be used to validate
the task's xml content. Upon error, a simple array of format
array(CODE, message)
must be returned. The code must be one
of the following constants, as defined in PEAR/Task/Common.php
:
PEAR_TASK_ERROR_NOATTRIBS - Attributes were expected, but none were present.
PEAR_TASK_ERROR_MISSING_ATTRIB - A specific attribute is not present.
PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE - The value of an attribute is incorrect.
PEAR_TASK_ERROR_INVALID - Any other error in validation.
The error message should include the file name as defined by $fileAttributes['name']
, and include as much useful information about the location
of the validation error as possible.
$pkg
This is the package.xml object that contains the task.
$xml
The raw parsed content of the task's xml as parsed from package.xml. Tags
like <tasks:windowseol> that have no attributes or child elements will
be passed an empty string ''
. Otherwise, simple text content
will be a string, and nested tags/attributes will be passed in as an array. Here
is a list of sample xml and their parsed values:
<tasks:blah/>
string("");
<tasks:blah>hello </tasks:blah>
string("hello");
<tasks:blah>hello <tasks:boo/> </tasks:blah>
array('_content' => 'hello', 'tasks:boo' => string(''))
<tasks:blah foo="one"> <tasks:boo/> </tasks:blah>
array('attribs' => array('foo' => 'one'),
'tasks:boo' => string(''))
$config
This is the current configuration object
$fileAttributes
The parsed attributes of the <file> tag that encloses this tag. This
is guaranteed to contain indices name
, specifying the file name,
and role
, specifying the file role. Other attributes like
baseinstalldir
may be present but are not required, and so will
not be guaranteed to be present for every file.
void init (
string|array $xml
, array $fileAttributes
, string|null $lastVersion
)
The init() function is called immediately prior to the startSession() method, and should be used for initialization that is not directly related to file modification. This method may move to another location in the installation process at any time. The logical separation of initialization from task action is important and the order of execution can be depended upon.
$xml
The raw parsed content of the task's xml as parsed from package.xml. Tags
like <tasks:windowseol> that have no attributes or child elements will
be passed an empty string ''
. Otherwise, simple text content
will be a string, and nested tags/attributes will be passed in as an array. Here
is a list of sample xml and their parsed values:
<tasks:blah/>
string("");
<tasks:blah>hello </tasks:blah>
string("hello");
<tasks:blah>hello <tasks:boo/> </tasks:blah>
array('_content' => 'hello', 'tasks:boo' => string(''))
<tasks:blah foo="one"> <tasks:boo/> </tasks:blah>
array('attribs' => array('foo' => 'one'),
'tasks:boo' => string(''))
$fileAttributes
The parsed attributes of the <file> tag that encloses this tag. This
is guaranteed to contain indices name
, specifying the file name,
and role
, specifying the file role. Other attributes like
baseinstalldir
may be present but are not required, and so will
not be guaranteed to be present for every file.
$lastVersion
This will be set to the string representing the version of the last installed version of this package. This is useful for determining whether the package is being upgraded or is a fresh installation. If the package is being installed for the first time, NULL will be passed in.
string|false|PEAR_Error startSession (
string $contents
, string $dest
)
For non-script tasks, startSession() is called to actually execute a task. The task should perform all needed operations on the file contents, and on success return the file contents regardless of any modification. These contents will be written to disk, so it is imperative that they be the full, original file contents if no modification is made to them.
For script tasks, startSession() is called to determine whether the script can be safely executed. For both task types, script and non-script, a return value of FALSE will cause the task to be silently skipped. A return value of a PEAR_Error will cause processing of all operations to stop, and an error message to be displayed by the installer prior to exiting. Any other return value will cause the script to be processed normally by the frontend as a post-installation script.
$contents
The original contents of the file whose <file> tag in package.xml contains the task tag.
$dest
The full path to the final installed file location. This is strictly informational, as the file does not yet exist, and should only be used for error messages or other processing that does not attempt to modify the file.
false|string run (
array $tasks
)
The run() method should return FALSE on success, and an
error message if there is a problem. This method is called only for tasks
that define $this->type
as multiple
.
For each task within package.xml that has this task, the run()
method will be called with an array containing all of the task objects from
the package.xml.
$tasks
An array of task objects.
$Date: 2008-11-17 19:44:01 $
Incomplete documentation
Documentation is not yet complete
The XML format for defining a post-install script in package.xml is documented here. This document describes the required elements for the PHP post-install script itself.
Post-install script files can be named anything one desires, but the class within
the file must be the same name as the file with all path separators replaced by
underscores plus "_postinstall
".
In other words, this postinstall script:
Path/To/Script.php
must contain a class named Path_To_Script_postinstall Due to casing differences between operating systems, it is recommended to always use lowercased file names.
The post-install script class must contain two methods, one named init(), and the other named run(). The init() method is called at the same time as all other post-install scripts. The run() method is called at the conclusion of each parameter group in order to process the user's responses to queries.
boolean init (
PEAR_Config $config
, PEAR_PackageFile_v2 $self
, string|null $lastInstalledVersion
)
$xml
The current configuration used for installation.
$self
The package.xml contents as abstracted by this object.
$lastInstalledVersion
The last version of this package that was installed. This is a very important parameter, as it is the only way to determine whether a package is being installed from scratch, or upgraded from a previous version. Using this parameter, it is possible to determine what incremental changes, if any, need to be performed.
The function has to return TRUE when initialization succeeded, FALSE when it failed. In the latter case, the post install script it stopped.
void run (
array $infoArray
, string $paramGroupId
)
$infoArray
if $paramGroupId
is _undoOnError
, then
$infoArray will contain a list of successfully completed parameter group
sections. This can be used to restore any system changes made by the installation
script.
Otherwise, $infoArray
contains the results of the user input
from the most recent <paramgroup> section.
$paramGroupId
This variable either contains _undoOnError
or the contents
of the most recent <paramgroup>'s <id> tag. Note that paramgroup
id cannot begin with an underscore (_
), and so
_undoOnError
can only be triggered by the PEAR installer.
Request for comments are normative documents about how PEAR works. RFCs get proposed through PEAR's PEPr system.
All RFCs listed here have been accepted by the PEAR developers through PEPr voting.
2008-11-05
The PEAR Group has voted and accepted this document. There will be no voting among developers.
This proposal has been in PEPr as proposal #538.
This RFC defines changes and enhancements to the current Coding Standards (CS) in PEAR. They are neccessary because the standards are not clear in some cases. With applications like PHP_CodeSniffer being used, is is important that those issues are worked out and tools can deliver reliable warnings and CS errors.
Most of the issues we currently have deal with long lines and how they should be split. General rule of thumb is that when splitting a line, the originating lines are indented by 4 spaces. Futher, the format shall allow it to easily comment out those lines - be it for debugging or development reasons. This implies that closing braces are to be put on a line on its own, and commas at the end of a line.
While it might seem archaic to some, the 75-80 line rule is still important. Please see http://paul-m-jones.com/?p=276 for an in-depth discussion on the topic.
Functions with many parameters need to be split onto several lines
to keep the 80 chars/line limit. The first parameters may be put onto the
same line as the function name if there is enough space. Subsequent
parameters on following lines are to be indented 4 spaces. The closing
parenthesis and the opening brace are to be put onto the next line, on the
same indentation level as the "function
" keyword.
<?php
function someFunctionWithAVeryLongName($firstParameter = 'something', $secondParameter = 'booooo',
$third = null, $fourthParameter = false, $fifthParameter = 123.12,
$sixthParam = true
) {
//....
?>
The CS require lines to have a maximum length of 80 chars. Calling functions or methods with many parameters while adhering to CS is impossible in that cases. It should be allowed to split parameters in function calls onto several lines.
<?php
$this->someObject->subObject->callThisFunctionWithALongName(
$parameterOne, $parameterTwo,
$aVeryLongParameterThree
);
?>
Several parameters per line should be allowed. Parameters need to be indented 4 spaces compared to the level of the function call. The opening parenthesis is to be put at the end of the function call line, the closing parenthesis gets its own line at the end of the parameters. This shows a visual end to the parameter indentations and follows the opening/closing brace rules for functions and conditionals. (See bug #11562)
The same applies not only for parameter variables, but also for nested function calls and for arrays.
<?php
$this->someObject->subObject->callThisFunctionWithALongName(
$this->someOtherFunc(
$this->someEvenOtherFunc(
'Help me!',
array(
'foo' => 'bar',
'spam' => 'eggs',
),
23
),
$this->someEvenOtherFunc()
),
$this->wowowowowow(12)
);
?>
Nesting those function parameters is allowed if it helps to make the code more readable, not only when it is necessary when the characters per line limit is reached.
Using fluent application programming interfaces often leads to many
concatenated function calls. Those calls may be split onto several lines.
When doing this, all subsequent lines are indented by 4 spaces and begin
with the "->
" arrow.
<?php
$someObject->someFunction("some", "parameter")
->someOtherFunc(23, 42)
->andAThirdFunction();
?>
Assigments may be split onto several lines when the character/line limit would be exceeded. The equal sign has to be positioned onto the following line, and indented by 4 characters.
<?php
$GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName]
= $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax'));
?>
Long if statements may be split onto several lines when the
character/line limit would be exceeded. The conditions have to be
positioned onto the following line, and indented 4 characters. The logical
operators (&&
, ||
, etc.)
should be at the beginning of the line to make it easier to comment (and
exclude) the condition. The closing parenthesis and opening brace get
their own line at the end of the conditions.
Keeping the operators at the beginning of the line has two advantages: It is trivial to comment out a particular line during development while keeping syntactically correct code (except of course the first line). Further is the logic kept at the front where it's not forgotten. Scanning such conditions is very easy since they are aligned below each other.
<?php
if (($condition1
|| $condition2)
&& $condition3
&& $condition4
) {
//code here
}
?>
The first condition may be aligned to the others.
<?php
if ( $condition1
|| $condition2
|| $condition3
) {
//code here
}
?>
The best case is of course when the line does not need to be split.
When the if clause is really long enough to be split, it might be better
to simplify it. In such cases, you could express conditions as variables
an compare them in the if()
condition. This has the
benefit of "naming" and splitting the condition sets into smaller, better
understandable chunks:
<?php
$is_foo = ($condition1 || $condition2);
$is_bar = ($condition3 && $condtion4);
if ($is_foo && $is_bar) {
// ....
}
?>
There were suggestions to indent the parantheses "groups" by 1 space for each grouping. This is too hard to achieve in your coding flow, since your tab key always produces 4 spaces. Indenting the if clauses would take too much finetuning.
The same rule as for if clauses also applies for the ternary operator: It may be split onto several lines, keeping the question mark and the colon at the front.
<?php
$a = $condition1 && $condition2
? $foo : $bar;
$b = $condition3 && $condition4
? $foo_man_this_is_too_long_what_should_i_do
: $bar;
?>
To support readability, parameters in subsequent calls to the same function/method may be aligned by parameter name:
<?php
$this->callSomeFunction('param1', 'second', true);
$this->callSomeFunction('parameter2', 'third', false);
$this->callSomeFunction('3', 'verrrrrrylong', true);
?>
To support readability, the equal signs may be aligned in block-related assignments:
<?php
$short = foo($bar);
$longer = foo($baz);
?>
The rule can be broken when the length of the variable name is at least 8 characters longer/shorter than the previous one:
<?php
$short = foo($bar);
$thisVariableNameIsVeeeeeeeeeeryLong = foo($baz);
?>
Assignments in arrays may be aligned. When splitting array definitions onto several lines, the last value may also have a trailing comma. This is valid PHP syntax and helps to keep code diffs minimal:
<?php
$some_array = array(
'foo' => 'bar',
'spam' => 'ham',
);
?>
Related lines of code should be grouped into blocks, separated from each other to keep readability as high as possible. The definition of "related" depends on the code :)
For example:
<?php
if ($foo) {
$bar = 1;
}
if ($spam) {
$ham = 1;
}
if ($pinky) {
$brain = 1;
}
?>
is a lot easier to read when separated:
<?php
if ($foo) {
$bar = 1;
}
if ($spam) {
$ham = 1;
}
if ($pinky) {
$brain = 1;
}
?>
To keep readability in functions and methods, it is wise to return early if simple conditions apply that can be checked at the beginning of a method:
<?php
function foo($bar, $baz)
{
if ($foo) {
//assume
//that
//here
//is
//the
//whole
//logic
//of
//this
//method
return $calculated_value;
} else {
return null;
}
}
?>
It's better to return early, keeping indentation and brain power needed to follow the code low.
<?php
function foo($bar, $baz)
{
if (!$foo) {
return null;
}
//assume
//that
//here
//is
//the
//whole
//logic
//of
//this
//method
return $calculated_value;
}
?>
We should keep to the 4 space indentation rule. Allowing 6, 8 or any other number for "personal preference" is an absurd line in a "standard".
Of course, the best rule is keeping your code easy and clean, avoing dozens of parameters to a function ("code smell" for long parameter lists). But sometimes there is no way to avoid functions with 6 parameters, and having default values for them does not simplfy the situation. The rules here are exactly for that code lines.
This document defines guidelines for error handling within PEAR, for PHP5 packages. It was written to cope with Exceptions, introduced in Zend Engine 2 as the error handling mechanism. The final objective is to integrate the document text into the PEAR Coding Guidelines.
This RFC has been ratified in PEPr as proposal #132.
This document is targeted at PHP developers writing packages for submission into the PEAR repository. As any coding guidelines, it is useful to developers using PHP in other environments. The requirements for reading this text are only familiarity with PHP as a programming language, as well as familiarity with the mechanism of Exceptions as an error handling mechanism.
For those PHP developers unfamiliar with Exceptions, the wiki page from which this document was extracted provides a good introduction, as well as pointers to other references.
An error is defined as an unexpected, invalid program state from which it is impossible to recover. For the sake of definition, recovery scope is defined as the method scope. Incomplete recovery is considered a recovery.
One pretty straightforward example for an error:
<?php
/**
* Connect to Specified Database
*
* @throws Example_Datasource_Exception When it can't connect
* to specified DSN.
*/
function connectDB($dsn)
{
$this->db =& DB::connect($dsn);
if (DB::isError($this->db)) {
throw new Example_Datasource_Exception(
"Unable to connect to $dsn:" . $this->db->getMessage()
);
}
}
?>
In this example the objective of the method is to connect to the given DSN. Since it can't do anything but ask PEAR DB to do it, whenever DB returns an error, the only option is to bail out and launch the exception.
The next example will introduce the concept of recovery:
<?php
/**
* Connect to one of the possible databases
*
* @throws Example_Datasource_Exception When it can't connect to
* any of the configured databases.
* @throws Example_Config_Exception When it can't find databases
* in the configuration.
*/
function connect(Config $conf)
{
$dsns =& $conf->searchPath(array('config', 'db'));
if ($dsns === false) {
throw new Example_Config_Exception(
'Unable to find config/db section in configuration.'
);
}
$dsns =& $dsns->toArray();
foreach ($dsns as $dsn) {
try {
$this->connectDB($dsn);
return;
} catch (Example_Datasource_Exception $e) {
// Some warning/logging code recording the failure
// to connect to one of the databases
}
}
throw new Example_Datasource_Exception(
'Unable to connect to any of the configured databases'
);
}
?>
This second example shows an exception being caught and recovered from. Altough the lower level connectDB method is unable to do anything but throw an error when one database connection fails, the upper level connect method knows the object can go by with any one of the configured databases. Since the error was recovered from, the exception is silenced at this level and not rethrown.
The last example illustrates incomplete recovery:
<?php
/**
* loadConfig parses the provided configuration. If the configuration
* is invalid, it will set the configuration to the default config.
*/
function loadConfig(Config $conf)
{
try {
$this->config = $conf->parse();
} catch (Config_Parse_Exception e) {
// Warn/Log code goes here
// Perform incomplete recovery
$this->config = $this->defaultConfig;
}
}
?>
The recovery produces side effects, so it is considered incomplete. However, the program may proceed, so the exception is considered handled, and must not be rethrown. As in the previous example, when silencing the exception, logging or warning should occur.
Error conditions in PEAR packages written for PHP5 must be signaled using exceptions. Usage of return codes or return PEAR_Error objects is deprecated in favor of exceptions. Naturally, packages providing compatibility with PHP4 do not fall under these coding guidelines, and may thus use the error handling mechanisms defined in the PHP4 PEAR coding guidelines.
An exception should be thrown whenever an error condition is met, according to the definition provided in the previous section. The thrown exception should contain enough information to debug the error and quickly identify the error cause. Note that, during production runs, no exception should reach the end-user, so there is no need for concern about technical complexity in the exception error messages.
The basic PEAR_Exception contains a textual error, describing the program state that led to the throw and, optionally, a wrapped lower level exception, containing more info on the lower level causes of the error.
The kind of information to be included in the Exception is dependent on the error condition. From the point of view of exception throwing, there are three classes of error conditions:
Errors detected during precondition checks
Lower level library errors signaled via error return codes or error return objects.
Uncorrectable lower library exceptions.
Errors detected during precondition checks should contain a description of the failed check. If possible, the description should contain the violating value. Naturally, no wrapped exception can be included, as there isn't a lower level cause of the error. Example:
<?php
function divide($x,$y)
{
if ($y == 0) {
throw new Example_Aritmetic_Exception('Divide by zero');
}
return $x/$y;
}
?>
Errors signaled via return codes by lower level libraries, if unrecoverable, should be turned into exceptions. The error description should try to convey all information contained in the original error. One example, is the connect method previously presented:
<?php
/**
* Connect to Specified Database
*
* @throws Example_Datasource_Exception when it can't connect to specified DSN.
*/
function connectDB($dsn)
{
$this->db =& DB::connect($dsn);
if (DB::isError($this->db)) {
throw new Example_Datasource_Exception(
"Unable to connect to $dsn:" . $this->db->getMessage()
);
}
}
?>
Lower library exceptions, if they can't be corrected, should either be rethrown or bubbled up. When rethrowing, the original exception must be wrapped inside the one being thrown. When letting the exception bubble up, the exception just isn't handled and will continue up the call stack in search of a handler.
One example for rethrowing:
<?php
function preTaxPrice($retailPrice, $taxRate)
{
try {
return $this->divide($retailPrice, 1 + $taxRate);
} catch (Example_Aritmetic_Exception e) {
throw new Example_Tax_Exception('Invalid tax rate.', e);
}
}
?>
And the same example for bubbling up:
<?php
function preTaxPrice($retailPrice, $taxRate)
{
return $this->divide($retailPrice, 1 + $taxRate);
}
?>
The case between rethrowing or bubbling up is one of software architecture: Exceptions should be bubbled up, except in these two cases:
The original exception is from another package. Letting it bubble up would cause implementation details to be exposed, violating layer abstraction, conducing to poor design.
The current method can add useful debugging information to the received error before rethrowing.
Exceptions should never be used as normal program flow. If removing all exception handling logic (try-catch statements) from the program, the remaining code should represent the "One True Path" -- the flow that would be executed in the absence of errors.
This requirement is equivalent to requiring that exceptions be thrown only on error conditions, and never in normal program states.
One example of a method that wrongly uses the bubble up capability of exceptions to return a result from a deep recursion:
<?php
/**
* Recursively search a tree for string.
*
* @throws ResultException
*/
public function search(TreeNode $node, $data)
{
if ($node->data === $data) {
throw new ResultException( $node );
} else {
search( $node->leftChild, $data );
search( $node->rightChild, $data );
}
}
?>
In the example the ResultException is simply using the "eject!" qualities of exception handling to jump out of deeply nested recursion. When actually used to signify an error this is a very powerful feature, but in the example above this is simply lazy development.
All of PEAR packages exceptions must be descendant from PEAR_Exception. PEAR_Exception provides exception wrapping abilities, absent from the top level PHP Exception class, and needed to comply with the previous section requirements.
Aditionally, each PEAR package must provide a top level exception, named <Package_Name>_Exception. It is considered best practice that the package never throws Exceptions that aren't descendant from its top level exception.
Because PHP, unlike Java, does not require you to explicitly state which Exceptions a method throws in the method signature, it is critical that Exceptions be thoroughly documented in your method headers.
Exceptions should be documented using the @throws
phpdoc keyword:
<?php
/**
* This method searches for aliens.
*
* @return array Array of Aliens objects.
*
* @throws AntennaBrokenException If the impedence readings indicate
* that the antenna is broken.
* @throws AntennaInUseException If another process is using the
* antenna already.
*/
public function findAliens($color = 'green');
?>
In many cases middle layers of an application will rewrap any lower-level exceptions into more meaningful application exceptions. This also needs to be made clear:
<?php
/**
* Load session objects into shared memory.
*
* @throws LoadingException Any lower-level IOException will be wrapped
* and re-thrown as a LoadingException.
*/
public function loadSessionObjects();
?>
In other cases your method may simply be a conduit through which lower level exceptions can pass freely. As challenging as it may be, your method should also document which exceptions it is not catching.
<?php
/**
* Performs a batch of database queries (atomically, not in transaction).
*
* @throws SQLException Low-level SQL errors will bubble up through this method.
*/
public function batchExecute();
?>
Exceptions play a critical role in the API of your library. Developers using your library depend on accurate descriptions of where and why exceptions might be thrown from your package. Documentation is critical. Also maintaining the types of messages that are thrown is also an important requirement for maintaining backwards-compatibility.
Because Exceptions are critical to the API of your package, you must ensure that you don't break backwards compatibility by making changes to exceptions.
Things that break BC include:
Any change to which methods throw exceptions.
A change whereby a method throws an exception higher in the
inheritance tree. For example, if you changed your method to throw a
PEAR_Exception
rather than a
PEAR_IOException
, you would be breaking backwards
compatibility.
Things that do not break BC:
Throwing a subclass of the original exception. For example,
changing a method to throw PEAR_IOException
when
before it had been throwing PEAR_Exception
would
not break BC (provided that PEAR_IOException
extends PEAR_Exception
).
2006-09-05
This RFC has been ratified in PEPr as proposal #419.
First stable release of PHP version 5 was done two years ago. This version of language has matured a lot throughout these two years and with PHP version 4 development effectively stopped should already be considered the preferred platform by developers of new PHP libraries and applications.
PEAR policies encourage "backwards compatibility", which unfortunately means supporting the inadequate object model of PHP version 4. While it does make sense for existing packages, requiring such "backwards compatibility" for new packages that get accepted into PEAR has at least two problems:
It slows the adoption of PHP version 5 by users of PEAR
PEAR risks becoming a garbage dump of obsolescent code, or at least be perceived as one
The proposed solution is to focus on "forward compatibility"
instead. PHP version 5 has a builtin means to ensure that code is forward
compatible, which is E_STRICT
error reporting level.
The package is considered E_STRICT
-compatible
if
it can be used under PHP 5.1.4+
its files do not emit error messages when used with error
reporting level set to E_ALL | E_STRICT
under PHP
5.1.4+
it follows PEAR coding standards that apply to PHP5-only packages
it has only E_STRICT
-compatible required
dependencies
E_STRICT
-compatible package may have optional
dependencies that are not E_STRICT
-compatible, but this
is discouraged.
After this RFC is accepted, a deadline for accepting the new non
E_STRICT
-compatible packages is set as January 1, 2007.
This deadline should be prominently announced on PEAR website and in
PEAR manual. All new PHP5-related coding
standards should also be integrated into the manual.
Developers wishing to propose a new non
E_STRICT
-compatible package or start work on a non
E_STRICT
-compatible new major version (as defined in
New
guidelines for BC breaking releases) of an existing package
should do so before the deadline.
After January 1, 2007 all new packages proposed via PEPr and all
new major versions of existing packages should be
E_STRICT
-compatible. Proposals for non
E_STRICT
-compatible packages that reach this deadline not
in "Called for votes" state should not be called for votes until the
package is reworked to be E_STRICT
-compatible.
the person proposing a new package may choose to also provide a
version of said package that can be run under PHP version 4. Such a
version requires a separate proposal that may only be called for votes
after the proposal of E_STRICT
-compatible version is
accepted. The package must have the name of
E_STRICT
-compatible version with 'PHP4' appended (ex.:
Example_Foo and
Example_FooPhp4).
if a package does not yet have
E_STRICT
-compatible version then non
E_STRICT
-compatible subpackages for it may be accepted
at the discretion of base package's developers. If base package
already has E_STRICT
-compatible version then acceptance
of such subpackages falls under the previous exception.
Development and release process of existing PEAR packages which
are not E_STRICT
-compatible may continue as usual. The
only new requirement is that if a new major version of a package is
started, it should be E_STRICT
-compatible.
If a new major version of an existing package was registered on PEAR website before the deadline is reached or the code of this new major version was present in PEAR SVN then this version is considered an "existing package" for the purpose of this RFC.
Nevertheless, developers of existing packages are strongly
encouraged to update their packages for
E_STRICT
-compatibility if a BC break is still allowed by
Version
Naming guidelines.
Several people expressed the concern that mandating
E_STRICT
dependencies may slow the development. While this
is true, one of the goals of this proposal is to encourage the rewrite of
base PEAR classes to PHP5.
Methods that are intended to be called statically should be
defined with static
keyword
The $foo =& new Foo()
construct should
not be used
instanceof
operator should be used instead of
is_a()
function
Declarations of methods in child classes should be compatible with those in parent classes
These changes obviously imply following the Error Handling Guidelines for PHP5
packages since PEAR class itself is not
E_STRICT
-compatible.
Also the new object model should be taken into account: this means removing unneded references when working with objects and using clone where needed.
The current "Header Comment Blocks" portion of the Coding Standards was basically copied from PHP sources without alteration. Those headers are often construed to indicate that the license summary for the PHP License and the PHP Group copyright must be included in each file, even if a different license and/or copyright are used.
In addition, the header comments aren't parseable by phpDocumentor.
2005-02-02
This RFC has been ratified in PEPr as proposal #128.
This RFC has been integrated into the Coding Standards under the Header Comment Blocks section.
All source code files in the PEAR repository shall contain a "page-level" docblock at the top of each file and a "class-level" docblock immediately above each class. Below are examples of such docblocks.
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Short description for file
*
* Long description for file (if any)...
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category CategoryName
* @package PackageName
* @author Original Author <author@example.com>
* @author Another Author <another@example.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version SVN: $Id: header-comments.xml,v 1.4 2009-05-04 20:16:19 cweiske Exp $
* @link http://pear.php.net/package/PackageName
* @see NetOther, Net_Sample::Net_Sample()
* @since File available since Release 1.2.0
* @deprecated File deprecated in Release 2.0.0
*/
// Place includes, constant defines and $_GLOBAL settings here.
/**
* Short description for class
*
* Long description for class (if any)...
*
* @category CategoryName
* @package PackageName
* @author Original Author <author@example.com>
* @author Another Author <another@example.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: @package_version@
* @link http://pear.php.net/package/PackageName
* @see NetOther, Net_Sample::Net_Sample()
* @since Class available since Release 1.2.0
* @deprecated Class deprecated in Release 2.0.0
*/
class foo
{
}
?>
Short descriptions must be provided for all docblocks. They should be a quick sentence, not the name of the item. Please read the Coding Standard's sample file about how to write good descriptions.
One of the following must go in the page-level docblock:
PHP version 4
PHP version 5
PHP versions 4 and 5
There are several possible licenses. One of the following must be picked and placed in the page-level and class-level docblocks:
@license http://www.apache.org/licenses/LICENSE-2.0
Apache License 2.0
@license
http://www.freebsd.org/copyright/freebsd-license.html BSD License (2
Clause)
@license http://www.debian.org/misc/bsd.license BSD
License (3 Clause)
@license
http://www.freebsd.org/copyright/license.html BSD License (4
Clause)
@license http://www.gnu.org/copyleft/lesser.html LGPL
License 2.1
@license http://www.php.net/license/3_0.txt PHP
License 3.0
For more information, see the PEAR Group's Licensing Announcement: http://pear.php.net/group/docs/20040402-la.php
The following must be used in both the page-level and class-level docblocks. Of course, change "PackageName" to the name of your package. This ensures the generated documentation links back your package.
@link
http://pear.php.net/package/PackageName
There's no hard rule to determine when a new code contributor should be added to the list of authors for a given source file. In general, their changes should fall into the "substantial" category (meaning somewhere around 10% to 20% of code changes). Exceptions could be made for rewriting functions or contributing new logic.
Simple code reorganization or bug fixes would not justify the addition of a new individual to the list of authors.
This tag is required when a file or class is added after the package's initial release. Do not use it in an initial release.
This tag is required when a file or class is no longer used but has been left in place for backwards compatibility.
Feel free to apply whatever copyrights you desire. When formatting this tag, the year should be in four digit format and if a span of years is involved, use a hyphen between the earliest and latest year. The copyright holder can be you, a list of people, a company, the PHP Group, etc.
Examples:
@copyright 2003 John Doe and Jennifer
Buck
@copyright 2001-2004 John Doe
@copyright 1997-2004 The PHP Group
@copyright 2001-2004 XYZ Corporation
If you are using the PHP License, use the summary text provided
above. If another license is being used, please remove the PHP License
summary. Feel free to substitute it with text appropriate to your
license, though to keep things easy to locate, please preface the text
with "LICENSE:
".
There were several comments about what to put in the @version
tags. This proposal uses the SVN "Id
" tag for the
"page-level" docblock which covers the file, while the class-level
docblocks will use @package_version@
, which gets
replaced with the release's version number by the PEAR installer.
This seems the best compromise because $Id$
's
talk about files while the release version numbers talk about the
classes. $Id$
is used rather than
$Revision$
or other shorter tags because it is the
standard and provides all of the potentially desired information.
Add a @see
tag when you want to refer users to
other sections of the package's documentation. If you have multiple
items, separate them with commas rather than adding multiple
@see
tags.
To ease long term readability of PEAR source code, the text and tags must conform to the order and spacing provided in the example above. This standard is adopted from the JavaDoc standard.
There are two ways to implement the @package_version@ replacements.
The procedure depends on whether you write your own
package.xml
files or if you use the
PackageFileManager.
For those authoring package.xml files directly, add a <replace> element for each file. The XML for such would look something like this:
<file name="Class.php"> <replace from="@package_version@" to="version" type="package-info" /> </file>
Maintainers using the PackageFileManager need to call addReplacement() for each file:
<?php
$pkg->addReplacement(
'filename.php', 'package-info',
'@package_version@', 'version'
);
?>
Existing packages that have only a few files are required to adopt these docblocks before the next release.
Existing packages with many files are encouraged to adopt the new headers as soon as possible. When such packages come out with a new major version upgrade, these docblocks must be implemented therein.
New packages and existing packages which have no releases yet must include these docblocks before their first release.
2005-02-18
This RFC has been ratified in PEPr as proposal #192.
Sometimes a package is left without care for a long time. It becomes lonely and full of bugs. This is a very sad situation and somebody needs to step up to remedy it.
The following are considered orphaned packages:
A package with bugs open for longer than 2 months and the developers have not commented on the bug or made any commits to SVN during that time frame.
A package that has no open bugs and has bug fixes committed to SVN but does not have a release within 6 months of the bugs being fixed.
A package owned by somebody who is commonly known to be inactive.
A package denounced by its leads (i.e. The lead actually says that he is no longer maintaining it on pear-dev)
A QA core-team member tries to contact the current maintainers and developers (cc'ing the QA list) asking about an update about their work on the package.
A package lead answers::
He states that he has no interest in the package anymore, he allows QA team to take the necessary steps
He states that he still intends to work on the package, but is busy at that point. Either a timeframe will then be decided upon by the lead and QA during which some activity must occur (i.e. I'm going to be busy the rest of this month but I'll get back to it on the 3rd of next month) OR a second lead developer will be sought to continue development
If no answer reaches the list in two weeks QA will consider the package orphaned. If the lead doesn't have the time he should write a mail to the list stating he needs more time to sort things out.
A message will be sent to pear-dev asking for volunteers.
If you are willing to take over a package you must notify the QA list and state who you are and why you want to take over.
If the QA team feels the person who stepped up is appropriate and capable, the team will make this person a new maintainer. In case several persons step up a case by case study will be carried out to assign new maintainer-ship.
A QA core team member will mark the package as orphaned.
If a package is orphaned, a warning and a call for maintainers will be displayed on the main package page.
refers to the whole QA team (core and subscribed mailing list members)
refers to the 7 elected members (only 6 presently)
2005-01-26
This RFC has been ratified in PEPr as proposal #188.
It happens more or less often, that
PEPr proposals get stuck in a specific proposal phase,
PEPr proposals get accepted, but no package is registered / released.
In these 2 cases PEAR QA should step in and try to get in touch with the specific proposers. If the proposer is no more interessted in the proposal, PEAR QA should search for a new maintainer for the package or the proposal should be deleted.
For these steps the following time frames are proposed:
PEAR QA posts a reminder comment to the proposal after 1 week of inactivity.
If the proposer does not react within 2 weeks, the proposal gets deleted.
PEAR QA tries to contact the maintainer 4 weeks after the proposal is finished and no release has been throwen.
If the maintainer can not be contacted within 4 weeks, PEAR QA starts searching for a new maintainer.
If no new maintainer is found within 4 weeks, the proposal gets marked as orphan.
The above stated actions maybe automized inside PEARWeb in future, as far as possible.
Finished proposals should not be deleted, but marked as orphan. This marker should have the following content:
To the name of the proposal the following tag will be added: "[QA-ORPHAN]".
To the describtion of the proposal the following text will be added at the top: "This proposal has been marked orphan by PEAR-QA on <DATE> because of the inactivity of the proposer. This means, that the proposal is invalid from this date on. If you have a similar proposal or want to re-activate this proposal, feel free to create a new PEPr proposal for it.".
To get this process running, an initial cleanup is required. The procedure is only run once, when this proposal gets accepted. This cleanup should have the following steps:
PEAR QA will post a reminder comment to every proposal which shows no action since 4 weeks. If the proposer is not reachable within 1 week, the proposal is deleted.
PEAR QA will try to get in touch with the proposers of every proposal which is older than 4 weeks and for which no package has been registerd, yet. If the proposer is not reachable within 2 weeks, the proposal is marked as orphan.
This RFC has been ratified in PEPr as proposal #99.
This RFC aims to resolve whether or not protected class members will be prefixed with an underscore, or prefixed with nothing.
Once a decision has been reached, the result will be added to the PEAR coding standards.
Private members will still be prefixed.
These standards will only apply to PHP5 classes. In PHP4, if it's not public it's private and thus prefixed.
The voting resulted in a sum of +27 votes. Thus, the "no prefix" solution had been chosen.
A vote of 0 will not be counted.
If the overall score at the end of the call-for-votes is positive, class members will not be prefixed. If negative, they will be prefixed.
A vote of +1
suggests class members should not
be prefixed.
<?php
class Foo
{
protected $somevar;
protected function somefunc();
}
?>
A vote of -1
suggests class members should be
prefixed with an underscore.
<?php
class Foo
{
protected $_somevar;
protected function _somefunc();
}
?>
PHP5 Introduces PPP - Private, Public and Protected.
Protected members can be accessed in classes extending the class they are declared in, where as private members can only be accessed by the class they belong to.
In PHP4, both private and protected members were prefixed with an underscore. This was to ensure the user of the class knew what methods were available to them, and not to mess with anything else.
One of the key reasons for prefixed members is the visual expression of "Do not mess with this member, I may remove/rename it in the future". This is fine for private members, however will not follow for protected members when the class is extended by the user.
If a member is declared as protected (using the protected keyword), an E_FATAL error will be thrown if the member is used incorrectly.
PHP5 will also provide errors if a user attempts to redeclare a protected var as private. However protected members may be declared public by child classes.
In this regard 'protected' is like public in terms of the obligations of the library author. For example, if you want to keep your public API small, but allow extending classes to expose more functionality. Or, if you would prefer to access class properties directly rather than using setter methods, you can redeclare the properties public in a child class.
In these situations, public vars would be prefixed as "private".
Some would argue yes, some would argue no. Most large classes consist almost completely of protected members, with the public members reserved for the API. In this situation, does having almost everything underscored really enhance readability?
Is private the same as protected? No, definitly not. Then why should it be prefixed the same? Some argue this actually decreases readability and confuses private members.
Is protected the same as public? No, definitly not. The user can't use a protected member (unless they are extending the class). In this example, protected has a lot more in common private. Should the user be protected visually (as well as with the language construct) from using protected members?
There has been talked about forming QA team, but no action yet taken. The following is proposal of how the QA team will work, what rights they have and so on.
2004-05-04
This RFC has been ratified in PEPr as proposal #60.
There is no official QA team for PEAR. There are only a few people who try to help with QA issues but they don't have any official status or rights to resolve issues themselves if needed. PEAR claims quality but that can't be met if there is no real QA team. PEAR group has been talking about fine tuning karma for all users so the current "user has access to everything under /pear in cvs" karma policy will be eliminated and thus QA needs special karma when that happens. More importantly, there is currently no formal process or entity to handle orphaned packages or urgent fixes to packages.
To summarize the problem:
No QA team = can't claim Quality
Nobody has defined how the QA team is supposed to work, how it should be structured and what its rights (karma) are supposed to be.
If there are more applicants than seats, there should be an open election, where everyone may cast as many votes as there are seats (only 1 vote may be cast per candidate). Of course, if there are less people than seats, all may be accepted and the remaining seats can be filled by QA group vote. Once the core team is formed all further team members can be voted in as per the rules stated below.
There will be a QA core team with a limited number (no more than 7, with the pear group this should give a sufficient amount of people with enough karma that can react to bad releases) people with extended "core-QA-karma". In order for the QA core team to react quickly, the core QA team should be spread out across all time zones as good as possible. This rather small number should empower the QA team sufficiently to handle issues but doesn't trivialize access rights.
This issue needs more discussion since Klaus raised a good point in his post but the problem is that it isn't wise to give many users so much karma.
All other people interested are free to join the QA team. However potential new members for the QA team (core and non core) must be voted in by a 2/3 majority of the existing members of the QA team (core and non core). This is to prevent that the addition of new members is done even though there is a considerable amount of opposition, as this could lead to internal quarrels which could result in reducing the QA teams ability to efficiently address QA issues. Removing a member from either group requires a 2/3 majority as well.
For casual helpers the QA mailing list will of course remain open.
All members of the QA team (core and non core) are eligible to participate in QA team votes.
Any member that leaves the QA team loses all his special karma which he may have received due to his membership. If any member of the core QA team leaves and as a result a time zone gets under staffed, QA should actively try to find a replacement.
When voting for a new member voters must be sure that the person has been helping with bug fixes in the past or has the potentials to help with that.
All votes (except membership related votes; see above) require a simple 50% majority. Any member of the QA team may call for a vote at any time. All voting is done on the QA mailinglist.
The core team may overrule any decision (this includes votes on memberships) of the QA team and may decide to hold their own internal vote to make the ultimate decision on how to proceed. Furthermore, the core QA team may also hold votes without even consulting the QA team. However this should be only done in rare cases. All members of the core QA team may make decision on their own if a decision is time critical (like pulling a broken release). However the core QA team should be aware that making too many decisions on their own may lead to alienating the other members and therefore if possible it should always be attempted to at least consult other members of the team.
If any QA related issues are found in a package that QA has not been granted permission to change without consulting the maintainer,the QA team will file a bug report. If the issue remains unresolved for 1 month (2+1+1 weeks; see below), QA may fix it themselves.
If the issue isn't fixed with in two weeks QA will email the maintainers about the issue (if the bug system won't have auto bug report notices every X week in the future) and if the problem isn't fixed within one week from that, QA will send yet another email to the maintainers and if no answer nor fix to the problem is provided within one week, all members of the core QA team will get the permission to modify any file needed to fix that QA issue as well as make a new release.
If any major QA issues are found in any package and the QA team doesn't have permission to change that package without contacting the lead first then the QA team will file a bug report. Any major issues can stay open for 2 weeks (5+5+4 days; see below), QA may fix it themselves.
If the issue isn't fixed within 5 days QA will email the maintainers about the issue (if the bug system won't have auto bug report notices every X week in the future) and if the problem isn't fixed with in 5 days, from that QA will send yet another email to the maintainers and if no answer nor fix to the problem is provided with in 4 days, all members of the core QA team will have permission to modify any file needed to fix that QA issue as well as make a new release.
The same time frames apply when the QA team want to make a release because of unreleased fixes/improvements which are only available via SVN, or when the QA team wants to determine if a package has been orphaned (using the time frames specified for non major issues).
The QA team can decide what are major issues after discussion on the QA Mailing-list.
The QA team will for this reason keep track of those QA issues in their pear web subsection. This would only be for having overview over which package maintainers need to be emailed for reminders and such things.
The QA team can use the above stated rules to determine if a package is orphaned. The core QA team gets lead rights on all orphaned packages until the original maintainer returns or a new maintainer is found.
The core QA team also has the permission to delete any release that might have any issues that have been found after the release, to reduce the effects of the problem. This right must be exercised with caution.
Package maintainers can also grant QA the rights to make a releases for their packages.
It seems that there is a lack of QA material in the manual that people agree on and there is also need to make it move visible for people so they will actually follow those rules/guidelines. The QA team will therefore get its own QA subsection in the manual and will have control over it just like package maintainer controls package related docs in the manual.
The QA team also gets its own subsection on pearweb so people can more easily access QA material which is too dynamic or just related to the QA team own internal documentation purposes. There the QA will, for example, publish their latest decisions.
Furthermore here the QA team will be able to provide interfaces to QA team services. For example, here the QA team could offer an interface for maintainers to configure the access rights they want to grant the QA. Finally the core QA team can add QA related notes to each package homepage to communicate QA related information to the user base.
The QA team needs to approve the first stable release of any major version.
For this purpose maintainers upload the release to PEARweb and contact the QA team with a link to the release, then QA will decide if the release is ready or not. The core QA team will at this point approve the release through PEAR web and release it for the maintainer.
The QA team must always inform the current lead maintainers of any changes that are done to a given package using the currently configured email addresses on pear web.
The core QA team may delegate tasks as much as possible to other QA team members. However no access rights will be assigned just for the sake of delegating a task (so members of the QA team may package a new release, but only members of the core QA team will have the necessary rights to upload the release).
QA core group access rights:
Max 7 core members that get the core-QA-karma
The core QA team will get more permission to fix issues and make release of packages if reported problems are not fixed in a specific time frame
The core QA team assumes lead over orphaned packages
The core QA team gets the rights to delete releases of packages if it determines that the release has severe issues
The QA team is allowed to add QA related notes to package homepages
QA team needs to approve an first stable release for a given major version number (done by votes)
The entire QA team will identify QA problems, will help writing QA related documents, will help people resolve their QA problems and such things
More focus is needed on writing QA related things in the manual and make then more visible
Make a little QA corner at pear.php.net so QA is more visible for people and package devs are more aware of the QA team and how it works.
Keep track of what QA does regarding packages that QA doesn't have permission to edit through a wiki of some sort
Forming of the QA team, electing the persons who should for the core of the QA team.
Giving the core dudes full SVN karma on the pear SVN module, as well as full access rights to package management part of pear web
Make that little QA pear page
Improve the QA manual pages
Discovering which packages' lead(s) allow QA to help keeping their package QA approved
Better coordination of the work which is done, who is going to do what. Know in advance who is going to write some more entries to the manual and so on.
Make a wiki page of some sort for the info tracking regarding QA editing package that QA doesn't have permission to edit.
Due to recent events in the way that regulations have been proposed -the following is put for comment on the future method for introducing new standards, rules, conventions, recommendations or guidelines into the pear community.
2004-04-01
This RFC has been ratified in PEPr as proposal #39.
For PEAR to continue to grow, and encourage contributors, testers, writers and users. It is important (in the view of the RFC author) to make the decision process within PEAR to be as open and fair as possible.
Due to historical issues of long, rather pointless, and heated discussions on the pear-dev mailing list, Some rules have been made in the privacy of the pear-group mailing lists with little consultation with the community at large. This, (in the view of the author of this RFC) is something that is a serious mistake and must strongly be prevented in the future.
To summarize the problem
No formal procedure for proposing and approving RFCs has been available.
Public RFC's (or ideas) previously have ended up in 'flamewars' and pointless discussions.
Long nested threads can become difficult to summarize, when the aim is to produce a consensus building document.
This public battering has discouraged contributors to put forward ideas to improve PEAR, or even keep subscribing to pear-dev.
It is impossible to gauge the community support for rules that have been decided and enforced by the pear group.
Without clear wide ranging support, rules, guidelines will never represent the view of all users and contributors to PEAR.
(RFC = Request for comments) for those who are wondering..
All RFC's should be proposed using the PEPR system (and hence cc'd to pear-dev)
RFC's should be named by prefixed RFC_ to the Wiki style name of the RFC (eg. RFC_RulesOnRulesAndGuidlineProposals)
Anyone may comment on the RFC using the PEPR system (or by emailing the author directly)
NOBODY SHOULD RESPOND TO COMMENTS ON THE RFC
This includes the author themselves
Repeated abuse by the author may result in RFC being rejected
Repeated abuse by others may result in them being unsubscribed from pear-dev for 1 week.
If you have a need to repond to responses - please email the author and the original respondant (not the list). - a summary of this discussion should be noted in the RFC
The RFC Author should update the RFC based on the comments received to represent the opinions presented. Either by updating the solution, or explaining differing opinion in the Comments section.
RFC's should take the form of
Title Block Containing
Title
Author (email simply encoded)
Revision
Status (Active|Final|Rejected|Replaced)
Replaces : Name of original RFC (eg. RfcProposals1)
Introduction (Short ~1-2 paragraphs)
The Issues (Detail discussion of need for RFC)
The Proposed Solution
Actions required if accepted.
After it has been proposed these section should be added
Comments - a summary of the opinion made about the RFC (if they are not represent in the updated RFC) Along with why the author has not included them in the Solution
Change log - a summary of the changes made to each revision
RFC's may under go any number of revisions, and put to vote (normally after a new revision of the RFC has been issued via PEPr, and no comments where added.)
Initial drafts of RFCs may be developed in private or in small groups. Once the RFC reaches a point nearing maturity, it should be made public (on the pear-dev mailing list) for comment.
All RFC's will be licenced under "Open Publication License" http://www.opencontent.org/openpub/
RFC's should not concern themselves with specific packages, or the addition of specific features. This should be done by discussions directly with package maintainers, or proposing competing packages.
PEAR group was set up to oversee PEAR, it's continued existance relies on support from the community, It is intended that the group is to have the power of veto over all proposals (however it well understands that continually doing this would seriously undermine its own authority, and veto should only occur in extremely serious situations.)
It is strongly recommended that contentious issues are broken out into separate RFCs, so they can be documented, discussed and voted on separatly.
Changes to the PEPr proposal system:
While the current system could be used it would be appreciated if the author could add the following features
RFC Category
No requirement for tgz/etc for RFC 'packages'
Wishlist for PEPr (not essential to use it however).
BIG warning messages on the bottom of package comments should say 'DO NOT EVER RESPOND TO THIS COMMENT - the RFC author will update the RFC to reflect these opinions' o reply to address on comments messages should be do_not_reply@localhost
Comments visible on 'a' Summary page, with tags saying "has been adressed by updating the RFC", "won't fix"
A licence link on the PEPr system indicating that all RFCs are published under the "Open Publication License"
Greg Beaver: main comment added, "Poorly thought-out RFCs should not be made public." - was not added as it was considered obvious... and a little difficult to define..
Jon Praise: mentioned Pythons PEPs (Python Enhancement Proposals) http://www.python.org/peps/pep-0001.html (some modifications made based on this document)
Lukas Smith: mentions that any opinions not incorporated should detail the authors reasoning for not including them. (added)
IRC discussions:
RFCs would be created on pedantic issues - like adding feature X to a package (see rule on RFC issues)
how should Pear-group be involved in a proposal / approval.. what if in a whim of chaos it decided to add support for GPL packages without understanding the concequences (see pear-group veto)
Stefan Neufeind: Would like to see notes auto-attached to PEPr like : modified Action list to include wishlist.
Toby : Called for volunteers to help out implement Wishlist (see mailing list for details)
Richard York: Asked for automated tracking of responses to Comments - so they get appended to PEPr. (while nice, I'm not sure this is directly related to the RFC, and really depends on someone volunteering to do it.)
Ian Eure, Lukas Smith: Commented on the varying views about Comment on comments. (While the rule is not intended to restrict open discussion, it is there to focus comments on the issue at hand, helping the RFC author gather views for the document.) - The document has been updated a bit to clarify this issue.
Lukas Smith: copyright note - have added that to the wishlist for PEPr. - It's a bit silly to add it to each document if we can do it via PEPr.
This RFC has been ratified in PEPr as proposal #65.
As discussed previously on pear-group, where a full consensus could not be reached on the version naming standard, below is the proposed replacement RFC, which attempts to address the issues.
Pear-group announced a versioning naming standard which unfortunatly failed to explain the reasons behind the decisions made. The aim of the previous standard was
Formalize the version naming of packages.
Fix problems with version_compare going from
1.0.0RC1
to 1.0.1
To introduce a rule stating 'no-stable releases before
1.0.0
'
Solve release naming for My_Package2
(second
releases)
It also attemtped to introduce a number of more controversial ideas
Make the package status more visible.
The last one did not result in a complete consensus, and patches have now been made to the packager and installer to add state to the filename, output when installing a package.
The solution is to replace the existing standard with one a simpler and slightly clearer one.
Version | Description |
---|---|
0.1.0 ...
0.1335.0 |
initial pre-stable releases (bug fix releases increment
.z eg. 0.12.1 ) |
1.0.0 |
first stable release |
1.1.0 |
first feature upgrade |
1.1.1 |
bug fixes on feature upgrade |
The next major version is MyPackage2
:
Version | Description |
---|---|
0.1.0 ...
0.1335.0 |
initial pre-stable releases (bug fix releases increment
.z eg. 0.12.1 ) |
2.0.0 |
first stable release |
2.1.0 |
first feature upgrade |
2.1.1 |
bug fixes on feature upgrade |
Breaking BC may only be done:
within the 0.*.*
series
moving from Major Package Versions (eg. to
My_Package2
)
should not be marked as stable
should use 0.1.0
or greater (as you may
have already started revision controlling while proposing it)
in pre stable - you can either increase y
(in x.y
) or z
(in
x.y.z
), for example
0.2.0
->
0.3.0
0.2.0
->
0.2.1
after a stable release
1.3.0
->
1.3.1
2.4.0
->
2.4.1
Should just increment the y
(in
x.y
), for examples
0.3.0
-> 0.4.0
1.3.0
-> 1.4.0
2.10.0
->
2.11.0
You should never release stable packages with a
0.x
format (this is a common situation at present,
and is the only significant change that is being proposed.)
RC releases are strongly recommended for larger, popular packages.
If you intend to append RC releases to your package, you should append a release number to after the RC, for example:
0.123.0
->
1.0.0RC1
1.0.0RC1
->
1.0.0RC2
1.0.0RC2
->
1.0.0
1.0.0
-> 1.0.1
At present this is left to the maintainer to define (using common sense), the exact definition should be defined later by future RFCs.
2009-06-25
What is a PEAR Channel? PEAR Channels make it possible to take advantage of the strengths of the PEAR Installer and Pyrus for your own personal packages. A channel is simply a website that provides packages for download and a few extra meta-information files.
A familiar example of a channel is
pear.php.net.
This channel was the first channel, and defines the standards to which other
channels must adhere. Each channel has a channel.xml
file in its document root (such as
http://pear.php.net/channel.xml) and a series of files utilizing the
REST
(Representational State Transfer) paradigm to describe
the packages available for installation and download. The pear.php.net
channel's REST
files can be viewed at
http://pear.php.net/rest.
Discovery of a channel's capabilities is extremely flexible. The XSD schema defining channel.xml can be found at http://pear.php.net/dtd/channel-1.0.xsd. Channel.xml defines:
the channel name
an optional suggested user alias for the channel
a brief summary of the channel's purpose
an optional package to perform custom validation of packages on both download and packaging
a list of protocols supported by a channel (XML-RPC, SOAP, and REST are supported)
a list of mirrors and the protocols they support.
Here is a sample channel.xml with all elements:
<channel version="1.0" xsi:schemaLocation="http://pear.php.net/channel-1.0 http://pear.php.net/dtd/channel-1.0.xsd"><name>
pear.example.com</name><suggestedalias>
foo</suggestedalias><summary>
Example channel.xml</summary><validatepackage
version="1.3.4">Foo_Validate</validatepackage><servers> <primary
port="8080" ssl="yes"><xmlrpc>
<!-- default path is xmlrpc.php --> <function version="1.0">logintest</function> <function version="1.0">package.listLatestReleases</function> <function version="1.0">package.listAll</function> <function version="1.0">package.info</function> <function version="1.0">package.getDownloadURL</function> <function version="1.1">package.getDownloadURL</function> <function version="1.0">package.getDepDownloadURL</function> <function version="1.1">package.getDepDownloadURL</function> <function version="1.0">package.search</function> <function version="1.0">channel.listAll</function> </xmlrpc><rest>
<!-- no default path, all must be defined in baseurl --> <baseurl type="REST1.0">http://pear.example.com/rest/</baseurl> <baseurl type="REST1.1">http://pear.example.com/rest/</baseurl> </rest><soap
path="soapy.php"> <!-- default path is soap.php --> <function version="1.0">package.listAll</function> </soap> </primary><mirror
server="foo2.example.com/pearmirror"> <xmlrpc path="mirrorxmlrpc.php"> <!-- default path is xmlrpc.php --> <function version="1.0">package.listLatestReleases</function> <function version="1.0">package.listAll</function> <function version="1.0">package.info</function> <function version="1.0">package.getDownloadURL</function> <function version="1.1">package.getDownloadURL</function> <function version="1.0">package.getDepDownloadURL</function> <function version="1.1">package.getDepDownloadURL</function> <function version="1.0">package.search</function> </xmlrpc> <rest> <!-- no default path, all must be defined in baseurl --> <baseurl type="REST1.0">http://foo2.example.com/rest/</baseurl> </rest> </mirror> </servers> </channel>
A channel's name should be the name of the server that users would browse to in order to learn more about the packages being offered. For instance, PEAR packages are located in the pear.php.net channel. PECL packages are located in the pecl.php.net channel. Note that for backwards compatibility, all existing packages based on package.xml version 1.0 are in the pear.php.net channel.
The benefit that comes from using the server name as the channel name is that auto-discovery becomes a real possibility, as well as ease of locating packages increases dramatically.
A channel need not be located in the document root, a channel can contain a path. This
is a perfectly valid channel name:
foo.example.com/path/to/pear
.
Note that users would have to type:
$ pear install foo.example.com/path/to/pear/Packagename
Unless you provide a <suggestedalias>.
The channel's definition file "channel.xml" must be placed in the root channel directory. If a channel is "pear.example.com", the channel.xml must be located in "http://pear.example.com/channel.xml". If the channel is "pear.example.com/path/to/pear", then the channel.xml must be located in "http://pear.example.com/path/to/pear/channel.xml"
<suggestedalias> defines a shorter, more friendly name to use when installing packages from a channel. For instance, the pear.php.net channel's suggested alias is "pear". The best aliases for a channel will be no more than 6 characters long - remember, a user must type them often when installing or upgrading, and this can be tedious for longer aliases.
Rather than call this tag <alias>, as it was originally named, the tag is named <suggestedalias> in order to provide the user some latitude. If the user does not like the alias suggested by the channel owners, he or she can easily re-alias a channel through the channel-alias command.
This tag provides a short description of what packages the user should expect to find on this channel. The summary is what users will see when the use the list-channels command.
Most channels will be satisfied with the restrictions placed upon package naming, versioning, and so on that PEAR provides by default. However, for some channels, the validation will be too strict, and others, too relaxed. The <validatepackage> tag provides the next level of customization.
If omitted, the installer assumed that the PEAR_Validate class should be used. Note that a looser version validation is provided by the PEAR_Validate_PECL class, for channels like pecl.php.net that do not wish to deal with PEAR's warnings on version transgressions.
<validatepackage> requires a version attribute and text content. The text content must be the name of a package that can be installed via:
$ pear install channelname.example.com/Packagename-version
as in:
$ pear install pear.example.com/Foo_Validate-1.3.4
for the sample channel.xml at the beginning of this section. In addition, the package must provide a single class named after the package in a file using the PEAR naming conventions (all underscores "_" converted into path separators "/" so that Foo_Validate is located in Foo/Validate.php), and this class should extend PEAR_Validate. Methods beginning with "validate" like validateVersion() are intended to be overridden by validation classes for use in extending existing validation functionality.
Mirroring is explicitly supported in channel.xml and in the PEAR installer. Users can
choose their favorite mirror via the default_channel
configuration
option, and channel.xml can list all the possible mirrors using the (surprise)
<mirror> tag.
The <primary> tag is used to define the location of protocols, and to list the protocols that are supported by the main channel server. Optional attributes can be used to modify how the PEAR installer will attempt to connect to the server. The "port" attribute can be used to define how the installer will connect to XML-RPC and SOAP services. REST services are always controlled by the individual <baseurl> tags.
channel.xml knows about the XML-RPC, SOAP, and REST protocols for web services. However, the PEAR installer only supports REST currently, and may support other methods in the future. No support for SOAP is planned for the near future. However, should it ever be implemented, channel.xml is ready.
The <xmlrpc> and <soap> tags have identical formats. Both tags can contain
an optional attribute "path" that tells the PEAR installer which URL to
query. By default, the path is protocol.php, as in xmlrpc.php or soap.php. In other
words, to access XML-RPC functions for the pear.example.com channel defined in the sample
channel.xml, the installer would query
https://pear.example.com:8080/xmlrpc.php
for XML-RPC functions, but
would query https://pear.example.com:8080/soapy.php
for SOAP
functions.
The <rest> tag reflects the design concept behind REST: each resource is defined by a base URL in tag <baseurl> that is then used by the installer along with hyperlinks to glean the same information that XML-RPC or SOAP would provide. Required attribute "type" tells the installer what version of the PEAR installer REST interface is provided at the base URL.
The <function> tag is quite simple. A required version attribute informs the installer what the API is, and the text content informs the installer what the name of the function is. Note that multiple functions with different versions can co-exist peacefully, as in:
<function version="1.0">package.getDownloadURL</function> <function version="1.1">package.getDownloadURL</function>
If a newer API is backwards-compatible, always define every possible API version in order to prevent older installer versions from giving up.
Some of you may be asking "why create another standard for web services discovery?" The answer is simple: channel.xml does not supplant the role that WSDL has for java, or XML-RPC introspection occupies. channnel.xml is a layer on top of these technologies. The point is to quickly list the remote protocols that are supported, not to describe what they do.
The PEAR installer is specialized enough that a generic listing of parameters and return values is entirely unnecessary: the installer knows exactly what xml-rpc function package.info version 1.0 requires and what it returns. Any other information simply adds wasted bandwidth and disk space.
This manual section describes how the REST interface of a PEAR channel server works, as well as the files and their formats look like.
A REST channel server simply delivers files in a certain directory
structure. The content of those files is static. Their location is relative
to the URLs given in the channel.xml
baseurl
tags.
All files are static - you do not need a scripting language installed on your server.
The following table lists all known files, a tiny description as well as the version they appeared in first.
Level 0 | Level 1 | Level 2 | Description | REST version |
---|---|---|---|---|
c/ |
Categories | 1.0 | ||
categories.xml |
List of all categories | 1.1 | ||
$CategoryName/ |
1.0 | |||
info.xml |
Info about the category | 1.0 | ||
packages.xml |
List of packages in category | 1.0 | ||
packagesinfo.xml |
Info about all packages | 1.1 | ||
m/ |
Maintainers | 1.0 | ||
allmaintainers.xml |
List of all maintainers | 1.1 | ||
$maintainernick/ |
1.0 | |||
info.xml |
Info about the maintainer | 1.0 | ||
p/ |
Packages | 1.0 | ||
packages.xml |
List of all packages | 1.0 | ||
$packagename/ |
1.0 | |||
info.xml |
General package information | 1.0 | ||
maintainers.xml |
List of package maintainers | 1.0 | ||
maintainers2.xml |
List of developers and their roles | 1.2 | ||
r/ |
Releases | 1.0 | ||
$packagename/ |
1.0 | |||
allreleases.xml |
List of all releases | 1.0 | ||
allreleases2.xml |
List of all releases including minimum PHP version | 1.3 | ||
latest.txt |
Latest version number | 1.0 | ||
stable.txt |
Latest stable version number | 1.0 | ||
beta.txt |
Latest beta version number | 1.0 | ||
alpha.txt |
Latest alpha version number | 1.0 | ||
devel.txt |
Latest development version number | 1.0 | ||
0.1.2.xml |
Short package info for version 0.1.2 | 1.0 | ||
v2.0.1.2.xml |
Short version of package.xml, version 2 | 1.3 | ||
package.0.1.2.xml |
package.xml for version 0.1.2 | 1.0 | ||
deps.0.1.2.txt |
Serialized dependencies for version 0.1.2 | 1.0 | ||
Here you will find detailled description of the file formats used for the REST interface.
In file names that contain version numbers, we use
0.1.2
as example.
In general, the files try to be as small as possible so that only little
bandwidth is required to fetch them. That's why all of the xml files
(except the original package.xml
have only tag names
of one or two characters.
Remember that operations like pear list-all download a
large number of files, so every saved bit helps.
This is the main file for a channel; nothing works without it. When discovering a channel, this file is retrieved. It defines the REST directory locations as well as mirrors.
The channel <name>
is a full qualified domain
name and is used as part of the URL when e.g. updating the
channel.xml
file.
PEAR provides aliases for channels as shortcuts in the daily work of your
user's lifes. The <suggestedalias>
should be
a short and easy to write word. Benefit is that, instead of
pear install pear.mynicelittlespaceon.example.org/package
they just can type
pear install nice/package
if the alias was nice
.
/channel.xml
It needs to be in the root directory of the domain.
While all other files can be located somewhere deep in a directory structure,
channel.xml
needs to be in /
.
<?xml version="1.0" encoding="utf-8"?> <channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0 http://pear.php.net/dtd/channel-1.0.xsd" > <name>pear.example.org</name><!-- URL, used to update channel.xml and such --> <suggestedalias>example</suggestedalias> <summary>Simple demo channel server</summary> <servers> <primary> <!-- you can ignore xmlrpc, it's deprecated anyway --> <xmlrpc> <function version="1.0">logintest</function> <function version="1.0">package.listLatestReleases</function> <function version="1.0">package.listAll</function> <function version="1.0">package.info</function> <function version="1.0">package.getDownloadURL</function> <function version="1.1">package.getDownloadURL</function> <function version="1.0">package.getDepDownloadURL</function> <function version="1.1">package.getDepDownloadURL</function> <function version="1.0">package.search</function> <function version="1.0">channel.listAll</function> </xmlrpc> <rest> <baseurl type="REST1.0">http://pear.example.org/rest/</baseurl> <baseurl type="REST1.1">http://pear.example.org/rest/</baseurl> <baseurl type="REST1.2">http://pear.example.org/rest/</baseurl> <baseurl type="REST1.3">http://pear.example.org/rest/</baseurl> </rest> </primary> <mirror host="us.pear.example.org"> <rest> <baseurl type="REST1.0">http://us.pear.example.org/rest/</baseurl> <baseurl type="REST1.1">http://us.pear.example.org/rest/</baseurl> <baseurl type="REST1.2">http://us.pear.example.org/rest/</baseurl> <baseurl type="REST1.3">http://us.pear.example.org/rest/</baseurl> </rest> </mirror> <mirror host="de.pear.example.org" ssl="yes" port="3452"> <rest> <baseurl type="REST1.0">https://de.pear.example.org:3452/rest/</baseurl> <baseurl type="REST1.1">https://de.pear.example.org:3452/rest/</baseurl> <baseurl type="REST1.2">https://de.pear.example.org:3452/rest/</baseurl> <baseurl type="REST1.3">https://de.pear.example.org:3452/rest/</baseurl> </rest> </mirror> </servers> </channel>
Provides a names and links to for all categories known on the server. Links are URL-encoded.
Unlike all other files, the channel name is wrapped in a
<ch>
instead of a plain<c>
tag.
c/categories.xml
<?xml version="1.0" encoding="utf-8" ?> <a xmlns="http://pear.php.net/dtd/rest.allcategories" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.allcategories http://pear.php.net/dtd/rest.allcategories.xsd" > <ch>pear.example.org</ch> <c xlink:href="/rest/c/Tools/info.xml">Tools</c> <c xlink:href="/rest/c/Garbage%2Band%2BStuff/info.xml">Garbage and Stuff</c> </a>
Here, the category is explained in detail. The file lists
the name (<n>
),
channel server (<c>
),
alias (<a>
)
and a longer description of the category (<d>
).
c/${categoryname}/info.xml
Category names may contain spaces and other special characters, so (x)links need to be url-encoded.
<?xml version="1.0" encoding="utf-8" ?> <c xmlns="http://pear.php.net/dtd/rest.category" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.category http://pear.php.net/dtd/rest.category.xsd" > <n>Tools</n> <c>pear.example.org</c> <a>Tools and Utilities</a> <d>This category holds all sorts of packages that might help you when trying to dominate the world.</d> </c>
The file simply contains a list of names and links to each package in the category.
c/${categoryname}/packages.xml
Category names may contain spaces and other special characters, so (x)links need to be url-encoded.
<?xml version="1.0" encoding="utf-8" ?> <l xmlns="http://pear.php.net/dtd/rest.categorypackages" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.categorypackages http://pear.php.net/dtd/rest.categorypackages.xsd" > <p xlink:href="/rest/p/earth">Earth</p> <p xlink:href="/rest/p/worlddominator">WorldDominator</p> </l>
packagesinfo.xml
is a collection of information about
packages in the category. It contains the contents of the package's
info.xml
,
release information from
allreleases.xml
and dependency information for each version.
Every package information piece is wrapped in a <pi>
tag.
c/${categoryname}/packagesinfo.xml
Category names may contain spaces and other special characters, so (x)links need to be url-encoded.
Provides "summary" information in the list-all commmand.
<?xml version="1.0" encoding="utf-8" ?> <f xmlns="http://pear.php.net/dtd/rest.categorypackageinfo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.categorypackageinfo http://pear.php.net/dtd/rest.categorypackageinfo.xsd" > <pi> <p> <n>WorldDominator</n> <c>pear.example.org</c> <!-- Full contents of p/${packagename}/info.xml follow --> </p> <a> <r><v>1.1.2</v><s>stable</s></r> <r><v>0.1.2</v><s>beta</s></r> <r><v>0.0.1</v><s>devel</s></r> </a> <deps> <v>0.1.2</v> <d><!-- serialized dependency information like deps.0.1.2.txt --></d> </deps> <deps> <v>0.0.1</v> <d><!-- serialized dependency information like deps.0.1.2.txt --></d> </deps> </pi> </f>
Simply lists names and links to all developers of any package on the server.
FIXME: full names or just nicks? FIXME: lowercased nicks?
m/allmaintainers.xml
<?xml version="1.0" encoding="utf-8" ?> <m xmlns="http://pear.php.net/dtd/rest.allmaintainers" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.allmaintainers http://pear.php.net/dtd/rest.allmaintainers.xsd" > <h xlink:href="/rest/m/pinky">pinky</h> <h xlink:href="/rest/m/thebrain">the brain</h> </m>
Contains maintainer information like handle
(nickname, <h>
),
full name (<n>
)
and URL (<u>
) to the developer's homepage.
m/${maintainernick}/info.xml
FIXME: lowercasednick?
<?xml version="1.0" encoding="utf-8" ?> <m xmlns="http://pear.php.net/dtd/rest.maintainer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.maintainer http://pear.php.net/dtd/rest.maintainer.xsd" > <h>thebrain</h> <n>The Brain</n> <u>http://pinkyandthebrain.example.org</u> </m>
This file lists all packages in this channel, together with the channel server name itself.
Package names should not contain any spaces; the behavior of the installer in such cases is undefined.
p/packages.xml
The package name is lowercased.
<?xml version="1.0" encoding="utf-8" ?> <a xmlns="http://pear.php.net/dtd/rest.allpackages" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.allpackages http://pear.php.net/dtd/rest.allpackages.xsd" > <c>pear.example.org</c> <p>Earth</p> <p>WorldDominator</p> </a>
This file contains general version-independent information about the package: License, category, summary, description and a link to the release directory.
r/${packagename}/info.xml
The package name is lowercased.
remote-info fetches this file and displays its information.
<?xml version="1.0" encoding="utf-8" ?> <p xmlns="http://pear.php.net/dtd/rest.package" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.package http://pear.php.net/dtd/rest.package.xsd" > <n>WorldDomination</n> <c>pear.example.org</c> <ca xlink:href="/rest/c/Tools">Tools</ca> <l>Dictatoric License</l> <s>Tool to dominate the world</s> <d> Helps you dominating the world by fulfilling various tasks: - Feed the cats - Lock the doors after 23:42 </d> <r xlink:href="/rest/r/worlddomination"/> </p>
All package developers are listed in this file, regardless if active or inactive.
Each maintainer's handle (<h>
) and
activity state (<a>
, 0
for inactive, 1
for active)
is provided.
r/${packagename}/maintainers.xml
The package name is lowercased.
<?xml version="1.0" encoding="utf-8" ?> <m xmlns="http://pear.php.net/dtd/rest.packagemaintainers" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.packagemaintainers http://pear.php.net/dtd/rest.packagemaintainers.xsd" > <p>WorldDominator</p> <c>pear.example.org</c> <m> <h>pinky</h> <a>1</a> </m> <m> <h>thebrain</h> <a>1</a> </m> <m> <h>deadcow</h> <a>0</a> </m> </m>
Same as
maintainers.xml
,
except that the developer's role is written down, too.
Valid
role names
are lead
, developer
,
contributor
and helper
.
r/${packagename}/maintainers2.xml
The package name is lowercased.
<?xml version="1.0" encoding="utf-8" ?> <m xmlns="http://pear.php.net/dtd/rest.packagemaintainers" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.packagemaintainers http://pear.php.net/dtd/rest.packagemaintainers.xsd" > <p>WorldDominator</p> <c>pear.example.org</c> <m> <h>pinky</h> <a>1</a> <r>developer</r> </m> <m> <h>thebrain</h> <a>1</a> <r>lead</r> </m> <m> <h>deadcow</h> <a>0</a> <r>helper</r> </m> </m>
This file lists all known versions of a package, together with its stability.
Releases in this file are ordered, the latest version has to be first.
r/${packagename}/allreleases.xml
The package name is lowercased.
<?xml version="1.0" encoding="utf-8" ?> <a xmlns="http://pear.php.net/dtd/rest.allreleases" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.allreleases http://pear.php.net/dtd/rest.allreleases.xsd" > <p>WorldDominator</p> <c>pear.example.org</c> <r><v>0.8.1</v><s>beta</s></r> <r><v>0.0.2</v><s>alpha</s></r> </a>
Same as allreleases.xml
, but with information
about the minimum version of PHP required.
r/${packagename}/allreleases2.xml
The package name is lowercased.
<?xml version="1.0" encoding="utf-8" ?> <a xmlns="http://pear.php.net/dtd/rest.allreleases2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.allreleases2 http://pear.php.net/dtd/rest.allreleases2.xsd" > <p>WorldDominator</p> <c>pear.example.org</c> <r><v>0.8.1</v><s>beta</s><m>4.4.2</m></r> <r><v>0.0.2</v><s>alpha</s><m>5.2.3</m></r> </a>
The only content of this file is the version number of the latest version in pure plain text. The stability state does not matter; the highest version number is written down here.
r/${packagename}/latest.txt
The package name is lowercased.
This file does not exist when no release has been made yet.
A package has a stable version 1.0.0
, two beta versions
0.9.8
and 1.0.9
and a development
version 1.0.1
. The highest version number is
1.0.9
, and this is put in latest.txt
.
1.0.9
The only content of this file is the version number of the latest stable version in pure plain text.
r/${packagename}/stable.txt
The package name is lowercased.
This file does not exist when no stable release exists.
0.1.2
The only content of this file is the version number of the latest beta version in pure plain text.
r/${packagename}/beta.txt
The package name is lowercased.
This file does not exist when the package has no beta version.
0.1.2
The only content of this file is the version number of the latest alpha version in pure plain text.
r/${packagename}/alpha.txt
The package name is lowercased.
This file does not exist when no alpha release exists.
0.1.2
The only content of this file is the version number of the latest development version in pure plain text.
r/${packagename}/devel.txt
The package name is lowercased.
This file does not exist when no development release exists.
0.1.2
This file is a special size-optimized version of
the full package.xml
with only necessary information.
As in package.xml
, the tag order is important
and may not be shuffled.
r/${packagename}/0.1.2.xml
The package name is lowercased.
<?xml version="1.0" encoding="utf-8" ?> <r xmlns="http://pear.php.net/dtd/rest.release" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.release http://pear.php.net/dtd/rest.release.xsd" > <p xlink:href="/rest/p/worlddominator">WorldDominator</p> <c>pear.example.org</c> <v>0.1.2</v> <st>beta</st> <l>Dictatoric License</l> <m>thebrain</m> <s>Tool to dominate the world</s> <d>Helps you dominating the world by fulfilling various tasks: - Feed the cats - Lock the doors after 23:42</d> <da>2007-12-24 23:42:00</da> <n>* Fix atomic X-mas bug [thebrain]</n> <f>19588</f> <g>http://pear.example/get/WorldDominator-0.1.2</g> <x xlink:href="package.0.1.2.xml"/> </r>
Tag name | Description |
---|---|
<p> |
Package name inclusive absolute path to the package directory |
<c> |
Channel server name |
<v> |
Release version |
<st> |
Stability state
(e.g. stable , beta etc.)
|
<l> |
License name |
<m> |
Handle/nickname of the releasing developer |
<s> |
Summary |
<d> |
Description, multiline |
<da> |
Date and time of release |
<n> |
Release notes |
<f> |
File size of the tgz in bytes |
<g> |
Full URL to the release archive |
<x> |
Link to the version's
package.xml
file.
|
Same as
0.1.2.xml
,
but with additional API and minimum PHP version.
r/${packagename}/v2.0.1.2.xml
The package name is lowercased.
<?xml version="1.0" encoding="utf-8" ?> <r xmlns="http://pear.php.net/dtd/rest.release2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://pear.php.net/dtd/rest.release2 http://pear.php.net/dtd/rest.release2.xsd" > <p xlink:href="/rest/p/worlddominator">WorldDominator</p> <c>pear.example.org</c> <v>0.1.2</v> <a>0.1.2</a> <mp>5.2.3</mp> <st>beta</st> <l>Dictatoric License</l> <m>thebrain</m> <s>Tool to dominate the world</s> <d>Helps you dominating the world by fulfilling various tasks: - Feed the cats - Lock the doors after 23:42</d> <da>2007-12-24 23:42:00</da> <n>* Fix atomic X-mas bug [thebrain]</n> <f>19588</f> <g>http://pear.example/get/WorldDominator-0.1.2</g> <x xlink:href="package.0.1.2.xml"/> </r>
New tags compared to 0.1.2.xml
.
Tag name | Description |
---|---|
<a> |
API version |
<mp> |
Minimum PHP version |
Full package.xml
for the release. May be version 1
or version 2 of the package.xml
format.
The highest version should be made available if the package contains
both.
r/${packagename}/package.0.1.2.xml
The package name is lowercased.
The file contains an array of dependency information, serialized with PHP's serialize() function.
r/${packagename}/deps.0.1.2.txt
The package name is lowercased.
array(2) { ["required"]=> array(2) { ["php"]=> array(1) { ["min"]=> string(5) "5.2.3" } ["pearinstaller"]=> array(1) { ["min"]=> string(7) "1.7.1" } } ["optional"]=> array(1) { ["package"]=> array(2) { ["name"]=> string(4) "Toolbox" ["channel"]=> string(12) "pear.example.org" ["min"] => string(7) "1.3.0" } } }
<dependencies> <required> <php> <min>5.2.3</min> </php> <pearinstaller> <min>1.7.1</min> </pearinstaller> </required> <optional> <package> <name>Toolbox</name> <channel>pear.example.org</channel> <min>1.3.0</min> </package> </optional> </dependencies>
This page lists the known REST versions and their changes they introduced.
This was the initial and first supported version of the REST interface.
This version added files to allow spidering channels without requiring the server to have directory listings enabled. These files are:
Now the developer's roles are listed in the package's maintainer list.
This version makes it possible to resolve PHP version incompatibilities
by downloading the releases list only.
allreleases2.xml
lists the minimum PHP version
for each package release, thus the package name does not need to be
changed when upgrading the package's minimum PHP version.
This section shows common actions on a channel server and what to do then.
The idea and initial cases have been taken from Jean-Lou Dupont's "PEAR Channel on Google Code" presentation.
When creating a fresh category on your channel server, the following steps are necessary:
Create c/${CategoryName}
directory.
Create the following files:
Update
c/categories.xml
.
A new package shall be published on the server, and here are the steps you need to do:
Check if the package category already exists, or a new category needs to be created.
Create the package directory p/${packagename}/
.
Create those files:
Update the category files:
The time has come to release a new version of an existing package. Those are the steps to do:
Publish the release's package.xml
as
r/${packagename}/package.0.1.2.xml
.
Create the version specific
r/${packagename}/0.1.2.xml
.
Update state files if appropriate:
r/${packagename}/latest.txt
,
stable.txt
,
beta.txt
,
alpha.txt
,
devel.txt
.
Generate
r/${packagename}/deps.0.1.2.xml
.
Update
r/${packagename}/allreleases.xml
and
r/${packagename}/allreleases2.xml
.
Place package archives (tar
and tgz
)
at the place specified in
r/${packagename}/0.1.2.xml
.
While you can use this documentation to build you own channel server, it is possibly easier for you to either get your package into PEAR itself, or setup your own channel server. The reference implementation of a PEAR channel server is the PEAR website itself, but it should not be used for your own server.
If you seek for an easy-to-setup software, try Chiara_PEAR_Server which resides on its own channel http://pear.chiaraquartet.net.
Another project is SimpleChannelServer, which is available as a plugin for Pyrus, or a standalone phar pearscs.phar. View the online documentation, or the source in SVN.
A standard PEAR channel should support this list of XML-RPC functions:
logintest - a stub function
package.getDownloadURL - retrieve a URL to download a package
package.getDepDownloadURL - retrieve a URL to download a package dependency
package.info - retrieve information on a package
package.listAll - list all packages and verbose information on each package
package.listLatestReleases - list all packages and their latest release versions
package.search - search all packages for a match
Channels can also implement channel.listAll, but we recommend that this only be implemented by pear.php.net and pecl.php.net channels, as the command is utilized by the update-channels command to retrieve an official list of channels.
The logintest xml-rpc function is called by the login command, and should return a boolean TRUE
false|struct package.getDownloadURL (
struct packageinfo
, string preferred_state = stable
, (v1.1) string installed_version = false
)
packageinfo
an array of format:
array(
'channel' => channel name,
'package' => package name,
['version' => specific version to retrieve,]
['state' => specific state to retrieve,]
)
if both version and state are set, the version index should be ignored.
preferred_state = stable
The client-side preferred_state. This should be used to exclude releases that are too unstable.
installed_version = false
The current installed version of the package on the client-side. This will either be a version string, or false if the package is not installed. Use this to ensure that older versions are never returned (as defined by version_compare(possible_version, installed_version, "<")).
The package.getDownloadURL function should return an array with either two or three indices.
"version" => version of the release returned
"info" => the complete package.xml contents from the release
"url" => a URL from which to download this release. If no releases exist that fit the constraints defined by preferred_state, installed_version, and the version/state indices of packageinfo, then do not return this index, and instead return the version and package.xml of the latest release.
The url entry should NOT append .tgz or .tar, but should be something like "http://pear.php.net/get/PEAR-1.4.0" instead of "http://pear.php.net/get/PEAR-1.4.0.tgz"
Note that version 1.0 of package.getDownloadURL did not have the installed_version parameter. Version 1.1 of package.getDownloadURL does - that is the only difference between the two versions.
false|struct package.getDepDownloadURL (
string xsdversion
, struct dependency
, struct parentpackage
, string preferred_state = stable
, (v1.1) string installed_version = false
)
xsdversion
This should be either '1.0' or '2.0', and should match the version attribute from the top-level <package version="X.0"> tag. This should be used to determine how to process the second parameter.
dependency
if the first parameter xsdversion is '1.0', this should be an array of format:
array(
'name' => package name
'type' => 'pkg' - anything else is an error
'rel' => 'has', 'ge', 'le', 'lt', 'le', 'not', 'ne'
['version' => specific version to retrieve,]
)
if xsdversion is '2.0', this should be an array of format:
array(
'name' => package name
'channel' => package channel - see notes below
['min' => minimum version (inclusive),]
['max' => maximum version (inclusive),]
['exclude' => single version to exclude (string),
or array of versions to exclude,]
)
Note that you must always verify that the channel matches your channel. If your channel server is not at pear.php.net or pecl.php.net, you must reject all xsdversion='1.0' requests, and all xsdversion='2.0' requests where the channel is not your channel.
parentpackage
This is information on the parent package, and is an array of format:
array(
'channel' => channel name,
'package' => package name,
'version' => specific version to retrieve,
)
preferred_state = stable
The client-side preferred_state. This should be used to exclude releases that are too unstable.
installed_version = false
The current installed version of the dependency on the client-side. This will either be a version string, or false if the package is not installed. Use this to ensure that older versions are never returned (as defined by version_compare(possible_version, installed_version, "<")).
Like package.getDownloadURL, package.getDepDownloadURL should return an array with either two or three indices.
"version" => version of the release returned
"info" => the complete package.xml contents from the release
"url" => a URL from which to download this release. If no releases exist that fit the constraints defined by preferred_state, installed_version, and the version/state indices of packageinfo, then do not return this index, and instead return the version and package.xml of the latest release.
The url entry should NOT append .tgz or .tar, but should be something like "http://pear.php.net/get/PEAR-1.4.0" instead of "http://pear.php.net/get/PEAR-1.4.0.tgz"
Note that version 1.0 of package.getDepDownloadURL did not have the installed_version parameter. Version 1.1 of package.getDepDownloadURL does - that is the only difference between the two versions.
false|struct package.info (
string package
, string field = null
)
package
Package name to retrieve information about
field = null
specific field to retrieve information about. If null, this should return an array with this indices, although others could be set as well:
<?php
array(
'name' => 'package name',
'category' => 'category name',
'license' => 'package license',
'summary' => 'package summary',
'description' => 'package description',
'releases' =>
array( // all releases indexed by version
'0.1' =>
array(
'license' => 'release license',
'summary' => 'release summary',
'description' => 'release description',
'releasedate' => 'date of release',
'releasenotes' => 'release notes',
'state' => 'release stability',
// the next index is optional
'deps' =>
array(
array( // release dependencies of latest release
'type' => 'dep type from package.xml <dep>',
'relation' => 'rel from package.xml <dep>',
'version' => 'version from package.xml <dep>, or empty string',
'name' => 'name from package.xml <dep>',
'optional' => 'yes or no',
),
// and so on with all deps
),
),
// and so on with all releases
// releases should be ordered by releasedate DESC
),
);
?>
The second parameter, if set, must be one of these choices:
authors - a current list of package maintainers in format:
<?php
array(
'handle1' =>
array(
'name' => 'Maintainer Name',
'email' => 'maintainer@example.com',
'role' => 'role from package.xml (lead, developer, contributor, helper)',
),
'handle2' =>
array(
'name' => 'Maintainer Name 2',
'email' => 'maintainer2@example.com',
'role' => 'role from package.xml (lead, developer, contributor, helper)',
),
// etc.
);
?>
category - the category this package is in
description - the description of the latest release
license - package license of latest release
notes - release notes of the latest release
releases - an array of the format documented above, containing information on all releases
summary - summary from latest release
struct package.listAll (
bool released_only = true
, bool stable_only = true
)
released_only
= true
If TRUE, then packages that have no releases should not be returned in the listing of available packages
stable_only
= true
If TRUE, then packages that have no stable releases should not be returned in the listing of available packages
This function should return an array of this format for all packages that match the constraints defined above:
<?php
array(
array(
'name' => 'packagename',
'category' => 'category name',
'license' => 'release license',
'summary' => 'package summary',
'description' => 'package description',
'stable' => 'latest release version that matches constraints',
'unstable' => 'latest unstable release version or false if stable_only',
'state' => 'release state of latest release that matches constraints',
'deps' =>
array( // same format as for package.info
)
),
// etc.
);
?>
struct package.listLatestReleases (
string state = ''
)
state = ''
If '', then the newest release will be returned for all packages. Otherwise, it must be one of 'snapshot', 'devel', 'alpha', 'beta', or 'stable', and the function should return the newest release that is more stable than state.
If state is 'beta', then the function should return the latest release that is beta or stable. If state is 'devel', the function should return the latest release that is devel, alpha, beta, or stable, and so on.
This function should return an array of this format for all packages that have a release within the constraint defined by the "state" parameter:
<?php
array(
array(
'package' => 'packagename',
'version' => 'release version',
'state' => 'release stability',
'filesize' => 'size of the .tgz file to download',
),
// etc.
);
?>
struct package.listAll (
string fragment
, string|bool summary = false
, bool released_only = true
, bool stable_only = true
)
fragment
A text fragment to use when searching for packages by name
summary
= false
If set to false, this should be ignored. Otherwise, this should be used to search through the summaries of packages that match the first parameter to limit the list of returned packages.
released_only
= true
If TRUE, then packages that have no releases should not be returned in the listing of available packages
stable_only
= true
If TRUE, then packages that have no stable releases should not be returned in the listing of available packages
This function should return an array of this format for all packages that match the constraints defined above:
<?php
array(
array(
'name' => 'packagename',
'category' => 'category name',
'license' => 'release license',
'summary' => 'package summary',
'description' => 'package description',
'stable' => 'latest release version that matches constraints',
'unstable' => 'latest unstable release version or false if stable_only',
'state' => 'release state of latest release that matches constraints',
'deps' =>
array( // same format as for package.info
)
),
// etc.
);
?>
2009-07-01
Documentation is a work in progress.
The SimpleChannelServer package allows developers to create a PEAR compatible channel server to distribute packages. This channel server is simple in that it only works with the local filesystem to create the necessary files for package distribution.
At a minimum, the ability to post files to the webspace where your channel will be hosted.
It is possible to host your PEAR channel on webspace such as Google Code, just use the webpath to the svn directory as your channel name, and be sure to set the correct svn:mime-type on the xml files.
To get started, simply download the pearscs.phar
file to your local filesystem, and execute the pearscs.phar
file with the create
command. Then, build the rest of
the channel using the other SimpleChannelServer commands.
Commands available for the pearscs command-line script.
The create
command creates a PEAR channel on the
local filesystem, including the channel.xml file and the get and rest
directories. This command assumes you are currently at the web root
for the channel you wish to create, unless you pass the path to where the
channel.xml file will be created. The local root can be a subdirectory
off of your webserver, but you should be in that subdirectory when
executing this command.
pearscs create pear.example.com summary [alias] [./channel.xml]
Required arguments are the channel name and channel summary. For example pear.example.com or can also be a subdirectory such as www.example.com/pear.
The alias is a short name which can be used to refer to your PEAR channel. If one is not specified, SimpleChannelServer will attempt to create one for you.
If the path to a channel.xml file is specified, the directory given will be used to create the channel.xml, get, and rest directories for the channel. If no path is specified, SimpleChannelServer will use the current directory.
The release
command releases a package to your PEAR
channel.
pearscs release MyPackage-1.0.0.tgz handle
The file must be a valid PEAR package, and can be either the package.xml file, or a compressed and optionally signed .tgz file.
The handle
attribute should be a lead maintainer for the
package, as who released this version will be stored within the channel's
xml files.
The update
command re-generates the xml files for all
releases.
pearscs update [./channel.xml]
This command updates the associated xml files for all releases present in the get directory.
The add-maintainer
command adds a maintainer's handle
to the list of channel maintainers.
pearscs add-maintainer handle
This command adds the handle of a maintainer to the local channel. It is not necessary to add maintainers before releasing a package, as they will automatically be added or updated.
The add-category
command adds a category to the channel.
pearscs add-cateogry category
This command adds a category to the channel for grouping related packages.
Provides Packages for authentication management
Provides a framework for user authentication.
Our goal during this "mini tutorial" is to set up a system that secures your site with an easy to use authentication mechanism.
At the top of the site to secure, place the following code snippet:
Typical usage example for PEAR::Auth
<?php
require_once "Auth.php";
// Takes three arguments: last attempted username, the authorization
// status, and the Auth object.
// We won't use them in this simple demonstration -- but you can use them
// to do neat things.
function loginFunction($username = null, $status = null, &$auth = null)
{
/*
* Change the HTML output so that it fits to your
* application.
*/
echo "<form method=\"post\" action=\"test.php\">";
echo "<input type=\"text\" name=\"username\">";
echo "<input type=\"password\" name=\"password\">";
echo "<input type=\"submit\">";
echo "</form>";
}
$options = array(
'dsn' => "mysql://user:password@localhost/database",
);
$a = new Auth("DB", $options, "loginFunction");
$a->start();
if ($a->checkAuth()) {
/*
* The output of your site goes here.
*/
}
?>
This few lines of code instantiate the authentication system.
The first line in the above script includes the file from your PEAR directory. It contains all the necessary code to run PEAR::Auth. Next, we define a function to display the login form which the visitor of your page has to use to enter his login data. You can change all the HTML formatting in this function.
Since we want to use a database to verify the login data, we now
create the variable $dsn
, it contains a valid
DSN string that will be used to connect to the database via
PEAR::DB. For the default
database table schema or to use a different storage container,
please see below for more information.
After that we create the authentication object. The first parameter defines the name of the storage container. Because we want to use a database driven storage container, we pass "DB" here. The second parameter is the connection parameter for the container driver. We use the previously defined DSN string. The third parameter is the name of our function that we defined at the beginning of the script. It prints the login form.
Now our authentication object is initialized and we need to check if the user is logged in. This is done via the method checkAuth(). If it returns TRUE, we can pass the content of our page to the user.
Using optional authentication
<?php
// In this test, the file is named "test.php".
require_once "Auth.php";
function loginFunction()
{
/*
* Change the HTML output so that it fits to your
* application.
*/
echo "<form method=\"post\" action=\"test.php?login=1\">";
echo "<input type=\"text\" name=\"username\">";
echo "<input type=\"password\" name=\"password\">";
echo "<input type=\"submit\">";
echo "</form>";
}
if (isset($_GET['login']) && $_GET['login'] == 1) {
$optional = true;
} else {
$optional = false;
}
$options = array(
'dsn' => "mysql://user:password@localhost/database",
);
$a = new Auth("DB", $options, "loginFunction", $optional);
$a->start();
echo "Everybody can see this text!<br />";
if (!isset($_GET['login'])) {
echo "<a href=\"test.php?login=1\">Click here to log in</a>\n";
}
if ($a->getAuth()) {
echo "One can only see this if he is logged in!";
}
?>
This is a pretty nice example for the optional login feature: The
last parameter $optional
can be either TRUE
or FALSE. If it is FALSE, the login form is not shown and the user
only sees the text "Everybody can see this text!". If he clicks on
the link above this text, he gets the same page but with the GET
parameter "login=1". Now he can enter his login information in the
login form. If he successfully logs in, he can then see the text
"Everybody can see this text!" and the text "One can only see this
if he is logged in!".
Logout functionality
The following example performs a "logout" for the current user and displays the login form again.
<?php
$myauth->start();
if ($_GET['action'] == "logout" && $myauth->checkAuth()) {
$myauth->logout();
$myauth->start();
}
?>
In the following passages we cover more detailed information about the functions of PEAR::Auth.
This SQL statement creates a table usable under the default database authentication scheme using MySQL:
CREATE TABLE auth ( username VARCHAR(50) default '' NOT NULL, password VARCHAR(32) default '' NOT NULL, PRIMARY KEY (username), KEY (password) );
These are the table and field names necessary for working authentication. When hashing the passwords with the MD5 algorithm, which is the default encryption method in PEAR::Auth, the password column must be at least 32 characters long. When using another encryption method like DES ("UNIX crypt"), the column size has to be changed correspondingly.
In addition to the options available for each container a series of options can be included that control the behaviour of Auth itself.
Option | Data Type | Default value | Description |
---|---|---|---|
"sessionName" | string | "_authsession" | The name used to identify this Auth session. See Auth::setSessionName() |
"allowLogin" | boolean | TRUE | Whether to allow logins to be performed on this page. See Auth::setAllowLogin() |
"postUsername" | string | "username" | The name of the form field that contains the username to authenticate. |
"postPassword" | string | "password" | The name of the form field that contains the password to authenticate. |
"advancedsecurity" | boolean | FALSE | Whether to enable the advanced security features. See Auth::setAdvancedSecurity() |
"enableLogging" | boolean | FALSE | Whether to enable the internal logging system. See the Logging Examples and Auth::attachLogObserver() |
"regenerateSessionId" | boolean | FALSE | Set to TRUE to regenerate the session id on every page load or leave as FALSE to regenerate only upon new login. |
Since version 1.5.0 PEAR::Auth provides a facility for retrieving logs of its internal behaviour. This is implemented using PEAR::Log and its log observer components.
Auth provides two levels of log messages which map to the Log priority levels PEAR_LOG_INFO and PEAR_LOG_DEBUG.
PEAR_LOG_INFO messages provide basic information about Auth's decisions, for example user authentication successful/failed, rendering login screen.
PEAR_LOG_DEBUG messages provide detailed information about what is happening within the internals of Auth, for example which functions are called, logic tracking for the Authentication method, what SQL queries are being run against the database backends.
Typical usage example for accessing the logs from PEAR::Auth
<?php
require_once "Auth.php";
require_once 'Log.php';
require_once 'Log/observer.php';
// Callback function to display login form
function loginFunction($username = null, $status = null, &$auth = null)
{
/*
* Change the HTML output so that it fits to your
* application.
*/
echo "<form method=\"post\" action=\"".$_SERVER['PHP_SELF']."\">";
echo "Username: <input type=\"text\" name=\"username\"><br/>";
echo "Password: <input type=\"password\" name=\"password\"><br/>";
echo "<input type=\"submit\">";
echo "</form>";
}
class Auth_Log_Observer extends Log_observer {
var $messages = array();
function notify($event) {
$this->messages[] = $event;
}
}
$options = array(
'enableLogging' => true,
'cryptType' => 'md5',
'users' => array(
'guest' => md5('password'),
),
);
$a = new Auth("Array", $options, "loginFunction");
$infoObserver = new Auth_Log_Observer(PEAR_LOG_INFO);
$a->attachLogObserver($infoObserver);
$debugObserver = new Auth_Log_Observer(PEAR_LOG_DEBUG);
$a->attachLogObserver($debugObserver);
$a->start();
if ($a->checkAuth()) {
/*
* The output of your site goes here.
*/
print "Authentication Successful.<br/>";
}
print '<h3>Logging Output:</h3>'
.'<b>PEAR_LOG_INFO level messages:</b><br/>';
foreach ($infoObserver->messages as $event) {
print $event['priority'].': '.$event['message'].'<br/>';
}
print '<br/>'
.'<b>PEAR_LOG_DEBUG level messages:</b><br/>';
foreach ($debugObserver->messages as $event) {
print $event['priority'].': '.$event['message'].'<br/>';
}
print '<br/>';
?>
To enable logging pass the option "enableLogging" with the value TRUE in the options array in the second parameter to the Auth constructor.
To retrieve the log messages from Auth create a new subclass of Log_Observer that implements a notify() function to perform whatever actions you want on the log messages.
Once defined pass an instance of your new Log_Observer instance to Auth::attachLogObserver().
To limit the types of messages you receive in the Log_Observer pass either PEAR_LOG_INFO or PEAR_LOG_DEBUG as the first parameter to the Log_Observer. The default is PEAR_LOG_INFO. For more information on this filtering see the Log Documentation.
<?php
$observer = new My_Log_Observer(PEAR_LOG_DEBUG);
?>
This container has only been available since version 1.5.0.
PEAR::Auth uses a number of so called storage containers to store the login data. The following passages describe all of them. If the containers that come with the package don't fit your needs, it is easy to create custom ones, also.
Array
Storage container using a PHP Array.
DB
Storage container using PEAR DB.
DBLite
A simplified version of the DB container that only provides user authentication. No user management functions are provided.
File
Storage container using PEAR File_Passwd.
IMAP
Storage container for use against IMAP servers.
KADM5
Storage container for use against Kerberos V servers.
LDAP
Storage container for use against LDAP servers.
MDB
Storage container using PEAR MDB.
MDB2
Storage container using PEAR MDB2.
Multiple
Storage container for using multiple Auth_Containers in a fall through manner.
PEAR
Storage container for use against the PEAR website.
POP3
Storage container for use against a POP3 server.
RADIUS
Storage container for use against a RADIUS server.
SAP
Storage container for use against a SAP server.
SMBPasswd
Storage container using PEAR File_SMBPasswd.
SOAP
Storage container for use against a SOAP service using PEAR SOAP as the client.
SOAP5
Storage container for use against a SOAP service using PHP5 SOAP as the client.
vpopmail
Storage container using vpopmail.
Custom
Instructions on creating a custom storage container.
This storage container provides a simple way to store a limited number of username/password pairs within the source code of the script.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"cryptType" | string | "none" | The encryption type the password is stored in. |
"users" | array | array() |
Named array of usernames and password hashes.
<?php
|
This container makes use of PEAR::DB abstraction layer for database access. That means that you can use all databases that are supported by the DB abstraction layer to store the login data.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"dsn" | string | " | A valid and well-formed DSN . |
"table" | string | "auth" | The name of the database table, where the authorization data is stored. |
"usernamecol" | string | "username" | The name of the column, where the username is stored |
"passwordcol" | string | "password" | The name of the column, where the crypted password is stored. |
"db_fields" | array | array() | An array of extra fields to retrieve when loading the user details. |
"cryptType" | string | "md5" | The encryption type the password is stored in. |
"auto_quote" | boolean | TRUE | Whether to enable automatic quoting of database table and field names. |
"db_options" | array | array() | An array of options to be passed to the PEAR::DB constructor. See PEAR::DB::setOption() for more information. |
"db_where" | string | " | A string to be appended to the WHERE clause of queries against the database. It is appended to the queries used in fetchData(), listUsers(), removeUser() and changePassword(). Available since Auth 1.5.0. |
The DBLite container is a simplified version of the DB container. It does away with all the code related to user management and simply provides user authentication.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"dsn" | string | " | A valid and well-formed DSN . |
"table" | string | "auth" | The name of the database table, where the authorization data is stored. |
"usernamecol" | string | "username" | The name of the column, where the username is stored |
"passwordcol" | string | "password" | The name of the column, where the crypted password is stored. |
"db_fields" | array | array() | An array of extra fields to retrieve when loading the user details. |
"cryptType" | string | "md5" | The encryption type the password is stored in. |
"auto_quote" | boolean | TRUE | Whether to enable automatic quoting of database table and field names. |
"db_options" | array | array() | An array of options to be passed to the PEAR::DB constructor. See PEAR::DB::setOption() for more information. |
"db_where" | string | " | A string to be appended to the WHERE clause of queries against the database. It is appended to the queries used in fetchData(). Available since Auth 1.5.0. |
description
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"type" | string | "Cvs" | The format of the file we are authenticating against. For a list of available types see the PEAR::File_Passwd documentation. |
"file" | string | " | The filename of the file to use. |
This storage container connects to the specified IMAP server and tries to login there with the specified username/password.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"host" | string | "localhost" | The hostname or the IP address of the IMAP server |
"port" | integer | 143 | The port where the IMAP server is listening |
"baseDSN" | string | " |
This controls the methods used to connect. Like SSL, TLS and certificate validation. To connect to an SSL IMAP server: "/imap/ssl" To connect to an SSL IMAP server with a self-signed certificate: "'/imap/ssl/novalidate-cert'" Further options may be available and can be found on the php site at http://www.php.net/manual/function.imap-open.php. |
"timeout" | integer | 20 | Timeout in seconds for connecting to the server. |
"checkServer" | boolean | TRUE | Whether to check if we can connect to the server when starting container. |
This storage driver makes use of the PECL kadm5 extension to provide authentication services against a Kerberos V.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"hostname" | string | "localhost" | The hostname or IP address of the Kerberos V server. |
"realm" | string | " | The Kerberos V realm to authenticate in. |
"timeout" | integer | 10 | Timeout in seconds for connecting to the server. |
"checkServer" | boolean | FALSE | Whether to check if we can connect to the server when starting container. |
This storage container makes use of the ldap extension to load user data from an LDAP server.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"host" | string | "localhost" | The host name or IP-adress to access |
"port" | integer | 389 | The port of the LDAP server to access |
"url" | string | " | A fully qualified URL for specifying the protocol, url and port to connect to. It is useful for specifying ldaps://. Takes precedence over "host" and "port" options. Only works if PHP has been compiled against OpenLDAP 2+ libraries. |
"version" | integer | 2 | LDAP version to use, usually 2 (default) or 3, must be an integer! |
"referrals" | boolean | TRUE | If set, determines whether the LDAP library automatically follows referrals returned by LDAP servers or not. |
"binddn" | string | " | If set, searching for user will be done after binding as this user, if not set the bind will be anonymous. This is reported to make the container work with MS Active Directory, but should work with any server that is configured this way. This has to be a complete dn for now (basedn and userdn will not be appended). |
"bindpw" | string | " | The password to use for binding with binddn. |
"basedn" | string | " | The base DN of your server. |
"userdn" | string | " | Gets prepended to basedn when searching for users. |
"userscope" | string | "sub" | Scope for user searching: one, sub (default), or base. |
"userattr" | string | "uid" | Defines the attribute to search for. |
"userfilter" | string | "(objectClass=posixAccount)" | Filter that will be added to the search filter this way: (&(userattr=username)(userfilter)). |
"attributes" | array | array('') | Array of additional attributes to fetch from entry. These will added to auth data and can be retrieved via Auth::getAuthData() . An empty array will fetch all attributes, array('') will fetch no attributes at all (default). If you add 'dn' as a value to this array, the user's DN that was used for binding will be added to auth data as well. |
"attrformat" | string | "AUTH" |
The returned format of the additional data defined in the 'attributes' option. Two formats are available. LDAP returns data formatted in a multidimensional array where each array starts with a 'count' element providing the number of attributes in the entry, or the number of values for attributes. When set to this format, the only way to retrieve data from the Auth object is by calling getAuthData('attributes'). This was the default format before version 1.3.0. AUTH returns data formatted in a structure more compliant with other Auth Containers, where each attribute element can be directly called by getAuthData() method from Auth. This became the default as of 1.3.0. |
"groupdn" | string | " | Gets prepended to basedn when searching for group. |
"groupattr" | string | "cn" | The group attribute to search for. |
"groupfilter" | string | "(objectClass=groupOfUniqueNames)" | Filter that will be added to the search filter when searching for a group: (&(groupattr=group)(memberattr=username)(groupfilter)). |
"memberattr" | string | "uniqueMember" | The attribute of the group object where the user dn may be found. |
"memberisdn" | boolean | TRUE | Whether the memberattr is the dn of the user (default) or the value of userattr (usually uid). |
"group" | string | " | The name of the group users have to be a member of to authenticate successfully. |
"groupscope" | string | "sub" | Scope for group searching: one, sub (default), or base. |
"start_tls" | boolean | "false" | Enable/disable the use of START_TLS encrypted connection. |
"try_all" | boolean | FALSE | If multiple entries are returned by the search attempt to authenticate against each entry in order or just the first one (default). |
"debug" | boolean | FALSE | Enable debug output. |
Authenticating against a LDAP server
<?php
$a1 = new Auth("LDAP", array(
'host' => 'localhost',
'port' => '389',
'version' => 3,
'basedn' => 'o=netsols,c=de',
'userattr' => 'uid',
'binddn' => 'cn=admin,o=netsols,c=de',
'bindpw' => 'password'
));
?>
Requiring users to be within specific groups
<?php
$a2 = new Auth('LDAP', array(
'url' => 'ldaps://ldap.netsols.de',
'basedn' => 'o=netsols,c=de',
'userscope' => 'one',
'userdn' => 'ou=People',
'groupdn' => 'ou=Groups',
'groupfilter' => '(objectClass=posixGroup)',
'memberattr' => 'memberUid',
'memberisdn' => false,
'group' => 'admin'
));
?>
Authenticating against a Microsoft Active Directory server
<?php
$a3 = new Auth('LDAP', array(
'host' => 'ldap.netsols.de',
'port' => 389,
'version' => 3,
'referrals' => false,
'basedn' => 'dc=netsols,dc=de',
'binddn' => 'cn=Jan Wagner,cn=Users,dc=netsols,dc=de',
'bindpw' => 'password',
'userattr' => 'samAccountName',
'userfilter' => '(objectClass=user)',
'attributes' => array(''),
'group' => 'testing',
'groupattr' => 'samAccountName',
'groupfilter' => '(objectClass=group)',
'memberattr' => 'member',
'memberisdn' => true,
'groupdn' => 'cn=Users',
'groupscope' => 'one',
));
?>
When talking to a Microsoft ActiveDirectory server you have to use 'samaccountname' as the 'userattr' and follow special rules to translate the ActiveDirectory directory names into 'basedn'. The 'basedn' for the default 'Users' folder on an ActiveDirectory server for the ActiveDirectory Domain (which is not related to its DNS name) "win2000.example.org" would be: "CN=Users, DC=win2000, DC=example, DC=org" where every component of the domain name becomes a DC attribute of its own. If you want to use a custom users folder you have to replace "CN=Users" with a sequence of "OU" attributes that specify the path to your custom folder in reverse order. So the ActiveDirectory folder "win2000.example.org\Custom\Accounts" would become "OU=Accounts, OU=Custom, DC=win2000, DC=example, DC=org"
It seems that binding anonymously to an Active Directory is not allowed, so you have to set binddn and bindpw for user searching.
LDAP Referrals need to be set to false for AD to work sometimes.
Note also that if you want an encrypted connection to an MS LDAP server, then, on your webserver, you must specify "TLS_REQCERT never" in /etc/ldap/ldap.conf or in the webserver user's ~/.ldaprc (which may or may not be read depending on your configuration).
This container makes use of PEAR::MDB abstraction layer for database access. That means that you can use all databases that are supported by the MDB abstraction layer to store the login data.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"dsn" | string | " | A valid and well-formed DSN . |
"table" | string | "auth" | The name of the database table, where the authorization data is stored. |
"usernamecol" | string | "username" | The name of the column, where the username is stored |
"passwordcol" | string | "password" | The name of the column, where the crypted password is stored. |
"db_fields" | array | array() | An array of extra fields to retrieve when loading the user details. |
"cryptType" | string | "md5" | The encryption type the password is stored in. |
"auto_quote" | boolean | TRUE | Whether to enable automatic quoting of database table and field names. |
"db_options" | array | array() | An array of options to be passed to the PEAR::MDB constructor. See PEAR::MDB for more information. |
"db_where" | string | " | A string to be appended to the WHERE clause of queries against the database. It is appended to the queries used in fetchData(), listUsers(), removeUser() and changePassword(). Available since Auth 1.5.0. |
This container makes use of PEAR::MDB2 abstraction layer for database access. That means that you can use all databases that are supported by the MDB2 abstraction layer to store the login data.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"dsn" | string | " | A valid and well-formed DSN . |
"table" | string | "auth" | The name of the database table, where the authorization data is stored. |
"usernamecol" | string | "username" | The name of the column, where the username is stored |
"passwordcol" | string | "password" | The name of the column, where the crypted password is stored. |
"db_fields" | array | array() | An array of extra fields to retrieve when loading the user details. |
"cryptType" | string | "md5" | The encryption type the password is stored in. |
"auto_quote" | boolean | TRUE | Whether to enable automatic quoting of database table and field names. |
"db_options" | array | array() | An array of options to be passed to the PEAR::MDB2 constructor. See PEAR::MDB2 for more information. |
"db_where" | string | " | A string to be appended to the WHERE clause of queries against the database. It is appended to the queries used in fetchData(), listUsers(), removeUser() and changePassword(). Available since Auth 1.5.0. |
By default, MDB2's default portability setting of MDB2_PORTABILITY_ALL is used. This setting may cause unexpected behaviour, such as field names being converted to lowercase regardless of their definition in the database schema. The "db_options" option can be used to override this, as shown in the following example.
Example overriding MDB2's default portability
<?php
$options = array('dsn' => 'mysql://user:password@localhost/database',
'usernamecol' => 'UserName',
'passwordcol' => 'PassWord',
'db_options' => array('portability' => MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE)
);
$auth = new Auth('MDB2', $options, 'loginFunction');
?>
This container provides a facility to attempt to authenticate against multiple Auth_Containers in order.
If a container's getAuthData() returns true Auth_Container_Multiple will return true.
When a container's getAuthData() returns false Auth_Container_Multiple will continue on through the list of available containers until a successful login is found or the list of containers is expired.
On receipt of an error from getAuthData() Auth_Container_Multiple will abort checking further containers and return the error.
The storage-specific argument for the Auth constructor() is an array of container configurations. Each container configuration has the following options:
Option | Data Type | Default value | Description |
---|---|---|---|
"type" | string | " | The type of container to instanciate. This is the same value as used in the first parameter of the Auth constructor() . |
"options" | array | array() | This is the standard array of options required for the container. |
Example usage of Auth_Container_Multiple
<?php
require_once "Auth.php";
require_once 'Log.php';
require_once 'Log/observer.php';
// Callback function to display login form
function loginFunction($username = null, $status = null, &$auth = null)
{
/*
* Change the HTML output so that it fits to your
* application.
*/
echo "<form method=\"post\" action=\"".$_SERVER['PHP_SELF']."\">";
echo "Username: <input type=\"text\" name=\"username\"><br/>";
echo "Password: <input type=\"password\" name=\"password\"><br/>";
echo "<input type=\"submit\">";
echo "</form>";
}
class Auth_Log_Observer extends Log_observer {
var $messages = array();
function notify($event) {
$this->messages[] = $event;
}
}
$options = array(
'enableLogging' => true,
array(
'type' => 'Array',
'options' => array(
'cryptType' => 'md5',
'users' => array(
'guest' => md5('password'),
),
),
),
array(
'type' => 'Array',
'options' => array(
'cryptType' => 'md5',
'users' => array(
'admin' => md5('password'),
),
),
),
);
$a = new Auth("Multiple", $options, "loginFunction");
$infoObserver = new Auth_Log_Observer(PEAR_LOG_INFO);
$a->attachLogObserver($infoObserver);
$debugObserver = new Auth_Log_Observer(PEAR_LOG_DEBUG);
$a->attachLogObserver($debugObserver);
$a->start();
if ($a->checkAuth()) {
/*
* The output of your site goes here.
*/
print "Authentication Successful.<br/>";
}
print '<h3>Logging Output:</h3>'
.'<b>PEAR_LOG_INFO level messages:</b><br/>';
foreach ($infoObserver->messages as $event) {
print $event['priority'].': '.$event['message'].'<br/>';
}
print '<br/>'
.'<b>PEAR_LOG_DEBUG level messages:</b><br/>';
foreach ($debugObserver->messages as $event) {
print $event['priority'].': '.$event['message'].'<br/>';
}
print '<br/>';
?>
This container has only been available since version 1.5.0.
This container provides authentication services against the PEAR website (http://pear.php.net/) and the developer accounts. The HTTP_Client package must be installed for this container to operate, as of Auth 1.5.3.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"url" | string | "https://pear.php.net/rest-login.php/" | The base URL of a PEAR website to authenticate against. |
"karma" | array | array() | List of karma levels required for a valid authentication. If no karma levels are supplied than simply validating the username and password will result in success. |
This storage container connects to the specified POP3 server and tries to login there with the specified username/password.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"host" | string | "localhost" | The hostname or IP address of the POP3 server. |
"port" | integer | "110" | The port number the POP3 server is listening on. |
"method" | boolean or string | TRUE |
The authentication method to use with the POP3 server. Available options:
|
You need Auth_RADIUS and the PECL radius in order to get this container to work.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"servers" | array | array("localhost", 0, "testing123", 3, 3) |
Array of RADIUS servers, containing: host, port, shared secret, timeout, maxtries. The host parameter specifies the server host, either as a fully qualified domain name or as a dotted-quad IP address in text form. The port parameter specifies the UDP port to contact on the server. If port is given as 0, the library looks up the radius/udp entry in the network services database, and uses the port found there. If no entry is found, the library uses the standard RADIUS port for authentication (1812). The shared secret for the server host is passed to the secret parameter. The RADIUS protocol ignores all but the leading 128 bytes of the shared secret. The timeout for receiving replies from the server is passed to the timeout parameter, in units of seconds. The maximum number of repeated requests to make before giving up is passed into the maxtries parameter. At most 10 servers may be specified. When multiple servers are given, they are tried in round-robin fashion until a valid response is received, or until each server's maxtries limit has been reached. |
"authtype" | string | "PAP" |
The authentication method for validating the request. Possible values are: PAP, CHAP_MD5, MSCHAPv1, MSCHAPv2. There are dependencies for the different methods. For all authentication methods except PAP you need the Crypt_CHAP package, when you are using MS-CHAP you need also the mhash extension. |
This container allows authentication against a SAP server using the SAPRFC extension available at http://saprfc.sourceforge.net/.
The storage-specific argument for the Auth constructor() is an array of options which are passed directly to the SAPRFC extension. None of the options are used internally, for more details see the SAPRFC's documentation.
This storage container provides authentication against SAMBA smbpasswd files. It makes use of the PEAR File_SMBPasswd package.
The storage-specific argument for the Auth constructor() is a string containing the filename of the SAMBA smbpasswd file to use.
This container makes use of the PEAR SOAP client to provide authentication against a SOAP service.
The storage-specific argument for the Auth constructor() is an array of options.
Option | Data Type | Default value | Description |
---|---|---|---|
"endpoint" | string | The URI where the service is located. | |
"namespace" | string | The namespace of the web service. | |
"method" | string | The SOAP method you wish to call. | |
"encoding" | string |
The content encoding that should be used (e.g.
UTF-8 ).
|
|
"usernamefield" | string | The name of the field where the username is stored. | |
"passwordfield" | string | The name of the field where the password is stored. | |
"matchpasswords" | boolean | TRUE |
If TRUE then the container will look for the password field in the returned result from the soap call and try to match that value with the one supplied from the end user. If FALSE then it is assumed that the soap call will return an error if the user does not authenticate properly. If no error is returned than the user is assumed to valid and allowed to proceed. |
"_features" | array |
A named array of extra parameters that are to be passed to the soap call in addition to the username and password fields. These are passed to the soap call as named parameters.
<?php
|
This container makes use of the PHP SOAP extension's client to provide authentication against a SOAP service.
The storage-specific argument for the Auth constructor() is an array of options. In addition to the below options all options for the PHP SOAP Client can be passed in this array and they will be passed onto the client.
Option | Data Type | Default value | Description |
---|---|---|---|
"wsdl" | string | The location of the WSDL file describing the SOAP service you wish to authenticate against. The WSDL file takes priority over a specified "location" and "uri". | |
"location" | string | The URI where the service is located, when not making use of a WSDL file. | |
"uri" | string | The namespace of the web service, when not making use of a WSDL file. | |
"method" | string | The SOAP method you wish to call. | |
"usernamefield" | string | "username" | The name of the field where the username is stored. |
"passwordfield" | string | "password" | The name of the field where the password is stored. |
"matchpasswords" | boolean | TRUE |
If TRUE then the container will look for the password field in the returned result from the soap call and try to match that value with the one supplied from the end user. If FALSE then it is assumed that the soap call will return an error if the user does not authenticate properly. If no error is returned than the user is assumed to valid and allowed to proceed. |
"_features" | array |
A named array of extra parameters that are to be passed to the soap call in addition to the username and password fields. These are passed to the soap call as named parameters.
<?php
|
This container uses an existing vpopmail service to validate the username and the password.
It does not require any storage-specific argument.
Here is a skeleton for a custom Auth storage container
CustomAuthContainer.php
<?php
include_once 'Auth/Container.php';
class CustomAuthContainer extends Auth_Container
{
/**
* Constructor
*/
function CustomAuthContainer($params)
{
// Init Here
}
function fetchData($username, $password)
{
// Check If valid etc
if($isvalid) {
// Perform Some Actions
return true;
}
return false;
}
}
?>
And here is how to use it.
authcustom.php
<?php
include_once 'CustomAuthContainer.php';
include_once 'Auth/Auth.php';
$auth_container = new CustomAuthContainer($params);
$myauth = new Auth($auth_container);
$myauth->start();
?>
PEAR::Auth_Frontend_HTML provides a generic default login page for use with PEAR::Auth. When building your own login page it is a reasonable place to start as it includes all the required elements of the login form.
-1
Returned if a session exceeds its idle time.
-2
Returned if a session has expired.
-3
Returned if the Auth Container in use is unable to validate the username/password pair supplied.
-4
Returned if the requested function is not implemented by the Auth Container in use.
-5
Returned if the advanced security checking detects a breach.
-6
Returned if the checkAuth callback function aborted the session.
Auth::Auth (
mixed $storageDriver
, mixed $options = ""
, string $loginFunction = ""
, boolean $showLogin
= true
)
Constructor for the authentication system.
The constructor will ensure that the PHP session management is started by calling session_start(). This is done as Auth requires a session to be active to operate correctly.
$storageDriver
Name of the storage driver that should be used, alternatively you can pass a custom Auth_Container object.
$options
Array containing the options for both Auth itself and the storage driver. See Auth Options for global options and each storage driver's documentation for specific options.
$loginFunction
The name of a user-defined function that prints the login screen. This function is passed three parameters when called $username, $status, &$auth. These are in order the previously attempted username, the status code that caused the previous auth attempt to fail and a reference to the Auth object itself.
$showLogin
Defines if the login is optional or not.
This function can not be called statically.
Using different DB parameters
<?php
require_once "Auth/Auth.php";
function myOutput($username, $status, $auth)
{
... /* See first example in introduction for the full listing */
}
$params = array(
"dsn" => "mysql://martin:test@localhost/auth",
"table" => "myAuth",
"usernamecol" => "myUserColumn",
"passwordcol" => "myPasswordColumn"
);
$a = new Auth("DB", $params, "myOutput");
$a->start();
if ($a->getAuth()) {
echo "You have been authenticated successfully.";
}
?>
This example shows you how you can specifiy alternative names for the database table and the column names. In our example, we use the table myAuth, select the username from the field myUserColumn and the password from the field myPasswordColumn. The default values for this fields are auth, username and password. Note that you can also specify an existing DB object with the dsn parameter instead of a DSN.
This feature is necessary if you want to use PEAR::Auth with a database layout that is different from the one PEAR::Auth uses by default.
Using different DB parameters
<?php
require_once "Auth/Auth.php";
function myOutput($username, $status)
{
... /* See first example in introduction for the full listing */
}
$a = new Auth(new CustomAuthContainer($options), null, "myOutput");
$a->start();
if ($a->getAuth()) {
echo "You have been authenticated successfully.";
}
?>
This example shows you how you can pass your own storage container to Auth.
If the storage containers supplied with Auth are not capable of fulfilling your requirements, you can easliy create your own storage container. Read the storage containers section for more info Introduction - The storage drivers
mixed Auth::addUser (
string $username
, string $password
, mixed $additional = ''
)
Add a new user to the Auth Container.
$username
the username of the new user
$password
the password of the new user
$additional = ''
additional options to be passed to the creation of the new user. Each Auth_Container has different options for these, please see the containers documentation for what is supported.
mixed
-
TRUE on success, a PEAR Error on failure.
This function can not be called statically.
Not all Auth Containers implement this functionality.
void Auth::attachLogObserver (
object &$observer
)
Attach an instance of a Log_Observer to the internal Log object. This allows the internal logs to be accessed and used as you wish within your application.
&$observer
The instance of the Log_Observer to attach to the internal Log object.
This function can not be called statically.
This function has only been available since version 1.5.0.
mixed Auth::changePassword (
string $username
, string $password
)
Change the password of a user within the Auth Container.
$username
the username of the user
$password
the new password for the user
mixed
-
TRUE on success, a PEAR Error on failure.
This function can not be called statically.
Not all Auth Containers implement this functionality.
boolean Auth::checkAuth (
)
Checks if a session exists that contains valid authentication information.
boolean
- If a session with valid
authentication information exists, the function return TRUE.
Otherwise it returns FALSE.
This function can not be called statically.
boolean Auth::getAuth (
)
Check if the user has been authenticated.
In previous versions, this function had a different behaviour than Auth::checkAuth()(). Now it is just an alias for Auth::checkAuth()().
boolean
-
If the user has already been authenticated, the function returns
TRUE. Otherwise it returns FALSE.
This function can not be called statically.
mixed Auth::getAuthData (
string $name
)
This function retrieves the value of a previously registered data field.
$name
the name of the data field
This function can not be called statically.
int Auth::getStatus (
)
Get the current status of the authentication session.
int
-
An Auth constant representing the current session state.
This function can not be called statically.
string Auth::getUsername (
)
Get the username of the logged in user.
string
-
The username of the logged in user.
This function can not be called statically.
array Auth::listUsers (
)
List all the users currently available within the current Auth Container.
array
-
An array of user details. Currently no consistency, see each Auth
Container for details on how and what they return.
This function can not be called statically.
Not all Auth Containers implement this functionality.
void Auth::logout (
)
Logout any currently logged in user.
<?php
//$a is our Auth object
$a->start();
if (isset($_GET['action']) && $_GET['action'] == 'logout'
&& $a->checkAuth()
) {
$a->logout();
$a->start();
}
?>
mixed Auth::removeUser (
string $username
)
Remove a user from the Auth Container.
$username
the username of the user to remove
mixed
-
TRUE on success, a PEAR Error on failure.
This function can not be called statically.
Not all Auth Containers implement this functionality.
void Auth::setAdvancedSecurity (
mixed $flag = true
)
Enables advanced security features to make man in the middle attacks and session hijacking much harder. Cookies and java script must be enabled on the client browser for some of these features to function correctly.
Enables the following security features of auth
Detection of client ip address change or User-Agent header change if such a change is detected the user will be logged out
Each client request a special unique cookie is given to the client. He must present this cookie on his next request. This cookie changes on every request. If client does not present the valid cookie he will be logged out.
Enables challenge responce for the default login screen of auth. The user password will be hashed with javascript before sent back to the server. Prevents the user password being stolen using password sniffing tools. Password is hashed with a random key so the md5 hash is not subject to brute force password cracking. This will only work for storage containers which support challenge responce password authenthication. Currently only the DB, MDB and MDB2 containers support this for md5 and clear text passwords
This method is available since
1.3.0
$flag
TRUE if you want to enable advanced security features FALSE if you want to disable them.
You also may pass an array if you want to fine-tune security options. TRUE means the following:
<?php
array(
AUTH_ADV_USERAGENT => true,
AUTH_ADV_IPCHECK => true,
AUTH_ADV_CHALLENGE => true
);
?>
This function can not be called statically.
void Auth::setAllowLogin (
bool $allowLogin = true
)
Controls if auth will process the post variables and attempt to login the user from those pages. For better security of your application it is recomended to disable login in all pages except your login page.
$allowLogin
If you want to allow login set this to TRUE otherwise set it to FALSE.
This function can not be called statically.
This method is available since 1.3.0
void Auth::setAuth (
string $username
)
Mark the session up to indicate that the username supplied has successfully logged in. Although normally used for internal purposes this function can be used to fake a login.
$name
the name of the data field
This function can not be called statically.
This function will also call session_regenerate_id() so as to prevent session fixation attacks.
void Auth::setAuthData (
string $name
, mixed $value
, boolean $overwrite = true
)
This function allows the registration of extra information to be stored allowing with the authentication data.
$name
the name of the data field
$value
the value of the data field
$overwrite
whether to overwrite the value of the data field if it already exists.
This function can not be called statically.
void Auth::setCheckAuthCallback (
string $checkAuthCallback
)
This function sets a callback function which is called whenever the validity of a logged in session is checked. This callback can be used to perform actions like checking the authentication source to see if the logged in user is still enabled.
The callback function will be passed two parameters, the username that successfully logged in and a reference to the Auth object. The callback function should return a boolean depending upon whether the session should continue or not. TRUE to allow the session to continue, FALSE to abort the session.
If the session is aborted by this callback the status will be set to AUTH_CALLBACK_ABORT.
$checkAuthCallback
the call back function to use
This function can not be called statically.
void Auth::setExpire (
integer $time
, boolean $add
= false
)
With this function you can set the maximum expire time that is used by auth to a new value.
$time
expiration time, number of seconds
$add
if TRUE $time
is added to the existing expiration time,
if FALSE the exitsing time value will be replaced.
This function can not be called statically.
Auth uses PHP's session mechanism to recognize users between http calls. Setting Auth's expiry time larger than the session timeout will still expire the session to the time the php session expires.
void Auth::setFailedLoginCallback (
string $loginFailedCallback
)
This function sets a callback function which is called upon failed login of a user account.
The callback function will be passed two parameters, the username that failed to login and a reference to the Auth object.
$loginFailedCallback
the call back function to use
This function can not be called statically.
void Auth::setIdle (
integer $time
, boolean $add
= false
)
With this function one can set the maximum idle time to a new value. The term "idle time" describes the maximum time interval (in seconds) between two actions by the user. If the maximum idle time is reached, the user will be automatically logged out. If on the other hand the user performs any action within the maximum idle time interval, the idle time is reset.
$time
idle time in seconds
$add
if TRUE $time
is added to the existing idle time,
if FALSE the existing time value will be replaced.
This function can not be called statically.
void Auth::setLoginCallback (
string $loginCallback
)
This function sets a callback function which is called upon successful login of a user account.
The callback function will be passed two parameters, the username that successfully logged in and a reference to the Auth object.
$loginCallback
the call back function to use
This function can not be called statically.
void Auth::setLogoutCallback (
string $loginCallback
)
This function sets a callback function which is called upon successful logout of a user account.
The callback function will be passed two parameters, the username that successfully logged out and a reference to the Auth object.
$loginCallback
the call back function to use
This function can not be called statically.
void Auth::setSessionName (
string $name
)
This function is used to set the name of the session variable used to
store the user's authentication details. The default for the session
variable name is _authsession
.
$name
the session variable name to use
This function can not be called statically.
You have to use setSessionName(), if you are running two or more different applications with Auth on the same domain. If you don't use this function, a user who has once logged in at one of the applications can also use the other applications without logging in again!
void Auth::setShowLogin (
bool $showLogin = true
)
Controls if the login page will be displayed to the user. Normally you might need to display the login form only on a certain page of your web application.
$showLogin
true if you want to display the login form and false if you want to hide it.
This function can not be called statically.
This is equivalent to the 4th paramether of Auth::Auth()
void Auth::sessionValidThru (
)
This method returns the time in seconds after which the idle time of the current authentication session has reached its limit, which will cause the user to be logged out automatically. If no maximum idle time is set, which is the default configuration of PEAR::Auth, this method will return 0.
This function can not be called statically.
void Auth::start (
)
Start the authentication process. To do this Auth checks for an existing session and checks its validity. If there is no existing session or the previous session has been ended for some reason then the login form is generated either via the specified callback or using the default form implemented in Auth_Frontend_HTML.
This function can not be called statically.
Provides a framework for user authentication (aka an "User-Login") based on the HTTP
Instead of generating an HTML driven form like PEAR::Auth does, this class sends header commands to the clients which cause them to present a login box like they are e.g. used in Apache's .htaccess mechanism.
Starting with Auth_HTTP 2.1.0, HTTP Digest Authentication (RFC2617) is experimentally supported.
void Auth_HTTP::Auth_HTTP (
string
$storageDriver = "DB"
, mixed
$options = ""
)
Constructor for the authentication system
$storageDriver
name of the storage driver that should be used
$options
a string containing some login information or an array containing a bunch of options for the storage driver
This function can not be called statically.
Using different DB parameters
<?php
require_once "Auth/HTTP.php";
$a = new Auth_HTTP("DB", "mysql://test:test@localhost/test");
$a->start();
?>
<?php
// example of Auth_HTTP basic implementation
require_once("Auth/HTTP.php");
// setting the database connection options
$AuthOptions = array(
'dsn'=>"pgsql://test:test@localhost/testdb",
'table'=>"testable", // your table name
'usernamecol'=>"username", // the table username column
'passwordcol'=>"password", // the table password column
'cryptType'=>"none", // password encryption type in your db
);
$a = new Auth_HTTP("DB", $AuthOptions);
$a->setRealm('yourrealm'); // realm name
$a->setCancelText('<h2>Error 401</h2>'); // error message if authentication fails
$a->start(); // starting the authentication process
if($a->getAuth()) // checking for autenticated user
{
echo "Hello $a->username welcome to my secret page";
};
?>
<?php
// example of Auth_HTTP implementation with encrypted password and multiple columns fetch
require_once("Auth/HTTP.php");
// setting the database connection options
$AuthOptions = array(
'dsn'=>"pgsql://test:test@localhost/testdb",
'table'=>"testable", // your table name
'usernamecol'=>"username", // the table username column
'passwordcol'=>"password", // the table password column
'cryptType'=>"md5", // password encryption type in your db
'db_fields'=>"*", // enabling fetch for other db columns
);
$a = new Auth_HTTP("DB", $AuthOptions);
$a->setRealm('yourrealm'); // realm name
$a->setCancelText('<h2>Error 401</h2>'); // error message if authentication fails
$a->start(); // starting the authentication process
if($a->getAuth()) // checking for autenticated user
{
echo "Hello $a->username welcome to my secret page <BR>";
echo "Your details on file are: <BR>";
echo $a->getAuthData('userid'); // retriving other details from the database row
echo $a->getAuthData('telephone'); // in this example the user id, telephone number
echo $a->getAuthData('email'); // and email address
};
?>
boolean Auth_HTTP::getAuth (
)
Check if the user has been authenticated.
boolean
-
If the user has already been authenticated, the function returns
TRUE. Otherwise it returns FALSE.
This function can not be called statically.
string Auth_HTTP::getStatus (
)
This function returns the current status of PEAR::Auth. The return values are constants that are defined by PEAR Auth.
string
- possible values are
AUTH_IDLED, AUTH_EXPIRED,
AUTH_EXPIRED
This function can not be called statically.
void Auth_HTTP::start (
)
Start the authentication process.
This function can not be called statically.
Provides a framework for managing preferences in applications.
Supported features include:
Setting of default values.
Serialization of values before saving to the database.
Support for any database supported by DB
The first step is to setup a database to store the values in. For this tutorial it's assumed that you already know the basics of using the PEAR DB class.
To set up the default table layout run the following SQL statement:
SQL to setup the table
CREATE TABLE `preferences` ( `user_id` varchar( 255 ) NOT NULL default '', `pref_id` varchar( 32 ) NOT NULL default '', `pref_value` longtext NOT NULL , PRIMARY KEY ( `user_id` , `pref_id` ) );
To use Auth_PrefManager we must first create an instance of the base object, as shown below.
Setting up the object
<?php
require_once('Auth/PrefManager.php');
$dsn = 'mysql://user:password@localhost/database'; // Change the DSN to fit your database.
$options = array('serialize' => true); // Enable serialization of data before saving, this ensures that the values are retrieved cleanly.
$prefmanager = new Auth_PrefManager($dsn, $options); // Create the object.
?>
Now that we have a PrefManager object, we can make use of it to set some preferences.
For this tutorial we're going to allow users to specify their country, and assume that any user who hasn't set their country is somewhere on Earth.
First we need to set the default value, using setDefaultPref.
Setting up the object
<?php
// Continued from example 1.
$prefmanager->setDefaultPref("country", "Earth");
?>
Now that the default is set, we can create a (very) basic page, welcoming users with a customised message.
Currently this message will only ever display "Welcome to the people of Earth!", since no users have their country set.
Getting preferences
$username = "guest";
<h1>Welcome to the people of <?=$prefmanager->getPref($username, "country")?>!</h1>
Finally we need a way for people to choose which country they're in.
This is going to be done with a simple text box, and you should obviously be a little more careful in a real application about allowing users to set preferences for people!
The Reset Country button will delete the user's preference, and cause the default to be displayed again.
Setting preferences
<h1>Set Country</h1>
// Allow users to set their country and username.
if (isset($_POST['submit'])) {
$username = htmlspecialchars($_POST['username']);
$prefmanager->setPref($username, 'country', $_POST['country']);
} else if (isset($_POST['reset'])) {
$username = htmlspecialchars($_POST['username']);
$prefmanager->deletePref($username, 'country');
} else {
$username = 'guest';
}
?>
<h1>Welcome to the people of <?=$prefmanager->getPref($username, "country")?>!</h1>
<h2>Set Country And Username</h2>
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']) ?>">
<label for="username">Username</label> <input name="username" value="<?=$username?>" /><br/>
<label for="country">Country</label> <input name="country" value="<?=$prefmanager->getPref($username, 'country')?>"/><br/>
<input type="submit" name="submit" value="Set Country" /> <input type="submit" name="reset" value="Reset Country" />
</form>
Now once a user has entered their username, and their country, whenever they login they'll get a personalized welcome.
Sourcecode for the example page
<h1>Set Country</h1>
<?php
require_once('Auth/PrefManager.php');
// Create the PrefManager object.
// Change the DSN to fit your database.
$dsn = 'mysql://user:password@localhost/database';
// Enable serialization of data before saving, this ensures that the values are retrieved cleanly.
$options = array('serialize' => true);
// Create the object.
$prefmanager = new Auth_PrefManager($dsn, $options);
// Set the default value (this doesn't need to be done everytime the script is run).
$prefmanager->setDefaultPref("country", "Earth");
// Allow users to set their country and username.
if (isset($_POST['submit'])) {
$username = htmlspecialchars($_POST['username']);
$prefmanager->setPref($username, 'country', $_POST['country']);
} else if (isset($_POST['reset'])) {
$username = htmlspecialchars($_POST['username']);
$prefmanager->deletePref($username, 'country');
} else {
$username = 'guest';
}
?>
<h1>Welcome to the people of <?=$prefmanager->getPref($username, "country")?>!</h1>
<h2>Set Country And Username</h2>
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']) ?>">
<label for="username">Username</label> <input name="username" value="<?=$username?>" /><br/>
<label for="country">Country</label> <input name="country" value="<?=$prefmanager->getPref($username, 'country')?>"/><br/>
<input type="submit" name="submit" value="Set Country" /> <input type="submit" name="reset" value="Reset Country" />
</form>
bool constructor
Auth_PrefManager::Auth_PrefManager
(
string
$dsn
,
array
$properties = NULL
,
string
$defaultUser
)
The $properties
property should be an associative array, with the structure below. Any options not set will be set to the default.
'table'
The table to retrieve preferences from. [preferences]
'userColumn'
The field to use for matching user IDs. [user_id]
'nameColumn'
The field to use for matching preference names. [pref_name]
'valueColumn'
The field to retrieve preference values from. [pref_value]
'defaultUser'
The user ID to use for retrieving default values. [__default__]
'cacheName'
The key to use for the cache in $_SESSION
. [prefsCache]
'useCache'
Should values be cached for later use. [true]
'serialize'
Should values be serialized before saving to the database, and unserialized on retrieval. [false]
$dsn
The DSN of the database connection to make, or a DB object.
$properties
An array of properties to set.
$defaultUser
The default user to manage for.
returns Success or failure.
No exceptions thrown.
This function can not be called statically.
Users with preferences created using Auth_PrefManager 1.0.4 or earlier shouldn't enable the serialize option, as it may result in data loss.
void
Auth_PrefManager::clearCache
(
)
Clears the cache of preferences stored in the current user's session.
No exceptions thrown.
This function can not be called statically.
bool
Auth_PrefManager::deleteDefaultPref
(
string
$pref_id
)
Deletes the default value for the preference passed as $pref_id
.
$pref_id
The preference to delete.
returns Success/Failure
No exceptions thrown.
This function can not be called statically.
bool
Auth_PrefManager::deletePref
(
string
$user_id
,
string
$pref_id
)
Deletes the preference $pref_id
for $user_id
if one is set.
$user_id
The userid of the user to delete from.
$pref_id
The preference to delete.
returns Success/Failure
No exceptions thrown.
This function can not be called statically.
mixed
Auth_PrefManager::getDefaultPref
(
string
$pref_id
)
Retrieves the default value for $pref_id
, even if there is currently a user ID set for the object being used.
$pref_id
The name of the preference to get.
returns The value if it's found, or NULL if it isn't.
No exceptions thrown.
This function can not be called statically.
mixed
Auth_PrefManager::getPref
(
string
$user_id
,
string
$pref_id
,
bool
$showDefaults
= true
)
Retrieves the preference specified for a user, or, if returning default values is enabled, the default.
$user_id
The user to get the preference for.
$pref_id
The preference to get.
$showDefaults
Should default values be searched (overrides the global setting).
returns The value if it's found, or NULL if it isn't.
No exceptions thrown.
This function can not be called statically.
bool
Auth_PrefManager::setDefaultPref
(
string
$pref_id
,
mixed
$value
)
Sets the default value for $pref_id
, which will be retrieved if a user doesn't have the preference set.
$pref_id
The name of the preference to set.
$value
The value to set it to.
returns Success or failure.
No exceptions thrown.
This function can not be called statically.
bool
Auth_PrefManager::setPref
(
string
$user_id
,
string
$pref_id
,
mixed
$value
)
Sets the value for $pref_id
.
$user_id
The user to set for.
$pref_id
The preference to set.
$value
The value it should be set to.
returns Success or failure.
No exceptions thrown.
This function can not be called statically.
void
Auth_PrefManager::setReturnDefaults
(
mixed
$returnDefaults
= true
)
Set whether a default value should be returned by getPref() if no value was set for the specified user.
$returnDefaults
No exceptions thrown.
This function can not be called statically.
getDefaultPref() will always return the default value.
void
Auth_PrefManager::useCache
(
bool
$use
= true
)
Sets whether the caching system should be used or not.
If the cache is enabled then any values retrieved will be saved in the user's session to reduce load on the database.
$use
Should the cache be used.
No exceptions thrown.
This function can not be called statically.
Client implementation of RADIUS. This package contains wrapper classes for the RADIUS PECL extension. Provides RADIUS Authentication (RFC2865) and RADIUS Accounting (RFC2866).
Auth_RADIUS is a php wrapper around the php radius extension. If you are unfamiliar with the RADIUS protocol, the Wikipedia article provides an excellent introduction.
Auth_RADIUS is structured around the facilities built into the radius extension. Understanding Auth_RADIUS generally first requires a basic understanding of how the radius extension is designed. Documentation of the radius extension is available through the PHP manual at http://www.php.net/radius
There are different Classes for the different authentication methods available through RADIUS. If you are using CHAP-MD5 or MS-CHAP authentication, you will also need the Crypt_CHAP package. In addition, if using MS-CHAP authentication, you will need to enable the mhash and mcrypt extension in php.ini, or compile them in statically.
Note that if you have the latest radius extension (version 1.2 or newer), you will not need to enable the mcrypt extension.
Basic usage examples follow, pulled from the package itself.
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/*
Copyright (c) 2003, Michael Bretterklieber <michael@bretterklieber.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This code cannot simply be copied and put under the GNU Public License or
any other GPL-like (LGPL, GPL2) License.
$Id: intro.xml,v 1.4 2008-10-09 15:16:20 cweiske Exp $
*/
require_once 'Auth/RADIUS.php';
require_once 'Crypt/CHAP.php';
//$type = 'PAP';
//$type = 'CHAP_MD5';
$type = 'MSCHAPv1';
//$type = 'MSCHAPv2';
$username = 'sepp';
$password = 'sepp';
$classname = 'Auth_RADIUS_' . $type;
$rauth = new $classname($username, $password);
$rauth->addServer('localhost', 0, 'testing123');
$rauth->setConfigfile('/etc/radius.conf');
$rauth->username = $username;
switch($type) {
case 'CHAP_MD5':
case 'MSCHAPv1':
$classname = $type == 'MSCHAPv1' ? 'Crypt_CHAP_MSv1' : 'Crypt_CHAP_MD5';
$crpt = new $classname;
$crpt->password = $password;
$rauth->challenge = $crpt->challenge;
$rauth->chapid = $crpt->chapid;
$rauth->response = $crpt->challengeResponse();
$rauth->flags = 1;
// If you must use deprecated and weak LAN-Manager-Responses use this:
// $rauth->lmResponse = $crpt->lmChallengeResponse();
// $rauth->flags = 0;
break;
case 'MSCHAPv2':
$crpt = new Crypt_CHAP_MSv2;
$crpt->username = $username;
$crpt->password = $password;
$rauth->challenge = $crpt->authChallenge;
$rauth->peerChallenge = $crpt->peerChallenge;
$rauth->chapid = $crpt->chapid;
$rauth->response = $crpt->challengeResponse();
break;
default:
$rauth->password = $password;
break;
}
if (!$rauth->start()) {
printf("Radius start: %s<br>\n", $rauth->getError());
exit;
}
$result = $rauth->send();
if (PEAR::isError($result)) {
printf("Radius send failed: %s<br>\n", $result->getMessage());
exit;
} else if ($result === true) {
printf("Radius Auth succeeded<br>\n");
} else {
printf("Radius Auth rejected<br>\n");
}
// get attributes, even if auth failed
if (!$rauth->getAttributes()) {
printf("Radius getAttributes: %s<br>\n", $rauth->getError());
} else {
$rauth->dumpAttributes();
}
$rauth->close();
?>
Here is the second example:
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/*
Copyright (c) 2003, Michael Bretterklieber <michael@bretterklieber.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This code cannot simply be copied and put under the GNU Public License or
any other GPL-like (LGPL, GPL2) License.
$Id: intro.xml,v 1.4 2008-10-09 15:16:20 cweiske Exp $
*/
require_once 'Auth/RADIUS.php';
require_once 'Crypt/CHAP.php';
$username = 'sepp';
$starttime = time();
$racct = new Auth_RADIUS_Acct_Start;
$racct->addServer('localhost', 0, 'testing123');
$racct->username = $username;
// RADIUS_AUTH_RADIUS => authenticated via Radius
// RADIUS_AUTH_LOCAL => authenicated local
// RADIUS_AUTH_REMOTE => authenticated remote
$racct->authentic = RADIUS_AUTH_LOCAL;
$status = $racct->start();
if(PEAR::isError($status)) {
printf("Radius start: %s<br>\n", $status->getMessage());
exit;
}
// you can put any additional attributes here
// $racct->putAttribute(RADIUS_ACCT_INPUT_PACKETS, 45236);
// $racct->putAttribute(RADIUS_ACCT_OUTPUT_PACKETS, 1212);
$result = $racct->send();
if (PEAR::isError($result)) {
printf("Radius send failed: %s<br>\n", $result->getMessage());
exit;
} else if ($result === true) {
printf("Radius Acounting succeeded<br>\n");
} else {
printf("Radius Acounting rejected<br>\n");
}
$racct->close();
// Wait a bit, that we can put the session-time
sleep(2);
// send an accounting update
$racct = new Auth_RADIUS_Acct_Update;
$racct->addServer('localhost', 0, 'testing123');
$racct->username = $username;
$racct->session_time = time() - $starttime;
$status = $racct->start();
if(PEAR::isError($status)) {
printf("Radius start: %s<br>\n", $status->getMessage());
exit;
}
$result = $racct->send();
if (PEAR::isError($result)) {
printf("Radius send failed: %s<br>\n", $result->getMessage());
exit;
} else if ($result === true) {
printf("Radius Acounting succeeded<br>\n");
} else {
printf("Radius Acounting rejected<br>\n");
}
// Wait a bit, that we can put the session-time
sleep(2);
// send the stop
$racct = new Auth_RADIUS_Acct_Stop;
$racct->addServer('localhost', 0, 'testing123');
$racct->username = $username;
$racct->session_time = time() - $starttime;
$status = $racct->start();
if(PEAR::isError($status)) {
printf("Radius start: %s<br>\n", $status->getMessage());
exit;
}
// you can put any additional attributes here
// $racct->putAttribute(RADIUS_ACCT_TERMINATE_CAUSE, RADIUS_TERM_SESSION_TIMEOUT);
$result = $racct->send();
if (PEAR::isError($result)) {
printf("Radius send failed: %s<br>\n", $result->getMessage());
exit;
} else if ($result === true) {
printf("Radius Acounting succeeded<br>\n");
} else {
printf("Radius Acounting rejected<br>\n");
}
$racct->close();
?>
LiveUser is an advanced authentication and permission framework that comes with a large array of out of the box features which can be used optionally. In short, the LiveUser package, provides Access Control List (ACL) functionality. The base class is called LiveUser and is often referred to as the "client" and is what will be commonly be used to authenticate a specific user and then optionally make permission checks on this user.
LiveUser provides the following key features: The LiveUser class takes care of the login process and can be configured to use a certain permission container and one or more different auth containers. That means, you can have your users' data scattered amongst many data containers and have the LiveUser class try each defined container until the user is found. For example, you can have all website users who can apply for a new account online on the webserver's local database. Also, you want to enable all your company's employees to login to the site without the need to create new accounts for all of them. To achieve that, a second container can be defined to be used by the LiveUser class.
You can also define a permission container of your choice that will manage the rights for each user. Depending on the container, you can implement any kind of permission schemes for your application while having one consistent API.
Using different permission and auth containers, it's easily possible to integrate newly written applications with older ones that have their own ways of storing permissions and user data. Just make a new container type and you're ready to go!
Currently available are containers using: PEAR::DB, PEAR::MDB, PEAR::MDB2, ext/PDO, PEAR::XML_Tree, Session and PEAR::Auth.
There is also an external wiki dedicated to LiveUser:
At this point LiveUser is still in beta stage. Even so LiveUser is already being used on a wide range of production websites. A non exhaustive list can be found here.
The current roadmap for LiveUser can be viewed here.
A detailed introduction to the core concepts of LiveUser can be found in the following phpmag article.
This article was published in april 2004 and is therefore not entirely uptodate. However the core principles remain unchanged and most of the recent changes resolve around making the API more consistent and flexible. As a result the API examples are mostly out of date. The article also fails to mention the fact that the permission containers are now separated in a logic and a storage layer. This was done to remove logic duplication as well as to allow the implementation of a stackable storage approach, where users could configure LiveUser to first call a cache storage layer, which in turn would fetch the necessary data on demand from the actual storage layer.
A more recent article providing a step by step guide to LiveUser and the different permission containers can be found here.
Before you can use LiveUser you will need to setup the necessary data structures for the chosen container. Through the use of the configuration options it is possible to customize alot of the aspects of the storage layer. Most notably it is possible to alias field and table names. This should make it possible to integrate any legacy data into LiveUser. Depending on the container chosen you may have to use the database schema installer. The installer requires the MDB2_Schema and MDB2 packages and is able to handle most configuration options properly to be able to install the database schema directly into your database. You can find the install class in "[PEAR]/data/misc/schema/install.php". There is some sample code which is partially commented out at the top. Basically it's a two step process for both the auth and perm: (1) generate the schema xml file, (2) install the schema.
During the installation process the installer will create backup files of the installed schema. These files will enable the installer to attempt to alter the database if run again with a different configuration. However if these files exist the installer will always attempt to alter instead of creating the tables from scratch as long as you are using the sam DSN. If for some reason you need to create the tables from scratch again then please delete the backup files with the matching DSN. You can find the installer inside the data directory of your pear install directory. Its called install.php and at the top of the file you will find a number of sample API calls which are commented out.
An ER diagram of the database structure can be found here.
The diagram details what tables are needed for what permission complexity level. If you want to prevent the installer from installing tables you don't need you can modify the "tables" property of the instance of the permission container you pass to the generateSchema() method in the installer.
In order to get started with LiveUser the following two articles should help in getting off the ground.
The observer support in LiveUser allows users to automatically have LiveUser call certain callbacks on a number of internal events. It is documented here.
LiveUser also ship with a wide range of examples. These will be installed into the "docs" directory in your PEAR install directory. They try to illustrate various usage scenarios. The database examples come with a schema file please see the demodata.php in the examples root folder of the LiveUser package for details on how to install these schema files from the command line or from a browser. You will once again need MDB2_Schema to be able to install the schema files.
LiveUser package:
Getting the LiveUser examples working can be somewhat difficult for
developers who are new to PHP and PEAR.
Here is a list of steps necessary to get the example4
and GVN examples working with a MySQL database in LiveUser
0.16.12
.
go-pear.php
to
your web host through FTP.
LiveUser
,
MDB2
, MDB2_Driver_mysql
,
HTML_Template_IT
, HTML_QuickForm
,
and Var_Dump
.
Command line will be something like
pear install --alldeps LiveUser-beta (repeat for each package).
If you used the go-pear.php
, there's some web-based
way to install packages through that, but I haven't used it.
example4
to your web root.
On my Linux system the examples ended up in
/usr/share/pear/docs/LiveUser/docs/examples
and my Apache web root is at /var/www
. You can find
out where the examples are by executing
pear list-files LiveUser.
demodata.php
through your web browser with a URL like
http://www.example.com/LU/demodata.php?dsn=mysql://dbuser:pwd@localhost/databasename&file=/var/www/LU/demodata.xml&create=1
.
You must replace the file path values and the database
user
, password
,
host
, and database name
with values
appropriate for your own system.
If this gives you a "success" message and you have phpMyAdmin or something,
you should be able to look at the database now and see tables with data
in them.
It is reported that on some systems, you must make alterations to the
demodata.xml
file before this will run properly. Within all <is_active>..</is_active> tags replace the value "Y
" with "1
" and "N
" with "0
".
conf.php
file and enter your database information.
index.html
page through a web browser and log in and use example4
.
LiveUser/LiveUser.php
had to be corrected to just be
LiveUser.php
.
Auth_XML.xml
from
example4
into the GVN folder.
example4/conf.php
into GVN's
conf.php
and
conf_admin.php
files.
peoples
table change the values in the
isactive
column to 1
.
The above steps got me to the point where I have the working sample app and pages that demonstrate adding users, etc.
LiveUser_Admin class which provides various ways to manage all relevant data used in LiveUser, like users, groups/roles, rights and their relations.
LiveUser_Admin provides the following key features: It can associate data from multiple authentication containers with a single permission container. Using different permission and auth containers, it's easily possible to integrate newly written applications with older ones that have their own ways of storing permissions and user data. Just make a new container type and you're ready to go! Currently available are containers using: PEAR::DB, PEAR::MDB, PEAR::MDB2, ext/PDO. LiveUser_Admin provides the following key features You'll be able to add/edit/delete/get things like:
There is also an external wiki dedicated to LiveUser.
At this point LiveUser_Admin is still in beta stage. Even so LiveUser_Admin is already being used on a wide range of production websites. A non exhaustive list can be found here.
The current roadmap for LiveUser_Admin can be viewed here.
A detailed introduction to the core concepts of LiveUser can be found in the following phpmag article: http://web.archive.org/web/20071218224817/http://www.php-mag.net/magphpde/magphpde_article/psecom,id,595,nodeid,21.html
This article was published in april 2004 and is therefore not entirely uptodate. The article also does not mention the SQL generator used in the LiveUser_Admin package. This feature has made the API much more flexible since now the entire storage schema is configurable and extensible. It also means that more or less any sort of filtering can be applied with on demand joining. On overview of this API can be found here: http://wiki.pooteeweet.org/LiveUser/AdminFilters
A more recent article providing a step by step guide to LiveUser and the different permission containers can be found here.
An ER diagram of the database structure can be found here.
The diagram details what tables are needed for what permission complexity level. If you want to prevent the installer from installing tables you don't need you can modify the "tables" property of the instance of the permission container you pass to the generateSchema() method in the installer.
In order to get started with LiveUser the following two articles should help in getting off the ground.
LiveUser_Admin also ship with a wide range of examples. These will be installed into the "docs" directory in your PEAR install directory. They try to illustrate various usage scenarios. The database examples come with a schema file please see the demodata.php in the examples root folder of the LiveUser package for details on how to install these schema files from the command line or from a browser. You will once again need MDB2_Schema to be able to install the schema files.
LiveUser_Admin package:
Provides Packages for Benchmarking and Profiling
Provides a simple framework for profiling and benchmarking your PHP applications.
Benchmark is a simple framework that allows you time, profile and benchmark your PHP scripts and applications. Benchmark provides you with three classes to enable you to profile: Benchmark_Timer, Benchmark_Profiler and Benchmark_Iterate.
The package is very easy to use. We'll discuss each of the three classes in detail, but some concepts are common. You basically instantiate one of the classes, begin your script (or portion of code that you want to profile) and analyze the results afterward.
All the three classes have two modes of operation: automatic and manual. In the automatic mode of operation, the profiling is automatically started and stopped, and results displayed. In the manual mode, you are responsible for starting, stopping and displaying the results.
The automatic mode makes sense in cases where you want to quickly test a fixed portion of code like a script or a single function. The manual mode offers you more control, which means arbitrary portions of code can be profiled, and the results can be carefully analyzed before being displayed. In the automatic mode of operation, the results are always displayed; while in the manual mode you have the option of logging them or storing them in a database.
If you wish to operate a class in automatic mode, simply pass the TRUE parameter to its constructor. Instantiating a class without a parameter or with the FALSE parameter sets the mode of operation to manual for that class.
Benchmark_Timer is the most fundamental class in the package. It performs the simple function of recording the amount of time it takes for a fixed amount of code to execute.
Benchmark_Profiler behaves just like Benchmark_Timer with the exception that you are allowed to specify sections between the start and stop times, at which point the timing details are recorded. This provides the ability to nest timers and profile functions which are called multiple times.
Benchmark_Iterate is a class that runs a particular function several times over and records the amount of time it took for each iteration to execute.
As mentioned before, this class simply records the time taken for a particular snippet of code to execute. In the automatic mode, this means the code remaining after the class's instantiation. In manual mode, the snippet of code to be timed is specified between calls to the start() and stop() methods of the class.
Additionally, you may specify markers between the snippet of code to be timed. The timing information is recorded wherever markers are defined.
An example will make this clear.
Benchmark_Timer in automatic mode
<?php
require_once "Benchmark/Timer.php";
$timer = new Benchmark_Timer(TRUE);
// Timer is in automatic mode, so timing starts here.
$j = array();
for ($i = 0; $i < 100; $i++) {
$j[] = $i;
if ($i == 49)
$timer->setMarker('Midway');
}
// Timer automatically stops and results are displayed.
?>
The example above will generate an output as shown below when run with the PHP CLI SAPI. Running it with the Apache SAPI will produce the same results, but formatted as HTML.
Results of a test run of Benchmark_Timer
---------------------------------------------------- marker time index ex time perct ---------------------------------------------------- Start 1182558324.48638900 - 0.00% ---------------------------------------------------- Midway 1182558324.48655800 0.000169 62.13% ---------------------------------------------------- Stop 1182558324.48666100 0.000103 37.87% ---------------------------------------------------- total - 0.000272 100.00% ----------------------------------------------------
In most cases however, you would like to do something with that information. When you run the timer in manual mode, you can obtain the results as an associative array using the getProfiling() method. To display the information (as shown above) use the display() method. More information on the methods that Benchmark_Timer implements is available in the API Documentation.
This class behaves mostly like Benchmark_Timer with the exception that you are allowed to specify the beginning and end of sections within the code to be profiled. sections are different from markers (markers) don't have ends in the sense that they are stateful and remember how many times they have been entered.
Timing information is recorded at the beginning and end of the code to be profiled and at the beginning and end of every section everytime it is encountered. An example follows.
Benchmark_Profiler in manual mode
<?php
require_once 'Benchmark/Profiler.php';
$profiler = new Benchmark_Profiler;
function wasteTime() {
$j = 2;
for ($i = 0; $i < 100; $i++) {
$j = $j * 10;
}
return;
}
function myFunction() {
global $profiler;
$profiler->enterSection('myFunction');
wasteTime();
$profiler->leaveSection('myFunction');
return;
}
// Manual mode, so start manually
$profiler->start();
wasteTime();
myFunction();
myFunction();
wasteTime();
// Manual mode, stop and display results
$profiler->stop();
$profiler->display();
?>
The example above will generate an output as shown below when run with the PHP CLI SAPI. Running it with the Apache SAPI will produce the same results, but formatted as HTML.
Results of a test run of Benchmark_Profiler
------------------------------------------------------------------------------------ Section Total Ex Time Netto Ex Time #Calls Percentage ------------------------------------------------------------------------------------ myFunction 5.8174133300781E-5 5.8174133300781E-5 2 17.22% Global 0.00033783912658691 0.00027966499328613 1 100.00%
As you can see, the default output may not be too helpful. You can obtain detailed section by section profiling information in manual mode through methods like getSectionInformations() and getAllSectionsInformations(). Refer to the API documentation for more information.
This class is useful when you want to iterate over a single piece of code (a function) several times over. Timing information is recorded for every iteration. An example follows.
Benchmark_Iterate in manual mode
<?php
require_once 'Benchmark/Iterate.php';
$benchmark = new Benchmark_Iterate;
function calculation($num) {
$x = sin($num) * 100;
$y = tan($x);
return;
}
$benchmark->run(100, 'calculation', 45.6);
$result = $benchmark->get();
print_r($result);
?>
The example above will generate an output as shown below when run with the PHP CLI SAPI.
Results of a test run of Benchmark_Iterate
Array ( [1] => 0.000073 [2] => 0.000014 [3] => 0.000011 [4] => 0.000011 [5] => 0.000011 ..93 more results.. [99] => 0.000011 [100] => 0.000011 [mean] => 0.000011 [iterations] => 100 )
You can display formatted results either by running the script in automatic mode or by using the display() method. For more information of the methods this class implements, refer to the API documentation.
Provides Packages for creating caches
Cache_Lite provides a fast, light and safe cache system. It's optimized for file containers and protected against cache corruptions (because it uses file locking and/or hash tests).
PEAR::Cache_Lite is a little cache system. It's optimized for high traffic websites so it is really fast and safe (because it uses file locking and/or anti-corruption tests).
Note : An independent documentation of Cache_Lite is available in chinese on this page.
Before all, PEAR::Cache_Lite has to be extremely fast. That returns the use of PEAR possible on sites with high traffic without falling in hi-level hardware solutions.
You can find more details about cache_lite technical choices on this document, but the main idea is to include PEAR.php file ONLY when an error occurs (very rare).
Because the cache system is often embedded in the application layer, PEAR::Cache_Lite kernel has to be small and flexible with an adapted licence (LGPL). Advanced functionality can be found in files that extend the core.
On high traffic websites, the cache system has to be really protected against cache files corruptions (because of concurrent accesses in read/write mode). Very few cache systems offer a protection against this problem.
File locking is not a perfect solution because it is useless with NFS or multithreaded servers. So in addition to it, PEAR::Cache_Lite offers two fully transparent mechanisms (based on hash keys) to guarantee the security of the data in all circumstances. Just have a look at the link given in the previous paragraph for more details.
Every module of Cache_Lite follows the same philosophy.
Parameters (others than default ones) are passed to the constructor by using an associative array.
A cache file is identified by a cache ID (and eventually a group). For obvious flexibility reasons, the logic of IDs choice is left to the developer.
In the following, we will use the term "group" as a pool of cache files and the term "block" as a piece of an HTML page.
Let's start with a simple example : the page is built, then recovered with a unique variable (string):
<?php
// Include the package
require_once('Cache/Lite.php');
// Set a id for this cache
$id = '123';
// Set a few options
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 3600
);
// Create a Cache_Lite object
$Cache_Lite = new Cache_Lite($options);
// Test if thereis a valide cache for this id
if ($data = $Cache_Lite->get($id)) {
// Cache hit !
// Content is in $data
// (...)
} else { // No valid cache found (you have to make the page)
// Cache miss !
// Put in $data datas to put in cache
// (...)
$Cache_Lite->save($data);
}
?>
If you wish use a cache per block and not a global cache, take as example the following script:
<?php
require_once('Cache/Lite.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 3600
);
// Create a Cache_Lite object
$Cache_Lite = new Cache_Lite($options);
if ($data = $Cache_Lite->get('block1')) {
echo($data);
} else {
$data = 'Data of the block 1';
$Cache_Lite->save($data);
}
echo('<br><br>Non cached line !<br><br>');
if ($data = $Cache_Lite->get('block2')) {
echo($data);
} else {
$data = 'Data of the block 2';
$Cache_Lite->save($data);
}
?>
However, it is not always possible to recover all the contents of a page in a single string variable. Thus Cache_Lite_Output comes to our aid :
<?php
require_once('Cache/Lite/Output.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 10
);
$cache = new Cache_Lite_Output($options);
if (!($cache->start('123'))) {
// Cache missed...
for($i=0;$i<1000;$i++) { // Making of the page...
echo('0123456789');
}
$cache->end();
}
?>
The idea is the same for a per block usage :
<?php
require_once('Cache/Lite/Output.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 10
);
$cache = new Cache_Lite_Output($options);
if (!($cache->start('block1'))) {
// Cache missed...
echo('Data of the block 1 !');
$cache->end();
}
echo('Non cached line !');
if (!($cache->start('block2'))) {
// Cache missed...
echo('Data of the block 2 !');
$cache->end();
}
?>
For a maximum efficiency with Cache_Lite, do not systematically include every package the page needs. ONLY load the modules you need when the page is not in the cache (and has to be recomputed) by using a conditional inclusion.
The BAD way :
<?php
require_once("Cache/Lite.php");
require_once("...")
require_once("...")
// (...)
$cache = new Cache_Lite();
if ($data = $Cache_Lite->get($id)) { // cache hit !
echo($data);
} else { // page has to be (re)constructed in $data
// (...)
$Cache_Lite->save($data);
}
?>
Here is the good way (often more than twice faster) :
<?php
require_once("Cache/Lite.php");
// (...)
$cache = new Cache_Lite();
if ($data = $Cache_Lite->get($id)) { // cache hit !
echo($data);
} else { // page has to be (re)constructed in $data
require_once("...")
require_once("...")
// (...)
$Cache_Lite->save($data);
}
?>
To go further with Cache_Lite, have a look at examples and technical details bundled with the package.
void constructor Cache_Lite::Cache_Lite (
array $options = array(NULL)
)
The constructor of the Cache_Lite core class. You can give an associative array as an argument to set a lot of options.
$options
associative array to set a lot of options
Option | Data Type | Default Value | Description |
---|---|---|---|
cacheDir | string | /tmp/ | directory where to put the cache file (with a trailing slash at the end) |
caching | boolean | TRUE | enable / disable caching |
lifeTime | integer | 3600 | cache lifetime in seconds (since 1.6.0beta 1, you can use a null value for an eternal cache lifetime) |
fileLocking | boolean | TRUE | enable / disable fileLocking. Can avoid cache corruption under bad circumstances. |
writeControl | boolean | TRUE | enable / disable write control. Enable write control will lightly slow the cache writing but not the cache reading. Write control can detect some corrupt cache files but maybe it's not a perfect control. |
readControl | boolean | TRUE | enable / disable read control. If enabled, a control key is embeded in cache file and this key is compared with the one calculated after the reading |
readControlType | string | crc32 | Type of read control (only if read control is enabled). Must be 'md5' (for a md5 hash control (best but slowest)), 'crc32' (for a crc32 hash control (lightly less safe but faster)) or 'strlen' (for a length only test (fastest)) |
pearErrorMode | integer | CACHE_LITE_ERROR_RETURN | pear error mode (when raiseError is called) (CACHE_LITE_ERROR_RETURN for just returning a PEAR_Error object or CACHE_LITE_ERROR_DIE for immediate stop of the script (good for debug) ) |
fileNameProtection | boolean | TRUE | file Name protection (if set to true, you can use any cache id or group name, if set to false, it can be faster but cache ids and group names will be used directly in cache file names so be careful with special characters...) |
automaticSerialization | boolean | FALSE | enable / disable automatic serialization (allows non-string data to be saved, with a small performance decrease) |
memoryCaching | boolean | FALSE | enable / disable "Memory Caching" (NB : there is no lifetime for memory caching, only the end of the script) |
onlyMemoryCaching | boolean | FALSE | enable / disable "Only Memory Caching" (if enabled, files are not used anymore) |
memoryCachingLimit | integer | 1000 | max number of records to store into memory caching |
automaticCleaningFactor | integer | 0 | Disable / Tune the automatic cleaning process. The automatic cleaning process destroy too old (for the given life time) cache files when a new cache file is written. 0 means "no automatic cache cleaning", 1 means "systematic cache cleaning" (slow), x>1 means "automatic cleaning randomly 1 times on x cache writes". A value between 20 and 200 is maybe a good start. |
hashedDirectoryLevel | integer | 0 | Set the hashed directory structure level. 0 means "no hashed directory structure", 1 means "one level of directory", 2 means "two levels"... This option can speed up Cache_Lite only when you have many thousands of cache file. Only specific benchs can help you to choose the perfect value for you. Maybe, 1 or 2 is a good start. |
errorHandlingAPIBreak | boolean | FALSE | If set to true, it introduces a little API break but the error handling is better in CACHE_LITE_ERROR_RETURN mode (especially with the save() method which can return a PEAR_Error object). |
No exceptions thrown.
This function can not be called statically.
Using most common options
<?php
require_once "Cache/Lite.php";
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 7200,
'pearErrorMode' => CACHE_LITE_ERROR_DIE
);
$cache = new Cache_Lite($options);
?>
string Cache_Lite::get (
string $id
, string $group = 'default'
, boolean $doNotTestCacheValidity
= false
)
One of the main method of Cache_Lite : test the validity of a cache file and return it if it's available (FALSE else)
$id
cache id
$group
name of the cache group
$doNotTestCacheValidity
if set to TRUE, the cache validity won't be tested
returns data of the cache (or false if no cache available)
This function can not be called statically.
Usage
<?php
require_once "Cache/Lite.php";
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 7200,
'pearErrorMode' => CACHE_LITE_ERROR_DIE
);
$cache = new Cache_Lite($options);
if ($data = $cache->get('id_of_the_page')) {
// Cache hit !
// Content is in $data
// (...)
} else {
// No valid cache found (you have to make and save the page)
// (...)
}
?>
boolean Cache_Lite::save (
string $data
, string $id = NULL
, string $group = 'default'
)
save the given data (which has to be a string if automaticSerialization is set to FALSE (default)).
$data
data to put in a cache file (can be another type than strings if automaticSerialization is TRUE).
$id
cache id
$group
name of the cache group
returns true if no problem
This function can not be called statically.
Usage
<?php
require_once "Cache/Lite.php";
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 7200,
'pearErrorMode' => CACHE_LITE_ERROR_DIE
);
$cache = new Cache_Lite($options);
if ($data = $cache->get('id_of_the_page')) {
// Cache hit !
// Content is in $data
echo $data;
} else {
// No valid cache found (you have to make and save the page)
$data = '<html><head><title>test</title></head><body><p>this is a test</p></body></html>';
echo $data;
$cache->save($data);
}
?>
boolean Cache_Lite::remove (
string $id
, string $group = 'default'
)
remove the given cache file (specified with its id and group)
$id
cache id
$group
name of the cache group
returns true if no problem
This function can not be called statically.
Usage
<?php
require_once "Cache/Lite.php";
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 7200,
'pearErrorMode' => CACHE_LITE_ERROR_DIE
);
$cache = new Cache_Lite($options);
$cache->remove('id_of_the_page');
if ($data = $cache->get('id_of_the_page')) {
// Cache hit !
// [IMPOSSIBLE !]
} else {
// No valid cache found (you have to make and save the page)
$data = '<html><head><title>test</title></head><body><p>this is a test</p></body></html>';
$cache->save($data);
}
?>
This is a dummy example because the cache is destroyed at the beginning of the script ! So the first case of the if statement is impossible.
boolean Cache_Lite::clean (
string
$group
= false
,
string
$mode = 'ingroup';
)
if no group is specified all cache files will be destroyed ; else only cache files of the specified group will be destroyed
$group
name of the cache group
$mode
flush cache mode : 'old' (clean too old cache files for the current lifetime), 'ingroup' (clean all cache files for the given group), 'notingroup' (clean all the cache files except for the given group), [since 1.5.0 beta] 'callback_myFunc' (call the function 'myFunc' with the complete path file and the group in parameters, if the callback return 'true', the cache file is deleted).
returns true if no problem
This function can not be called statically.
Usage
<?php
require_once 'Cache/Lite.php';
$options = array('cacheDir' => '/tmp/');
$cache = new Cache_Lite($options);
$cache->clean();
?>
This example cleans all of the cache files.
void Cache_Lite::setToDebug (
)
when an error is found, the script will stop and the message will be displayed (in debug mode only) ; same effect than pearErrorMode option in the constructor
This function can not be called statically.
void Cache_Lite::setLifeTime (
int $newLifeTime
)
change the cache life time
$newLifeTime
new life time (in seconds)
throws no exceptions thrown
This function can not be called statically.
void Cache_Lite::saveMemoryCachingState (
string $id
, string $group = 'default'
)
save the current state of the memory caching array into a classical cache file
$id
cache id
$group
name of the cache group
This function can not be called statically.
void Cache_Lite::getMemoryCachingState (
string $id
, string $group = 'default'
, bool $doNotTestCacheValidity
= false
)
load a previously saved state of a memory caching array from a classical cache file
$id
cache id
$group
name of the cache group
$doNotTestCacheValidity
if set to TRUE, the cache validity won't be tested
This function can not be called statically.
int Cache_Lite::lastModified (
)
[ONLY FOR CACHE_LITE HACKERS] return the cache last modification time
returns last modification time
This function can not be called statically.
void
Cache_Lite::raiseError
(
string $msg
, int $code
)
include dynamically the PEAR.php file and trigger a PEAR error
To improve performances, the PEAR.php file is included dynamically. The file is so included only when an error is triggered. So, in most cases, the file isn't included and perfs are much better.
$msg
error message
$code
error code
This function can not be called statically.
void Cache_Lite::extendLife (
)
[since Cache_Lite-1.7.0beta2] Extend the life of an existent cache file. The cache file is "touched", so it starts a new lifetime period. See this feature request for more details.
No exceptions thrown.
This function can not be called statically.
Classical use
<?php
require_once "Cache/Lite.php";
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 7200,
'pearErrorMode' => CACHE_LITE_ERROR_DIE
);
$cache = new Cache_Lite($options);
$id = 'foo';
if (!($data = $cache->get($id))) {
// the cache is not hit !
$data = '123456789';
$cache->save($data);
} else {
// the cache is hit !
if (isset($_GET['extend'])) {
$cache->extendLife();
}
}
echo($data);
?>
void constructor Cache_Lite_Output::Cache_Lite_Output (
array $options
)
The constructor of the Cache_Lite_Output class. You can give an associative array as an argument to set a lot of options.
$options
associative array to set a lot of options (see Cache_Lite constructor for details)
This function can not be called statically.
boolean Cache_Lite_Output::start (
string $id
, string $group = 'default'
, boolean $doNotTestCacheValidity
= false
)
Test if a cache is available and (if yes) return it to the browser. Else, the output buffering is activated.
$id
cache id
$group
name of the cache group
$doNotTestCacheValidity
if set to TRUE, the cache validity won't be tested
returns true if the cache is hit (false else)
This function can not be called statically.
classical using
<?php
require_once "Cache/Lite/Output.php";
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 7200,
'pearErrorMode' => CACHE_LITE_ERROR_DIE
);
$cache = new Cache_Lite_Output($options);
if (!($cache->start('id_of_the_page'))) {
// Cache not hit !
// All the output is bufferised until the end() method
// (...)
$cache->end();
}
?>
void Cache_Lite_Output::end (
)
Stop the output buffering started by the start() method and store the output to a cache file
This function can not be called statically.
Usage
<?php
require_once "Cache/Lite/Output.php";
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 7200,
'pearErrorMode' => CACHE_LITE_ERROR_DIE
);
$cache = Cache_Lite_Output($options);
if (!($cache->start('id_of_the_page'))) {
// Cache not hit !
// All the output is bufferised until the end() method
// (...)
$cache->end(); // the bufferised output is now stored into a cache file
}
?>
void constructor Cache_Lite_Function::Cache_Lite_Function (
array $options = array(NULL)
)
The constructor of the Cache_Lite_Output class. You can give an associative array as an argument to set a lot of options.
$options
associative array to set a lot of options (see Cache_Lite constructor for details). Be careful, with Cache_Lite_Function, additional options are available (comparatively to Cache_Lite). There are described on the following table.
Option | Data Type | Default Value | Description |
---|---|---|---|
[...] | [...] | [...] | See Cache_Lite constructor for more options |
defaultGroup | string | Cache_Lite_Function | default cache group for function caching |
debugCacheLiteFunction | boolean | FALSE | debug the caching process |
dontCacheWhenTheOutputContainsNOCACHE | boolean | FALSE | Don't cache the method call when its output contains the string "NOCACHE". If set to true, the output of the method will never be displayed (because the output is used to control the cache) |
dontCacheWhenTheResultIsFalse | boolean | FALSE | Don't cache the method call when its result is false |
dontCacheWhenTheResultIsNull | boolean | FALSE | Don't cache the method call when its result is null |
This function can not be called statically.
mixed Cache_Lite_Function::call (
string$functionName
, mixed$arg1
, mixed$arg2
, mixed$arg3
, mixed...
)
call the given function with given arguments only if there is no cache about it ; else, the output of the function is read from the cache then send to the browser and the return value if read also from the cache and returned.
returns result of the function/method
This function can not be called statically.
classical using with a function
<?php
require_once('Cache/Lite/Function.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 3600
);
$cache = new Cache_Lite_Function($options);
$cache->call('function_to_bench', 12, 45);
function function_to_bench($arg1, $arg2)
{
echo "This is the output of the function function_to_bench($arg1, $arg2) !<br>";
return "This is the result of the function function_to_bench($arg1, $arg2) !<br>";
}
?>
classical using with a method
<?php
require_once('Cache/Lite/Function.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 3600
);
$cache = new Cache_Lite_Function($options);
$obj = new bench();
$obj->test = 666;
$cache->call('obj->method_to_bench', 12, 45);
class bench
{
var $test;
function method_to_bench($arg1, $arg2)
{
echo "\$obj->test = $this->test and this is the output of the method \$obj->method_to_bench($arg1, $arg2) !<br>";
return "\$obj->test = $this->test and this is the result of the method \$obj->method_to_bench($arg1, $arg2) !<br>";
}
}
?>
If you try to use Cache_Lite_Function with $this object ($cache->call('this->method',...) for example), have a look first at the last example of this page.
classical using with a static method
<?php
require_once('Cache/Lite/Function.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 3600
);
$cache = new Cache_Lite_Function($options);
$cache->call('bench::static_method_to_bench', 12, 45);
class bench
{
var $test;
function static_method_to_bench($arg1, $arg2) {
echo "This is the output of the function static_method_to_bench($arg1, $arg2) !<br>";
return "This is the result of the function static_method_to_bench($arg1, $arg2) !<br>";
}
}
?>
another using with a method (how to use cache $this->method() calls)
<?php
// Since Cache_Lite-1.7.0beta2
require_once('Cache/Lite/Function.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 3600
);
$obj = new foo($options);
class foo
{
var $test;
function foo($options)
{
$cache = new Cache_Lite_Function($options);
$this->test = 'foobar';
$cache->call(array(&$this, 'method_bar'), 12, 45);
}
function method_bar($arg1, $arg2)
{
echo "\$this->test = $this->test and this is the output of the method \$this->method_bar($arg1, $arg2) !<br>";
return "\$this->test = $this->test and this is the result of the method \$this->method_bar($arg1, $arg2) !<br>";
}
}
?>
So, for method calls, the best way is to use array(&$object, 'nameOfTheMethod') as first argument instead of '$object->nameOfTheMethod' which doesn't work with "$this" for example.
boolean Cache_Lite_Function::drop (
string$functionName
, mixed$arg1
, mixed$arg2
, mixed$arg3
, mixed...
)
[since Cache_Lite 1.6.0beta1] Drop the cache file of the corresponding function call with given arguments.
returns true if ok
This function can not be called statically.
classical using with a function
<?php
require_once('Cache/Lite/Function.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 3600
);
$cache = new Cache_Lite_Function($options);
$cache->call('function_to_bench', 12,45);
$cache->call('function_to_bench', 12,45);
// clean the corresponding cache file
$cache->drop('function_to_bench', 12, 45);
function function_to_bench($arg1, $arg2)
{
echo "This is the output of the function function_to_bench($arg1, $arg2) !<br>";
return "This is the result of the function function_to_bench($arg1, $arg2) !<br>";
}
?>
void constructor Cache_Lite_File::Cache_Lite_File (
array $options
)
The constructor of the Cache_Lite_File class. You can give an associative array as an argument to set a lot of options.
$options
associative array to set a lot of options (see Cache_Lite constructor for details). Be careful, with Cache_Lite_File, there is an additional "mandatory option" described on the following table.
Option | Data Type | Default Value | Description |
---|---|---|---|
[...] | [...] | [...] | See Cache_Lite constructor for more options |
masterFile [REQUIRED] | string | '' | complete path of the file used for controlling the cache lifetime |
This function can not be called statically.
string Cache_Lite_File::get (
string $id
, string $group = 'default'
)
test the validity of a cache file and return it if it's available (FALSE else)
$id
cache id
$group
name of the cache group
returns data of the cache (or false if no cache available)
This function can not be called statically.
Usage
<?php
require_once "Cache/Lite/File.php";
$options = array(
'cacheDir' => '/tmp/',
'masterFile' => '/home/web/config.xml',
'pearErrorMode' => CACHE_LITE_ERROR_DIE
);
$cache = new Cache_Lite_File($options);
if ($data = $cache->get('id_of_the_page')) {
// Cache hit !
// Content is in $data
// (...)
} else {
// No valid cache found (you have to make and save the page)
// (...)
}
?>
Provides Packages for managing configurations
The Config package provides methods for configuration manipulation.
Config helps you manipulate your configuration whether they are stored in XML files, PHP arrays or other kind of datasources. It supports these features:
The Config object acts as a container for other Config_Container objects. It doesn't do much but makes handling IO operations easier. It contains the root Config_Container object which in turn contains a child Config_Container object. Config_Container objects store references to their parent and have an array of children. This structure makes it easy to access the different containers and their contents.
A Config_Container object can be of different type:
When using the Config package, most of the work is done with Config_Container objects.
An example that will create a new Config_Container
<?php
// initialize a Config_Container object
require_once('Config.php');
$conf =& new Config_Container('section', 'conf');
$conf_DB =& $conf->createSection('DB');
$conf_DB->createDirective('type', 'mysql');
$conf_DB->createDirective('host', 'localhost');
$conf_DB->createDirective('user', 'root');
$conf_DB->createDirective('pass', 'root');
// set this container as our root container child in Config
$config = new Config();
$config->setRoot($conf);
// write the container to a php array
$config->writeConfig('/tmp/config_test.php', 'phparray',
array('name' => 'test'));
// print the content of our conf section to screen
echo $conf->toString('phparray', array('name' => 'test'));
?>
The above example illustrates how Config and Config_Container can interact. There are other ways. You could have for example first created the Config object and then used $config->getRoot() to add sections and directives to the returned object reference.
Reading configuration from an XML file
<?php
require_once 'Config.php';
$conf = new Config;
$root =& $conf->parseConfig('config.xml', 'XML');
if (PEAR::isError($root)) {
die('Error while reading configuration: ' . $root->getMessage());
}
$settings = $root->toArray();
printf('User settings: <a href="%s">%s %s</a>',
$settings['root']['conf']['www'],
$settings['root']['conf']['firstname'],
$settings['root']['conf']['lastname']
);
?>
In this example the XML file config.xml
looks like this:
<?xml version="1.0" encoding="UTF-8"?> <conf> <firstname>John</firstname> <lastname>Doe</lastname> <www>http://example.com/</www> </conf>
For more information, You can read API doc, sample of package, tests of package, and a great tutorial of DevShed about the Config package.
There are many ways to create content in your Config object. You can simply pass it an array like it is shown in the example below:
Create configuration with array
<?php
// configuration array
$conf = array(
'DB' => array(
'type' => 'mysql',
'host' => 'localhost',
'user' => 'root',
'pass' => 'root'
)
);
// Config object
$config = new Config();
$root =& $config->parseConfig($conf,
'phparray',
array('name' => 'conf'));
echo $root->toString('phparray', array('name' => 'conf'));
?>
You can also ask Config to look for a file on your filesystem and try to parse it. You would then do it like this. This example assumes you have a valid XML file:
Create configuration with a file
<?php
// Config object
$config = new Config();
$root =& $config->parseConfig('/path/to/file.xml', 'xml');
echo $root->toString('phparray', array('name' => 'conf'));
?>
At the moment, Config can parse XML, PHP arrays, ini files, Apache conf and other generic type of configurations with comments and key-value pairs.
Of course, it is also possible to create the configuration from scratch as shown in the previous section example.
Supported configuration file types
The Config package supports reading and writing to different "containers" - types of configuration files. Ini-style configuration files are very common.
Container | Comments | Sections | Nested sections | Multiline strings | Boolean | Int/float | Null |
---|---|---|---|---|---|---|---|
Apache | yes | yes | yes | - | (yes)* | (yes)* | (yes)* |
GenericConf | yes | - | - | yes | (yes)* | (yes)* | (yes)* |
IniCommented | yes | yes | - | - | (yes)* | (yes)* | (yes)* |
IniFile | - | yes | - | yes | (yes)* | (yes)* | (yes)* |
PHPArray | - | yes | yes | yes | yes | yes | yes |
PHPConstants | yes | - | - | - | (yes)* | (yes)* | (yes)* |
XML | no | yes | yes | yes | (yes)* | (yes)* | (yes)* |
(yes)*
indicates that saving and loading those types does work, but the type of the variable is "string" after reading the config file.
Parses and saves Apache configuration files. No options are provided by this container.
This example script will create a new apache configuration file
for a virtual host. It first adds a
Listen
directive
and then a virtual host section containing its settings.
<?php
require_once 'Config.php';
$conf = new Config();
$root = $conf->getRoot();
$root->createDirective('Listen', '82');
$root->createBlank();
$root->createComment('My virtual host');
$vhost = $root->createSection('VirtualHost', array('127.0.0.1:82'));
$vhost->createDirective('DocumentRoot', '/usr/share/www');
$vhost->createDirective('ServerName', 'my-pear.example.org');
$conf->writeConfig(sys_get_temp_dir() . '/dummy-apache.conf', 'apache');
?>
Generated configuration file
# My virtual host <VirtualHost 127.0.0.1:82> DocumentRoot /usr/share/www ServerName my-pear.example.org </VirtualHost>
Our task is to open an existing Apache configuration file and
extract all Listen
directives from it, using
the container's
getItem
method.
Config file
# If you just change the port or add more ports here, you will likely also # have to change the VirtualHost statement in # /etc/apache2/sites-enabled/000-default # This is also true if you have upgraded from before 2.2.9-3 (i.e. from # Debian etch). See /usr/share/doc/apache2.2-common/NEWS.Debian.gz and # README.Debian.gz NameVirtualHost *:80 Listen 80 Listen 82 <IfModule mod_ssl.c> # If you add NameVirtualHost *:443 here, you will also have to change # the VirtualHost statement in /etc/apache2/sites-available/default-ssl # to <VirtualHost *:443> # Server Name Indication for SSL named virtual hosts is currently not # supported by MSIE on Windows XP. Listen 443 </IfModule> <IfModule mod_gnutls.c> Listen 443 </IfModule>
PHP script
<?php
require_once 'Config.php';
$file = dirname(__FILE__) . '/apache-read-config.txt';
$conf = new Config();
$root = $conf->parseConfig($file, 'apache');
if (PEAR::isError($root)) {
echo 'Error reading config: ' . $root->getMessage() . "\n";
exit(1);
}
$i = 0;
while ($item = $root->getItem('directive', 'Listen', null, null, $i++)) {
echo $item->name . ': ' . $item->content . "\n";
}
?>
Generic configuration files. The equals, comment start and new line characters in the parser can be customised to match your preferred configuration format.
Option | Data Type | Default value | Description |
---|---|---|---|
comment |
string | # |
The character that signifies the start of a comment. |
equals |
string | : |
The character that separates keys from values. |
newline |
string | \ |
The character that signifies that a value continues across multiple lines. |
<?php
require_once 'Config.php';
$conf = new Config();
$root = $conf->getRoot();
$root->createComment('Demo config file with GenericConf container');
$root->createDirective('openLastFile', true);
$root->createBlank();
//GenericConf does not support sections, but section contents are serialized
// into the main config namespace
$dbsect = $root->createSection('database');
$dbsect->createDirective('host', 'db-server.example.org');
$dbsect->createDirective('name', 'demo-db');
$r = $conf->writeConfig(
'generic-conf-write.txt',
'genericconf',
array(
'comment' => '#',
'equals' => '=>',
)
);
if (PEAR::isError($r)) {
echo $r->getMessage() . "\n";
}
?>
Generated file
#Demo config file with GenericConf container openLastFile=>1 host=>db-server.example.org name=>demo-db
Parses standard INI files, maintaining comments within the file.
In contrast to
IniFile,
the IniCommented
container parses the ini file manually using
regular expressions and is slower.
If you only read the file, using IniFile is more appropriate since it's faster.
Option | Data Type | Default value | Description |
---|---|---|---|
linebreak |
string | \n |
Character(s) used to separate lines from each other. |
<?php
require_once 'Config.php';
$conf = new Config();
$root = $conf->getRoot();
//note: inicommented uses ";" as comment indicator while the desktop entry spec
// says that it has to be "#" - so no comments for .desktop files
$entry = $root->createSection('Desktop Entry');
$entry->createDirective('Version', '1.0');
$entry->createDirective('Type', 'Link');
$entry->createDirective('URL', 'http://pear.php.net/');
$entry->createDirective('Name', 'go to pear.php.net');
$entry->createDirective('Icon', 'gnome-fs-bookmark');
$r = $conf->writeConfig(
'/home/cweiske/Desktop/pear.php.net.desktop',
'inicommented',
//windows newlines are not required in desktop files;
// they just serve as example how to use container options
array('linebreak' => "\r\n")
);
if (PEAR::isError($r)) {
echo $r->getMessage() . "\n";
}
?>
Generated file
[Desktop Entry] Version = 1.0 Type = Link URL = http://pear.php.net/ Name = go to pear.php.net Icon = gnome-fs-bookmark
Parse standard INI files using PHP's in built parse_ini_file(). Does not read in comments. No options are available for this container.
Ini file
[database] host=db.example.org user=bloguser pass=blogpassword
<?php
require_once 'Config.php';
$file = dirname(__FILE__) . '/ini-file-read.txt';
$conf = new Config();
$root = $conf->parseConfig($file, 'inifile');
if (PEAR::isError($root)) {
echo 'Error reading config: ' . $root->getMessage() . "\n";
exit(1);
}
$cfg = $root->toArray();
echo "Database connection:\n";
echo ' Host: ' . $cfg['root']['database']['host'] . "\n";
echo ' User: ' . $cfg['root']['database']['user'] . "\n";
echo ' Password: ' . $cfg['root']['database']['pass'] . "\n";
?>
Output
Database connection: Host: db.example.org User: bloguser Password: blogpassword
Parses PHP Array structures. Can read from a PHP Source file or from an in memory array. Due to technical limitations, this container does not parse blank lines or comments when reading from a configuration file, therefore any information contained within PHP comments will be lost.
Option | Data Type | Default value | Description |
---|---|---|---|
name |
string | conf |
The name to use for the root configuration variable, both when parsing and writing PHP source files. |
useAttr |
boolean | TRUE | Controls whether attributes are parsed and saved. |
Since config files containing php arrays are just included using the standard php methods, code comments and structure will be lost when saving.
<?php
require_once 'Config.php';
$conf = new Config();
$root = $conf->getRoot();
$root->createComment('Demo config file with PHPArray container');
$root->createDirective('openLastFile', true);
$root->createBlank();
$dbsect = $root->createSection('database');
$dbsect->createDirective('host', 'db-server.example.org');
$dbsect->createDirective('name', 'demo-db');
//this is a nested section
$subsect = $dbsect->createSection('settings');
$subsect->createDirective('charset', 'utf-8');
$subsect->createDirective('reconnect', true);
$r = $conf->writeConfig('php-array-write.txt', 'phparray');
if (PEAR::isError($r)) {
echo $r->getMessage() . "\n";
}
?>
Generated file
<?php
// Demo config file with PHPArray container
$conf['openLastFile'] = true;
$conf['database']['host'] = 'db-server.example.org';
$conf['database']['name'] = 'demo-db';
$conf['database']['settings']['charset'] = 'utf-8';
$conf['database']['settings']['reconnect'] = true;
?>
Parses a set of PHP define() from a PHP source file. Comments are maintained by this container, although blank lines will be lost. There are no options for this container.
<?php
require_once 'Config.php';
$conf = new Config();
$root = $conf->getRoot();
$root->createComment('Demo config file with PHPConstants container');
$root->createDirective('openLastFile', true);
$root->createBlank();
//PHPConstants container does not support sections but puts them in the main
// namespace
$dbsect = $root->createSection('database');
$dbsect->createDirective('host', 'db-server.example.org');
$dbsect->createDirective('name', 'demo-db');
$r = $conf->writeConfig('php-constants-write.txt', 'phpconstants');
if (PEAR::isError($r)) {
echo $r->getMessage() . "\n";
}
?>
Generated file
<?php
/**
*
* AUTOMATICALLY GENERATED CODE - DO NOT EDIT BY HAND
*
**/
// Demo config file with PHPArray container
define('OPENLASTFILE', true);
//
// database
//
define('HOST', 'db-server.example.org');
define('NAME', 'demo-db');
?>
Parses a XML file using XML_Parser.
Option | Data Type | Default value | Description |
---|---|---|---|
version |
string | 1.0 |
The XML version to use. |
encoding |
string | ISO-8859-1 |
The content encoding to use when parsing and storing data. |
name |
string | conf |
As with PHPArray, this defines the name of the global configuration root. |
indent |
string | |
The character used for indentation when writing the XML document, if any. By default, two spaces are used. |
linebreak |
string | \n |
The line-breaking character(s) to use when writing the XML document. |
addDecl |
boolean | TRUE | Controls whether the XML declaration is added to the start of the XML document. |
useAttr |
boolean | TRUE | Controls whether attributes are parsed and saved. |
isFile |
boolean | TRUE | If TRUE, the first argument to parseConfig() will be taken as the file name for the XML file to load. If FALSE, the argument will be taken as the XML data itself and parsed accordingly. |
useCData |
boolean | FALSE | Controls whether data is enclosed in CDATA blocks. |
To utilize the XML container, you need to have the XML_Parser package installed (optional dependency).
<?php
require_once 'Config.php';
$conf = new Config();
$root = $conf->getRoot();
$root->createComment('Demo config file with XML container');
//we need a main section, otherwise we get invalid XML
// see http://pear.php.net/bugs/bug.php?id=18357
$main = $root->createSection('config');
$main->createDirective('openLastFile', true);
$main->createDirective('rememberFiles', 8);
$main->createBlank();
$dbsect = $main->createSection('database');
$dbsect->createDirective('host', 'db-server.example.org');
$dbsect->createDirective('name', 'demo-db');
//this is a nested section
$subsect = $dbsect->createSection('settings');
$subsect->createDirective('charset', 'utf-8');
$subsect->createDirective('reconnect', false);
$r = $conf->writeConfig(
'xml-write.txt',
'xml',
array(
'encoding' => 'UTF-8',
'indent' => ' ',
)
);
if (PEAR::isError($r)) {
echo $r->getMessage() . "\n";
}
?>
Generated file
<?xml version="1.0" encoding="UTF-8"?> <!-- Demo config file with XML container --> <config> <openLastFile>1</openLastFile> <rememberFiles>8</rememberFiles> <database> <host>db-server.example.org</host> <name>demo-db</name> <settings> <charset>utf-8</charset> <reconnect /> </settings> </database> </config>
object Config::Config (
void
)
The Config constructor, it creates a config object.
The next thing you should do is getting the root Config_Container object with getRoot() .
object &Config::getRoot (
void
)
This method returns a reference to the root Config_Container object in the Config object. Use the returned Config_Container object reference to manipulate your configuration data.
object
-
a reference to config's root container object
This function can not be called statically.
Using getRoot()
<?php
$config = new Config();
$root =& $config->getRoot();
?>
bool Config::isConfigTypeRegistered (
string $configType
)
This method checks if the required container type exists in
$GLOBALS['CONFIG_TYPES']
.
$configType
Type of config (php array, xml, inifile...)
bool
- TRUE if registered, FALSE otherwise.
This function can not be called statically.
mixed &Config::parseConfig (
mixed $datasrc
, string $configType
, array $options = array()
)
This method will parse the datasource given and fill the root Config_Container object with other Config_Container objects. It will return a reference to the root Config_Container object or a PEAR_Error if something went wrong.
$datasrc
Datasource to parse. For most containers, it is a file path. For the PHP array parser, it can be an array too.
$configType
Type of configuration to parse
$options
Options for the parser
object
-
a reference to Config_Container object
Error code | Error value | Meaning | Solution |
---|---|---|---|
PEAR_ERROR_RETURN | "Configuration type '$configType' is not registered in Config::parseConfig." | The config type must be registered for use by Config. | Use one of the standard config types, or register your custom container using Config::registerConfigType |
Other errors may be returned from the parser for the specific container you're using.
This function can not be called statically.
Using parseConfig()
<?php
$config = new Config();
$root =& $config->parseConfig('/path/to/file.php', 'phparray',
array('name' => 'conf'));
?>
bool Config::registerConfigType (
string $configType
, array $configInfo = false
)
This method registers a new container type with the Config class.
$configType
The name of the configuration type.
$configInfo
An array defining the file containing the container's class definition and the class name. The format for this array is as follows:
<?php
$configInfo = array('path/to/Name.php', // The path to the source file for the class.
'Config_Container_Class_Name' // The name of the container class.
);
?>
If omitted, the default values are:
<?php
$configInfo = array("Config/Container/$configType.php",
"Config_Container_$configType"
);
?>
mixed
-
Returns TRUE on success,
PEAR_Error on failure.
mixed Config::setRoot (
object &$rootContainer
)
This method will replace the current child of the root Config_Container object by the given object.
$rootContainer
Container to be used as the first child of root
boolean
-
Returns TRUE on success, FALSE on failure.
This function can not be called statically.
Using setRoot()
<?php
$container =& new Config_Container('section', 'conf');
$config = new Config();
$root =& $config->setRoot($container);
?>
mixed Config::writeConfig (
mixed $datasrc
= null
, string $configType
= null
, array $options = array()
)
This method will call the root Config_Container::writeDatasrc() method which in turn will try to write the Config contents to the datasource.
$datasrc
Datasource to write to
$configType
Type of configuration for writer
$options
Options for writer
mixed
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
This example shows how to convert a configuration from a php file which contains a php array to an xml file.
Using writeConfig()
<?php
$config = new Config();
$config->parseConfig(
'/path/to/file.php',
'phparray',
array('name' => 'conf')
);
$config->writeConfig( '/path/to/file.xml',
'xml',
array('name' => 'conf')
);
?>
&Config_Container::Config_Container (
string $type = ''
, string $name = ''
, string $content = ''
, mixed $attributes
= null
)
Creates a new Config_Container and returns it by reference.
$type
Type of container object, should be something among
'section'
, 'comment'
,
'directive'
, 'blank'
.
$name
Name of container object. The name is required for sections and directives, not for blanks or comments.
$content
Content of container object. The content is used in directives and comments.
$attributes
Array of attributes for container object. Optionally, you can add attributes to your container. These can be used by the chosen parser.
object
- A reference to the created object.
This function can not be called statically.
object Config_Container::addItem (
object &$item
, string $where = 'bottom'
, object $target
= null
)
This method will add a Config_Container child to the current container
children. Thus, addItem() can only be called
one a section type container. If a position is specified,
the object will be added at this position. If
'before'
or 'after'
are specified as position, a target object is required.
The object will then be added before or after the target
object position in the current container.
&$item
a container object
$where
choose a position 'bottom'
,
'top'
, 'after'
,
'before'
$target
needed if you choose 'before'
or
'after'
in $where
.
$target
must be one of this
container's children. ZendEngine2 will accept references
with default. It will then be possible to have
&$target
instead.
object
- A reference to the added object
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Adding an item using addItem()
<?php
$section =& new Config_Container('section', 'conf');
$directive =& new Config_Container('directive', 'user', 'mansion');
$section->addItem($directive);
?>
Adding an item using addItem() and a position relative to another item
<?php
$section =& new Config_Container('section', 'conf');
$directive =& new Config_Container('directive', 'user', 'mansion');
$section->addItem($directive);
$comment =& new Config_Container('comment', null, 'Here goes my name');
$section->addItem($comment, 'before', $directive);
?>
int Config_Container::countChildren (
string $type
= null
, string $name
= null
)
This method will return the number of children this
container has. If either $name
or $type
, it will affine its
search just to return the number of children
corresponding to these parameters.
$type
Type of children to count.
$name
Name of children to count.
int
- number of children found
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
objectConfig_Container::createBlank (
mixed $where = 'bottom'
, mixed $target
= null
)
The current item must be a section. This is a helper method that calls createItem()
$where
Where to create the new item ('top'
,
'bottom'
, 'before'
,
'after'
)
$target
Required only if 'before'
or
'after'
is used for
$where
object
- reference to new item
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Create a new directive using createBlank()
<?php
$section =& new Config_Container('section', 'conf');
$directive =& $section->createDirective('user', 'mansion');
$section->createBlank('before', $directive);
?>
objectConfig_Container::createComment (
string $content = ''
, string $where = 'bottom'
, object $target
= null
)
The current item must be a section. This is a helper method that calls createItem()
$content
Comment content
$where
Where to create the new item ('top'
,
'bottom'
,
'before'
, 'after'
)
$target
Required only if 'before'
or 'after'
is used
for $where
object
- reference to new item
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Create a new directive using createComment()
<?php
$section =& new Config_Container('section', 'conf');
$section->createComment('Database Configuration');
?>
object Config_Container::createDirective (
string $name
, string $content
, mixed $attributes
= null
, string $where = 'bottom'
, mixed $target
= null
)
The current item must be a section. This is a helper method that calls createItem()
$name
Name of new directive
$content
Content of new directive
$attributes
Directive attributes
$where
Where to create the new item ('top'
,
'bottom'
, 'before'
,
'after'
)
$target
Required only if 'before'
or
'after'
is used for
$where
object
- reference to new item
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Create a new directive using createDirective()
<?php
$section =& new Config_Container('section', 'conf');
$section->createDirective('user', 'mansion');
?>
object &Config_Container::createItem (
string $type
, mixed $item
, string $content
, array $attributes
= null
, string $where = 'bottom'
, object $target
= null
)
This method must be called on a section, the created item can be anything. It adds a new child to the current item. If a position is specified, the child will be created at there. It is recommended to use the helper methods instead of calling this method directly.
$type
type of item: directive
,
section
, comment
,
blank
...
$item
item name
$content
item content
$attributes
item attributes
$where
choose a position 'bottom'
,
'top'
, 'after'
,
'before'
$target
needed if you choose 'before'
or 'after'
for
$where
object
- reference to new item
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Create some new items using createItem()
<?php
$section =& new Config_Container('section', 'conf');
$section->createItem('directive', 'user', 'root');
$section->createItem('directive', 'pass', 'root');
$header =& $section->createItem('comment', null, 'Database Configuration', 'top');
$section->createItem('blank', null, null, 'after', $header);
?>
objectConfig_Container::createSection (
string $name
, array $attributes
= null
, string $where = 'bottom'
, object $target
= null
)
Must be called on a section. This is a helper method that calls createItem()
$name
Name of new section
array $attributes
Section attributes
$where
Where to create the new item ('top'
,
'bottom'
,
'before'
,
'after'
)
$target
Required only if 'before'
or 'after'
is used for
$where
object
- reference to new item
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Create a new section using createSection()
<?php
$conf =& new Config_Container('section', 'MAIN');
$dbConf =& $conf->createSection('DB');
$dbConf->createDirective('user', 'mansion');
?>
mixed Config_Container::getAttribute (
string $attribute
)
This method returns the value associated with the key attribute. To get all attributes, see method getAttributes().
$attribute
Attribute key
mixed
- item's attribute value
This function can not be called statically.
array Config_Container::getAttributes (
void
)
This method returns an array containing the attributes of this container.
array
- item's attributes
This function can not be called statically.
Using attribute
<?php
$attributes = array('id' => 'db', 'language' => 'en');
$section =& new Config_Container('section', 'main', null, $attributes);
$section->createDirective('authentication', 'test', array('type' => 'mysql',
'host' => 'localhost'));
echo $section->toString('phparray');
echo $section->toString('xml');
?>
Attributes are set for '@' key with php array type
Attributes are set the usual way with xml type
mixed Config_Container::getChild (
int $index
)
Children are stored in an array. This method returns the Config_Container object at the specified index.
$index
Child index
mixed
- returns reference to child object or
FALSE if child does not exist
This function can not be called statically.
mixed Config_Container::getContent (
void
)
Accessor method. Returns the item content.
mixed
- item's content
This function can not be called statically.
mixed Config_Container::getItem (
string $type
= null
, string $name
= null
, mixed $content
= null
, array $attributes
= null
, int $index = -1
)
This method tries to find the items that respond to the specified parameters.
This method can only be called on an object of type
'section'
. Note that root is a section.
This method is not recursive and tries to keep the current structure.
$type
type of item: directive
, section
,
comment
, blank
...
$name
item name
$content
find item with this content
$attributes
find item with attribute set to the given value
$index
index of the item in the returned object list. If it is not set, will try to return the last item with this name.
mixed
- reference to item found or FALSE when not found
This function can not be called statically.
A few examples on how to find items using getItem()
<?php
// will return the last directive found
$directives =& $obj->getItem('directive');
// will return the last directive found with content 'root'
$directives =& $obj->getItem('directive', null, 'root');
// will return the fourth directive with name 'bar'
$directive_bar_4 =& $obj->getItem('directive', 'bar', null, null, 4);
// will return the last section named 'foo'
$section_foo =& $obj->getItem('section', 'foo');
// will return the last section with attribute 'id' set to 'db'
$section_foo =& $obj->getItem('section', 'foo', null, array('id' => 'db'));
?>
mixed Config_Container::getItemIndex (
void
)
Children Config_Container objects are stored in an array. This method will return the index of this item in its parent array.
mixed
- returns int or NULL if root object
This function can not be called statically.
int Config_Container::getItemPosition (
void
)
Returns the item rank in its parent children array according to other items with same type and name.
mixed
- returns int or NULL if root object
This function can not be called statically.
string Config_Container::getName (
void
)
Return the name of this item.
string
- item's name
This function can not be called statically.
object Config_Container::getParent (
void
)
Returns the item parent object.
object
- reference to parent object or NULL if root object
This function can not be called statically.
string Config_Container::getType (
void
)
Gets this item's type.
string
- item type
This function can not be called statically.
bool Config_Container::isRoot (
void
)
There is only one root item. It has no parent, no name, no content and is of type 'section'. The root item is not used in the parsers, only its children.
bool
- TRUE if item is the root item
This function can not be called statically.
mixed Config_Container::removeItem (
void
)
This method removes the current item. References to its children are removed as well. This method does not accept parameters yet.
boolean
- TRUE if object was removed,
FALSE if not, or PEAR_Error if root
This function can not be called statically.
mixed Config_Container::searchPath (
mixed $args
)
This method tries to find an item by following a given path from the current container.
This method can only be called on an object of type
'section'
. Note that root is a section.
This method is recursive.
This method takes as many parameters as is needed to define your path to the requested item.
The format is array (item1, item2, ..., itemN). Items can be strings or arrays. Strings will match
the item name
, while arrays will match 'name'
and/or 'attributes'
properties of the requested item.
$args
Strings or arrays of item to match in the order they will be matched, separated by commas
mixed
- reference to item found or FALSE when not found
This function can not be called statically.
Example for searchPath() usage
<?php
// Let's say our XML configuration looks like this:
// <config>
// <db>
// <user>root</user>
// <password>pass</user>
// <host>localhost</host>
// </db>
// </config>
$config = new Config();
$root =& $menuObj->parseConfig('db.xml', 'xml');
// Will return the password directive in db
$passObj =& $root->searchPath(array('config', 'db', 'password'));
?>
More complex example with attributes for searchPath()
<?php
// Let's say our XML configuration looks like this:
// <menu>
// <group id="company">
// <page id="news"/>
// <page id="jobs"/>
// </group>
// <group id="projects">
// <page id="project1"/>
// <page id="project2"/>
// </group>
// </menu>
$menuObj = new Config();
$root =& $menuObj->parseConfig('menu.xml', 'xml');
// Will return the container in menu which 'id' is set to 'projects'
$section =& $root->searchPath(array('menu', array('group', array('id' => 'projects'))));
// To get a page we could also use
$page =& $root->searchPath(array('menu',
array('group', array('id' => 'projects')),
array('page', array('id' => 'project2'))));
?>
void Config_Container::setAttributes (
array $attributes
)
Attributes are stored in an array. They are used in some containers like PHP Array, XML, Apache. In IniFile or IniCommented containers, attributes are not used. See examples in getAttributes() method to have an idea of the attributes output. This method will replace existing attributes. Use updateAttributes() if you just want to change some of them.
$attributes
Array of attributes
This function can not be called statically.
void Config_Container::setContent (
mixed $content
)
Set this item's content.
mixed $content
Item's content
This function can not be called statically.
void Config_Container::setDirective (
string $name
, mixed $content
, int $index = -1
)
This is an helper method that will first try to find the item with the desired name using getItem(). If the item is found, it will call its setContent() method. If not, it will just create a new directive at the bottom with the given name and content.
$name
Name of the directive to look for
$content
New content, usually a string
$index
Index of the directive to set, in case there are more than one directive with the same name
object
- Newly set directive
This function can not be called statically.
void Config_Container::setName (
string $name
)
Set this item's name.
$name
Item name
This function can not be called statically.
void Config_Container::setType (
string $type
)
Set this item's type.
$type
item type
This function can not be called statically.
array Config_Container::toArray (
void
)
This method returns an array representation of the Config tree. The format is
<?php
$section[directive][index] = value
?>
If the container has attributes, it will use '@'
as key
for attributes and '#'
for values.
index is here because multiple directives and sections can have the same name,
the toArray() method takes care of that.
array
- an array representation of
the Config_Container tree
This function can not be called statically.
Using toArray()()
<?php
$attributes = array('id' => 'db', 'language' => 'en');
$section =& new Config_Container('section', 'main', null, $attributes);
$section->createDirective('authentication', 'one', array('type' => 'mysql'));
$section->createDirective('authentication', 'two', array('type' => 'oracle'));
$section->createDirective('permission', 'group');
var_dump($section->toArray());
?>
Resulting array with attributes and directives with same name or not
string Config_Container::toString (
string $configType
, array $options = array()
)
This method will call the toString() method in the choosen config type. It will return a string representation of the Config_Container and its children.
$configType
Type of configuration used to generate the string
$options
Specify special options used by the parser
string
- the generated string
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
void Config_Container::updateAttributes (
array $attributes
)
If the specified attributes are already set, they are overwritten. New attributes are set. The ones that are not set in the given parameter array are left untouched.
$attributes
Array of attributes
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
boolean Config_Container::writeDatasrc (
mixed $datasrc
, string $configType
, array $options = array()
)
By default, this method will write a new string generated with the toString() method to a file. If a writeDatasrc() method exists in the chosen config type, this method will be called instead. This allows for more flexibility and makes it possible to add your own save handlers.
$datasrc
Info on datasource such as path to the configuraton file or dsn...
$configType
Type of configuration
$options
Options for writer
boolean
- TRUE on success
Error code | Error value | Meaning | Solution |
---|---|---|---|
" |
This function can not be called statically.
Provides Packages for PHP-based command-line applications like the pear installer.
This section gives an overview about the different command line option parser packages in PEAR.
Console_GetOpt: standard getopt implementation
Console_GetOptPlus: php5 rewrite of getopt with added features
Console_GetArgs: object oriented full featured parser
Console_CommandLine: similar to GetArgs with php5 and added features
Feature | Console_GetOpt | Console_GetOptPlus | Console_GetArgs | Console_CommandLine |
---|---|---|---|---|
Package homepage | Link | Link | Link | Link |
PHP4 | yes | yes | ||
PHP5 | yes | yes | yes | |
PHP5 with E_STRICT | yes | yes | ||
Has end user doc | yes | yes | ||
Stability | stable | beta | stable | stable |
POSIX/GNU compliance | yes | yes | yes | |
Generate help | yes | yes | yes | |
Generate version | yes | |||
Argument validation | yes | yes | ||
Handle password options | yes | |||
Option validation | yes | |||
Subcommands | yes | |||
Optional callback actions | yes | |||
L10n/I18n | yes |
Console_Color allows you to easily use ANSI console colors in your application to colorize the output.
Console_Color provides methods to convert complete strings tagged with special markers into ANSI-compatible color code representations, and methods that directly return those codes for certain colors.
The method used most often is convert(). It takes a string, e.g.
%bHello World!%n\n
or
3 out of 4 people make up about %r75%% %n
and returns the ANSI representation. The characters following a percentage sign (%) do have special meanings. See color codes to get an overview.
To reset the colors to normal, use %n
.
Further, there is an escape() method that prevents special chars from being treated as markers.
Methods bgcolor(), color(), fgcolor() and style() directly return ANSI control codes for the given color or style value.
Using strip(), you can remove color and style codes from a string.
<?php
require 'Console/Color.php';
//make it blue
print Console_Color::convert("%bHello World!%n\n");
//more colors
print Console_Color::convert("%rred%n, %ggreen%n, %yyellow%n\n");
?>
Color | Text normal | Text bold | Background |
---|---|---|---|
Black | %k |
%K |
%0 |
Red | %r |
%R |
%1 |
Green | %g |
%G |
%2 |
Yellow | %y |
%Y |
%3 |
Blue | %b |
%B |
%4 |
Magenta | %m |
%M |
%5 |
Purple | %p |
%P |
|
Cyan | %c |
%C |
%6 |
White | %w |
%W |
%7 |
Style | Code |
---|---|
Blinking, flashing | %F |
Underline | %U |
Invert, reverse | %8 |
Bold | %_ , %9 |
Reset color | %n |
Single % | %% |
Console_CommandLine is a full featured package for managing command line options and arguments highly inspired from the python optparse module, it allows the developer to easily build complex command line interfaces.
Console_CommandLine is a convenient, flexible, and powerful library for parsing command-line options and arguments.
It uses adeclarative style of command-line parsing: you create an instance of Console_CommandLine, populate it with options, arguments and even sub-commands and parse the command line. Console_CommandLine allows users to specify options in the conventional GNU/POSIX syntax, and additionally generates usage and help messages for you.
DISCLAIMER: since Console_CommandLine was highly inspired from the python optparse module and thus is very similar to it, some parts of this document were shamelessly copied from optparse manual.
A simple example:
<?php
require_once 'Console/CommandLine.php';
$parser = new Console_CommandLine();
$parser->description = 'A fantastic command line program that does nothing.';
$parser->version = '1.5.0';
$parser->addOption('filename', array(
'short_name' => '-f',
'long_name' => '--file',
'description' => 'write report to FILE',
'help_name' => 'FILE',
'action' => 'StoreString'
));
$parser->addOption('quiet', array(
'short_name' => '-q',
'long_name' => '--quiet',
'description' => "don't print status messages to stdout",
'action' => 'StoreTrue'
));
try {
$result = $parser->parse();
// do something with the result object
print_r($result->options);
print_r($result->args);
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
With the above lines of code, users of the script can now use the program like this:
$ <yourscript> --file=outfile -q
As it parses the command line, Console_CommandLine sets attributes of the
result object returned by
Console_CommandLine::parse()
method based on user-supplied command-line values.
When
Console_CommandLine::parse()
returns from parsing this command line,
$result->options['filename']
will be
"outfile" and $result->options['quiet']
will be TRUE.
Console_CommandLine supports both long and short options, allows short options to be merged together, and allows options to be associated with their arguments in a variety of ways.
The following lines are all equivalent for the above example:
$ <yourscript> -f outfile --quiet $ <yourscript> --quiet --file outfile $ <yourscript> -q -foutfile $ <yourscript> -qfoutfile
Additionally, to get help, users can do this:
$ <yourscript> -h $ <yourscript> --help
These commands will print:
A fantastic command line program that does nothing. Usage: tmp.php [options] Options: -f FILE, --file=FILE write report to FILE -q, --quiet don't print status messages to stdout -h, --help show this help message and exit --version show the program version and exit
Console_CommandLine also manage the program version automatically:
$ <yourscript> --version
The above command will print:
$ <yourscript> version 1.5.0.
where the value of yourscript is determined at runtime (normally from
$argv[0]
, except if you specified explicitely a
name for your parser).
Console_CommandLine::__construct() takes an optional array of parameters explained in the table below. Note that if you are using an XML definition file, you can pass these parameters in it (see XML example for details).
name | type | required | description |
---|---|---|---|
name | string | no, default to $argv[0] if not given |
the name of your program |
description | string | no, but recommended for the help message | the description of your program: this should explain what your program is supposed to do |
version | mixed (string or numeric) | no, note that if not given, the --version option will not be available | the program version number |
add_help_option | boolean | no, default to TRUE | if set to FALSE the parser will not generate automatically the "help" option |
add_version_option | boolean | no, default to TRUE | if set to FALSE the parser will not generate automatically the "version" option |
force_posix | boolean | no, default to FALSE | if set to TRUE, the parser will force POSIX compliance (please see the gettext manual for more information) |
The examples below demonstrate how to instanciate Console_CommandLine and build a parser using PHP code.
The simplest way
<?php
require_once 'Console/CommandLine.php';
$parser = new Console_CommandLine(array(
'description' => 'A useful description for your program.',
'version' => '0.0.1', // the version of your program
));
?>
Alternative method
<?php
require_once 'Console/CommandLine.php';
$parser = new Console_CommandLine();
$parser->description = 'A useful description for your program.';
$parser->version = '0.0.1'; // the version of your program
?>
The examples below demonstrate how to instanciate Console_CommandLine and build a parser using an XML definition file, this can be very useful if you have a big program or if you need to reuse your user interface settings for a web frontend for example.
The XML file
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <command> <description>A useful description for your program.</description> <version>0.0.1</version> </command>
The PHP file
<?php
require_once 'Console/CommandLine.php';
$parser = Console_CommandLine::fromXmlFile('example.xml');
?>
Using an XML string you would have called Console_CommandLine::fromXmlString() instead of Console_CommandLine::fromXmlFile() of course.
Options are used to provide extra information to tune or customize the execution of a program. In case it wasn't clear, options are usually optional. A program should be able to run just fine with no options whatsoever. Pick a random program from the Unix or GNU toolsets. Can it run without any options at all and still make sense? The main exceptions are find, tar, and dd--all of which are mutant oddballs that have been rightly criticized for their non-standard syntax and confusing interfaces.
Lots of people want their programs to have "required options". Think about it. If it's required, then it's not optional! If there is a piece of information that your program absolutely requires in order to run successfully, that's what arguments are for.
As an example of good command-line interface design, consider the humble cp utility, for copying files. It doesn't make much sense to try to copy files without supplying a destination and at least one source. Hence, cp fails if you run it with no arguments. However, it has a flexible, useful syntax that does not require any options at all:
$ cp SOURCE DEST $ cp SOURCE ... DEST-DIR
You can get pretty far with just that. Most cp implementations provide a bunch of options to tweak exactly how the files are copied: you can preserve mode and modification time, avoid following symlinks, ask before clobbering existing files, etc. But none of this distracts from the core mission of cp, which is to copy either one file to another, or several files to another directory.
To add options to your parser, just create the parser as explained in the previous section and use the Console_CommandLine::addOption() method.
The Console_CommandLine::addOption() method takes two arguments:
$result->options['foo']
;
name | type | required | description | example |
---|---|---|---|---|
short_name | string | yes, if no long_name given | the option short name | -o |
long_name | string | yes, if no short_name given | the option long name | --orientation |
description | string | no, but recommended for the help message | a description for the option | orientation of the page: ltr (default) or rtl |
action | string | no, default to "StoreString" | the option action (see next section for details) | StoreString |
default | mixed | no | the option default value | ltr |
choices | array | no, only relevant for options that expect argument(s) | list of possible values for the option | array('ltr', 'rtl') |
list | array | no, only relevant for options that use the "List" action | list of values to display | array('blue', 'green', 'yellow') |
add_list_option | boolean | no, default to FALSE | this property is only relevant when the choices property is set, if set to TRUE the parser will generate an additional option to list the choices (eg. --list-foo). | TRUE |
help_name | string | no, if not given it will default to the option name | the name to display in the option help line | page_orientation (the help ligne will be: --orientation=page_orientation) |
Adding commandline options
<?php
require_once 'Console/CommandLine.php';
$parser = new Console_CommandLine(array(
'description' => 'A useful description for your program.',
'version' => '0.0.1', // the version of your program
));
// Adding a simple option that takes no argument and that tell our program to
// turn on verbose output:
$parser->addOption(
'verbose',
array(
'short_name' => '-v',
'long_name' => '--verbose',
'description' => 'turn on verbose output',
'action' => 'StoreTrue'
)
);
// Adding an option that will store a string
$parser->addOption(
'orientation',
array(
'short_name' => '-o',
'long_name' => '--orientation',
'description' => 'orientation of the page, "ltr" (default) or "rtl"',
'action' => 'StoreString',
'default' => 'ltr',
'help_name' => 'page_orientation'
)
);
try {
$result = $parser->parse();
print_r($result->options);
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
Now if the user type:
$ <yourprogram> -vo rtl
or (equivalent):
$ <yourprogram> --verbose --orientation=rtl
The output of the above script will be:
Array ( [verbose] => 1 [orientation] => rtl [help] => [version] => )
Actions tell the parser how to handle option values, among other things they tell the parser if the option expects a value or not and how to store this value in the result object.
This action tells the parser to store the value true in the result object if the option is present in the command line, for example:
$ <yourprogram> -v
will store TRUE in $result->options['verbose']
,
assuming the option was defined like this:
<?php
$parser->addOption('verbose', array('short_name'=>'-v', 'action'=>'StoreTrue'));
$result = $parser->parse();
?>
This action tells the parser to store the value false in the result object if the option is present in the command line, for example:
$ <yourprogram> -q
will store FALSE in $result->options['verbose']
,
assuming the option was defined like this:
<?php
$parser->addOption('verbose', array('short_name'=>'-q', 'action'=>'StoreFalse'));
$result = $parser->parse();
?>
This action tells the parser that the option expects a value and to store this value as a string in the result object, for example:
$ <yourprogram> -o out.txt
will store the string "out.txt" in
$result->options['outfile']
,
assuming the option was defined like this:
<?php
$parser->addOption('outfile', array('short_name'=>'-o', 'action'=>'StoreString'));
$result = $parser->parse();
?>
This action tells the parser that the option expects a value and to store this value as an integer in the result object, for example:
$ <yourprogram> --width=500
will store the integer 500 in $result->options['width']
,
assuming the option was defined like this:
<?php
$parser->addOption('width', array('long_name'=>'--width', 'action'=>'StoreInt'));
$result = $parser->parse();
?>
This action tells the parser that the option expects a value and to store this value as a float in the result object, for example:
$ <yourprogram> -l=0.3
will store the float 0.3 in $result->options['level']
,
assuming the option was defined like this:
<?php
$parser->addOption('level', array('short_name'=>'-l', 'action'=>'StoreFloat'));
$result = $parser->parse();
?>
This action tells the parser to increment the value in the result object each time it encounters the option in the command line, for example:
$ <yourprogram> -vvvv
or the equivalent:
$ <yourprogram> -v -v -v --verbose
will store the integer 4 in
$result->options['verbose_level']
,
assuming the option was defined like this:
<?php
$parser->addOption('verbose_level', array(
'short_name' => '-v',
'long_name' => '--verbose',
'action' => 'Counter'
));
$result = $parser->parse();
?>
This action tells the parser to display the help message if it encounters the option in the command line, most of the time you won't need this since it is handled by Console_CommandLine internally.
This action tells the parser to display the program version if it encounters the option in the command line, as for Help action, chances are that you won't need this since it is handled by Console_CommandLine internally.
This action allows the user to either type the password on the commandline or to be prompted for it (will not echo on unix systems), some examples:
<?php
$parser->addOption('password', array('short_name'=>'-p', 'action'=>'Password'));
$result = $parser->parse();
?>
$ <yourprogram> -ps3cret
will store the string "s3ecret" in
$result->options['password']
whereas:
$ <yourprogram> -p
will "prompt" the user for entering his/her password
without echoing it, and will store "s3ecret" in
$result->options['password']
This action allows to specify a PHP callback to handle user input. The callback must be a php callable and must accept five arguments:
Your callback function must return the modified (or not modified) value (the first argument).
All these arguments should give you enough flexibility to build complex callback actions.
If the callback is to a class method, then the method must be declared as a public.
Here is a simple example:
<?php
/**
* A simple encryption callback.
*
*/
function encryptCallback($value, $option, $result, $parser, $params=array())
{
if (!isset($params['salt'])) {
$params['salt'] = '';
}
return sha1($params['salt'] . $value);
}
require_once 'Console/CommandLine.php';
$parser = new Console_CommandLine();
$parser->addOption('encrypt', array(
'short_name' => '-e',
'long_name' => '--encrypt',
'action' => 'Callback',
'description' => 'encrypt the given value using sha1 + salt',
'callback' => 'encryptCallback',
'action_params' => array('salt' => 'x2897ZHKx7200j1__2')
));
try {
$result = $parser->parse();
echo $result->options['encrypt'] . "\n";
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
Now if the user type:
$ <yourprogram> -e foobar
The output of the above script will be:
7f12da3b1c126d7a47745b09dc0040c92cee1700
a special action that simply output an array as a list.
<?php
$parser->addOption('list_colors', array(
'short_name' => '-l',
'long_name' => '--list-colors',
'action' => 'List',
'action_params' => array(
'list' => array('blue', 'green', 'yellow'),
'delimiter' => ',' //optional
)
));
$result = $parser->parse();
?>
$ <yourprogram> --list-colors
will display the list of colors separated by commas.
Arguments are for those pieces of information that your program absolutely, positively requires to run.
A good user interface should have as few absolute requirements as possible. If your program requires 17 distinct pieces of information in order to run successfully, it doesn't much matter how you get that information from the user, most people will give up and walk away before they successfully run the program. This applies whether the user interface is a command-line, a configuration file, or a GUI: if you make that many demands on your users, most of them will simply give up.
In short, try to minimize the amount of information that users are absolutely required to supply and use sensible defaults whenever possible. Of course, you also want to make your programs reasonably flexible. That's what options are for.
To add arguments to your parser, just create the parser as explained in the previous section and use the Console_CommandLine::addArgument() method.
The Console_CommandLine::addArgument() method takes two arguments:
$result->args['foo']
;
name | type | required | description | example |
---|---|---|---|---|
description | string | no, but recommended for the help message | a description for the argument | entry list of input files separated by spaces |
multiple | boolean | no, default to FALSE | tells the parser that the argument expects multiples values | TRUE |
optional | boolean | no, default to FALSE | tells the parser that the argument is optional | TRUE |
help_name | string | no, if not given it will default to the argument name | the name to display in the argument help line | files |
Adding commandline arguments
<?php
require_once 'Console/CommandLine.php';
$parser = new Console_CommandLine();
// add an array argument
$parser->addArgument('input_files', array('multiple'=>true));
// add a simple argument
$parser->addArgument('output_file');
try {
$result = $parser->parse();
print_r($result->args);
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
If the user type:
$ <yourprogram> file1 file2 file3
The output of the above script will be:
Array ( [input_files] => Array ( [0] => file1 [1] => file2 ) [output_file] => file3 )
Some programs are very complex, you can't do anything about that but you need to provide the best user interface possible in order to have happy users. In some cases, sub-commands can be very useful, take the PEAR installer for example, it is much more clearer to separate the installer functionalities than to mix all functionalities in the same interface, that's why you have an interface like:
$ pear install <options> <pkgname> $ pear upgrade <options> <pkgname> and so on...
Adding sub-commands is quite simple, basically you use the Console_CommandLine::addCommand() method that returns a Console_CommandLine_Command instance and then you build your command instance like you would do for the main parser (add options, arguments etc...). One thing to remember is that sub-commands are exactly the same as main parsers: they have the same properties and methods.
Let's take a simple example to demonstrate the use of sub-commands, in the following code we will build a simple command line program that will have two sub-commands: "foo" and "bar":
<?php
// Include the Console_CommandLine package.
require_once 'Console/CommandLine.php';
// create the parser
$parser = new Console_CommandLine(array(
'description' => 'A great program that can foo and bar !',
'version' => '1.0.0'
));
// add a global option to make the program verbose
$parser->addOption('verbose', array(
'short_name' => '-v',
'long_name' => '--verbose',
'action' => 'StoreTrue',
'description' => 'turn on verbose output'
));
// add the foo subcommand
$foo_cmd = $parser->addCommand('foo', array(
'description' => 'output the given string with a foo prefix'
));
$foo_cmd->addOption('reverse', array(
'short_name' => '-r',
'long_name' => '--reverse',
'action' => 'StoreTrue',
'description' => 'reverse the given string before echoing it'
));
$foo_cmd->addArgument('text', array(
'description' => 'the text to output'
));
// add the bar subcommand
$bar_cmd = $parser->addCommand('bar', array(
'description' => 'output the given string with a bar prefix'
));
$bar_cmd->addOption('reverse', array(
'short_name' => '-r',
'long_name' => '--reverse',
'action' => 'StoreTrue',
'description' => 'reverse the given string before echoing it'
));
$bar_cmd->addArgument('text', array(
'description' => 'the text to output'
));
?>
Of course, we could also build our parser with sub-commands using an xml file, the xml file would be:
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <command> <description>A great program that can foo and bar !</description> <version>1.0.0</version> <option name="verbose"> <short_name>-v</short_name> <long_name>--verbose</long_name> <description>turn on verbose output</description> <action>StoreTrue</action> </option> <command> <name>foo</name> <description>output the given string with a foo prefix</description> <option name="reverse"> <short_name>-r</short_name> <long_name>--reverse</long_name> <description>reverse the string before echoing it</description> <action>StoreTrue</action> </option> <argument name="text"> <description>the text to output</description> </argument> </command> <command> <name>bar</name> <description>output the given string with a bar prefix</description> <option name="reverse"> <short_name>-r</short_name> <long_name>--reverse</long_name> <description>reverse the string before echoing it</description> <action>StoreTrue</action> </option> <argument name="text"> <description>the text to output</description> </argument> </command> </command>
Also note that sub-commands support aliases, for example it is possible to have an "update" command and specify "up" as an alias (shortcut) of the command:
<?php
$parser->addCommand('update', array(
'description' => 'Update the given package',
'aliases' => array('up'), // we can have more than 1 alias
));
?>
So far, so good, now let's see how to use this parser. When a user type a sub-command in the command line, the result object will have two properties set: the Console_CommandLine_Result::$command_name that will contain the name (as a string) of the sub-command typed by the user, and the Console_CommandLine_Result::$command that will contain a Console_CommandLine_Result instance, specific to the provided sub-command. For example with the above parser, we would do:
<?php
try {
$result = $parser->parse();
// find which command was entered
switch ($result->command_name) {
case 'foo':
// the user typed the foo command
// options and arguments for this command are stored in the
// $result->command instance:
print_r($result->command);
exit(0);
case 'bar':
// the user typed the bar command
// options and arguments for this command are stored in the
// $result->command instance:
print_r($result->command);
exit(0);
default:
// no command entered
exit(0);
}
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
Console_CommandLine allows you to register custom actions when you need it. Basically, you just create a action class that inherits from Console_CommandLine_Action and you call the Console_CommandLine::registerAction() method to register it.
Let's take a simple example, suppose we want to create a "range" action that would allow the user to type:
$ <yourprogram> -r 1,5
And in our Console_CommandLine_Result instance we would have:
// $result->options['range']: array('min' => 1, 'max' => 5)
Here's how we would do:
<?php
require_once 'Console/CommandLine.php';
require_once 'Console/CommandLine/Action.php';
class ActionRange extends Console_CommandLine_Action
{
public function execute($value=false, $params=array())
{
$range = explode(',', str_replace(' ', '', $value));
if (count($range) != 2) {
throw new Exception(sprintf(
'Option "%s" must be 2 integers separated by a comma',
$this->option->name
));
}
$this->setResult(array('min' => $range[0], 'max' => $range[1]));
}
}
// then we can register our action
Console_CommandLine::registerAction('Range', 'ActionRange');
// and now our action is available !
$parser = new Console_CommandLine();
$parser->addOption('range', array(
'short_name' => '-r',
'long_name' => '--range',
'action' => 'Range', // note our custom action
'description' => 'A range of two integers separated by a comma'
));
// etc...
?>
TODO: write this section
So there is a nice shell script that does everything it ever should do, and other people - without shell access - have to use it now. Or the script needs to be called regularly from outside the server it is running on. What now?
If your server runs a HTTP server with PHP, the problem is already solved
because Console_Commandline supports reading HTTP
$_GET
and $_POST
variables
out of the box.
The scripts task in this example is to print out the current time. The user may customize the output by specifying the format string.
Between shell and web access is only one difference: On the web, the text
shall be printed out in large letters by wrapping it in a
h1
tag.
Console_CommandLine by itself cares about reading
HTTP GET and POST variables when it is run through PHP's CGI or web
server (e.g. Apache with mod_php
). Let's try it:
<?php
require_once 'Console/CommandLine.php';
$parser = new Console_CommandLine();
$parser->description = 'Print out the current time';
$parser->version = '1.23';
$parser->addOption(
'format',
array(
'short_name' => '-f',
'long_name' => '--format',
'action' => 'StoreString',
'default' => 'Y-m-d H:i:s',
'description' => 'date()-compatible format string',
)
);
$result = $parser->parse();
echo date($result->options['format']) . "\n";
?>
Shell output
$ php web.php
2009-06-13 10:10:15
$ php web.php --help
Print out the current time
Usage:
web.php [options]
Options:
-f format, --format=format date()-compatible format string
-h, --help show this help message and exit
-v, --version show the program version and exit
$ php web.php --format=d.m.Y
13.06.2009
Web output without parameters
2009-06-13 10:10:15
Web output for web.php?--help
Print out the current time Usage: --help [options]
Options: -f format, --format=format date()-compatible format string -h,
--help show this help message and exit -v, --version show the program
version and exit
Web output for web.php?format=d.m.Y
13.06.2009
The functionality is there, but it does not look nice.
When using
GET
andPOST
parameters, Console_CommandLine accepts three types of parameter names:
short_name
as configured for the option
long_name
as configuredoption name as passed as first parameter to Console_CommandLine::addOption().
So in our example, one can call
web.php?-f=d.m.Y
web.php?--format=d.m.Y
web.php?format=d.m.Y
For arguments, the argument name has to be used as GET or POST key.
Building upon our previous example, we care about pretty output here:
Time wrapped in h1
tags.
Monospaced help text
Monospaced error text
While we did not catch any errors - as there are nearly none to produce in that example - the code contains it now for completeness.
First and foremost, we check if we are in an HTTP environment by checking
the SAPI (Server API) name. If we are not in CLI, we echo out
<h1>
and </h1>
.
To get the help text printed with monospaced text, a custom renderer is defined. For the sake of easiness, the default console renderer is extendet since it does nearly everything we need here.
<?php
require_once 'Console/CommandLine.php';
$parser = new Console_CommandLine();
$parser->description = 'Print out the current time';
$parser->version = '1.23';
$parser->addOption(
'format',
array(
'short_name' => '-f',
'long_name' => '--format',
'action' => 'StoreString',
'default' => 'Y-m-d H:i:s',
'description' => 'date()-compatible format string',
)
);
class HtmlRenderer extends Console_CommandLine_Renderer_Default
{
public function usage()
{
return '<pre>' . parent::usage() . '</pre>';
}
public function error($error)
{
return '<pre>' . parent::error($error) . '</pre>';
}
}
$parser->accept(new HtmlRenderer());
$http = (php_sapi_name() != 'cli');
try {
$result = $parser->parse();
if ($http) {
echo '<h1>';
}
echo date($result->options['format']) . "\n";
if ($http) {
echo '</h1>';
}
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
Basic example
<?php
// Include the Console_CommandLine package.
require_once 'Console/CommandLine.php';
// create the parser
$parser = new Console_CommandLine(array(
'description' => 'zip given files using the php zip module.',
'version' => '1.0.0'
));
// add an option to make the program verbose
$parser->addOption(
'verbose',
array(
'short_name' => '-v',
'long_name' => '--verbose',
'action' => 'StoreTrue',
'description' => 'turn on verbose output'
)
);
// add an option to delete original files after zipping
$parser->addOption(
'delete',
array(
'short_name' => '-d',
'long_name' => '--delete',
'action' => 'StoreTrue',
'description' => 'delete original files after zip operation'
)
);
// add the files argument, the user can specify one or several files
$parser->addArgument(
'files',
array(
'multiple' => true,
'description' => 'list of files to zip separated by spaces'
)
);
// add the zip file name argument
$parser->addArgument('zipfile', array('description' => 'zip file name'));
// run the parser
try {
$result = $parser->parse();
// write your program here...
print_r($result->options);
print_r($result->args);
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
Basic example (XML file)
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <command> <description>zip given files using the php zip module.</description> <version>1.0.0</version> <option name="verbose"> <short_name>-v</short_name> <long_name>--verbose</long_name> <description>turn on verbose output</description> <action>StoreTrue</action> </option> <option name="delete"> <short_name>-d</short_name> <long_name>--delete</long_name> <description>delete original files after zip operation</description> <action>StoreTrue</action> </option> <argument name="files"> <description>a list of files to zip together</description> <multiple>true</multiple> </argument> <argument name="zipfile"> <description>path to the zip file to generate</description> </argument> </command>
Basic example (PHP file)
<?php
// Include the Console_CommandLine package.
require_once 'Console/CommandLine.php';
// create the parser from xml file
$xmlfile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'ex2.xml';
$parser = Console_CommandLine::fromXmlFile($xmlfile);
// run the parser
try {
$result = $parser->parse();
// todo: your program here ;)
print_r($result->options);
print_r($result->args);
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
Sub-commands example
<?php
// Include the Console_CommandLine package.
require_once 'Console/CommandLine.php';
// create the parser
$parser = new Console_CommandLine(array(
'description' => 'A great program that can foo and bar !',
'version' => '1.0.0'
));
// add a global option to make the program verbose
$parser->addOption('verbose', array(
'short_name' => '-v',
'long_name' => '--verbose',
'action' => 'StoreTrue',
'description' => 'turn on verbose output'
));
// add the foo subcommand
$foo_cmd = $parser->addCommand('foo', array(
'description' => 'output the given string with a foo prefix'
));
$foo_cmd->addOption('reverse', array(
'short_name' => '-r',
'long_name' => '--reverse',
'action' => 'StoreTrue',
'description' => 'reverse the given string before echoing it'
));
$foo_cmd->addArgument('text', array(
'description' => 'the text to output'
));
// add the bar subcommand
$bar_cmd = $parser->addCommand('bar', array(
'description' => 'output the given string with a bar prefix'
));
$bar_cmd->addOption('reverse', array(
'short_name' => '-r',
'long_name' => '--reverse',
'action' => 'StoreTrue',
'description' => 'reverse the given string before echoing it'
));
$bar_cmd->addArgument('text', array(
'description' => 'the text to output'
));
// run the parser
try {
$result = $parser->parse();
if ($result->command_name) {
$st = $result->command->options['reverse']
? strrev($result->command->args['text'])
: $result->command->args['text'];
if ($result->command_name == 'foo') {
echo "Foo says: $st\n";
} else if ($result->command_name == 'bar') {
echo "Bar says: $st\n";
}
}
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
Sub-commands example (XML file)
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <command> <description>A great program that can foo and bar !</description> <version>1.0.0</version> <option name="verbose"> <short_name>-v</short_name> <long_name>--verbose</long_name> <description>turn on verbose output</description> <action>StoreTrue</action> </option> <command> <name>foo</name> <description>output the given string with a foo prefix</description> <option name="reverse"> <short_name>-r</short_name> <long_name>--reverse</long_name> <description>reverse the string before echoing it</description> <action>StoreTrue</action> </option> <argument name="text"> <description>the text to output</description> </argument> </command> <command> <name>bar</name> <description>output the given string with a bar prefix</description> <option name="reverse"> <short_name>-r</short_name> <long_name>--reverse</long_name> <description>reverse the string before echoing it</description> <action>StoreTrue</action> </option> <argument name="text"> <description>the text to output</description> </argument> </command> </command>
Sub-commands example (PHP file)
<?php
// Include the Console_CommandLine package.
require_once 'Console/CommandLine.php';
// create the parser
$xmlfile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'ex4.xml';
$parser = Console_CommandLine::fromXmlFile($xmlfile);
// run the parser
try {
$result = $parser->parse();
if ($result->command_name) {
$st = $result->command->options['reverse']
? strrev($result->command->args['text'])
: $result->command->args['text'];
if ($result->command_name == 'foo') {
echo "Foo says: $st\n";
} else if ($result->command_name == 'bar') {
echo "Bar says: $st\n";
}
}
} catch (Exception $exc) {
$parser->displayError($exc->getMessage());
}
?>
Console_Getopt provides functions for easily fetching and processing command-line arguments.
Getopt() supports two types of options:
short options
and long options
Calling a script with short and long options
# Using short optionsmyphpscript
-q -l en -o
# Using long options insteadmyphpscript
--quite --lang=en --option
# Mixing bothmyphpscript
-q --lang=en -o
You have to define which options you want to support. The second argument of getopt() requires a string containing all supported chars. For the example above this would be at least:
<?php
$shortoptions = "qlo";
?>
The order of the characters is not important. Often you have to define options with (optional) parameters. To express that a option requires a parameter, you have to add a colon. If the parameter is optional, add a double colon, ie:
<?php
$shortoptions = "ql:o::";
?>
this means the following script calls are permitted, ie.
-q
myphpscript -q -l en
myphpscript -o text
myphpscript -o
whilst
-l
is not permitted. The -l
option
requires a parameter, if the option is used.
The long options work equally, but they have to be defined in an array:
<?php
$longoptions = array("quite", "lang", "option");
?>
For defining optional parameters, use '='
and
'=='
like the colon in short options.
<?php
$longoptions = array("quite", "lang=", "option==");
?>
The return value is an array of two elements: the list of parsed options and the list of non-option command-line arguments. Each entry in the list of parsed options is a pair of elements - the first one specifies the option, and the second one specifies the option argument, if there was one, else the value is NULL.
array getopt (
array $args
, string $shortoptions
, array
$longoptions
= null
)
Parses the command-line options and returns them.
array $args
- an array of command-line arguments
string $shortoptions
- specifies the list of allowed
short options. See the "Options" section
for more information.
array $longoptions
- specifies the list of allowed
long options. Default is NULL
. See the "Options" section
for more information.
array
- two-element array containing the list of
parsed options and the non-option arguments or a PEAR_Error.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"Console_Getopt: option --$opt is ambiguous"
|
Two or more long options starts with the same character. | Change the naming of the options. It is also possible that the error is caused by a typing mistake. |
NULL |
"Console_Getopt: option --$opt requires an argument"
|
No parameter for a option was given. | Normally this is a user mistake. If the parameter is optional, you have to markup the parameter as optional in the option definitions. |
NULL |
"Console_Getopt: option --$opt doesn't allow an argument"
|
A parameter for a option was given. | Normally this is a user mistake. If the option requires a (optional) parameter, you have to markup it in the options definition. |
NULL |
"Console_Getopt: unrecognized option
--$opt "
|
Unknown option. | Normally this is a user mistake. If the option exists, you have to define them in the options definition. |
This function can not be called statically.
array readPHPArgv (
)
Reads the $argv PHP array across different PHP configurations. Will take care of the register_globals and register_argc_argv ini directives.
array
- array containing the options and
parameters or PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "Console_Getopt: Could not read cmd args (register_argc_argv=Off?)" | PHP does not provide the command-line arguments for the script. | Check "register_argc_argv" in your php.ini |
This function can not be called statically.
Using readPHPArgv()
<?php
$con = new Console_Getopt;
$args = $con->readPHPArgv();
array_shift($args);
$options = $con->getopt2($args, $shortopt);
?>
The following example shows how to setup short and long option arrays as well as reading the arguments passed via commandline. The example also introduces a helper method that converts the parsed parameters returned by Console_Getopt into a key-value-array.
<?php
/**
* Example how to get a key-value pair array
* from command line parameters with Console_Getopt.
*
* @link http://pear.php.net/bugs/bug.php?id=13902
*/
require_once 'Console/Getopt.php';
/**
* Make a key-value array.
* Since Console_Getopt does not provide such a method,
* we implement it ourselves.
*
* @params array $params Array of parameters from Console_Getopt::getopt2()
*
* @return array key-value pair array
*/
function &condense_arguments($params)
{
$new_params = array();
foreach ($params[0] as $param) {
$new_params[$param[0]] = $param[1];
}
return $new_params;
}
$cg = new Console_Getopt();
$args = $cg->readPHPArgv();
array_shift($args);
$shortOpts = 'u:g:';
$longOpts = array('user=', 'group=');
$params = $cg->getopt2($args, $shortOpts, $longOpts);
if (PEAR::isError($params)) {
echo 'Error: ' . $params->getMessage() . "\n";
exit(1);
}
var_dump(condense_arguments($params));
/*
When called as follows:
associative.php -u jason -g argonauts
you will get this output:
array(2) {
["u"]=>
string(5) "jason"
["g"]=>
string(9) "argonauts"
}
*/
?>
Console_ProgressBar displays a customizable progress bar in the console/terminal.
Some progress bar examples
This will display a very simple bar:
=====================================================
A more complicated bar, showing current status in numbers as well:
* 3/7 [==========================>-------------------------------------] 42.86%
Showing elapsed time, and using ANSI codes:
- 109/345 [========>------------------] 31.59% Elapsed Time: 00:03.94
Showing an estimate for the remaining time:
- 105/345 [============>-------------------------------] 30.43% ETA: 00:11.52
require_once "Console/ProgressBar.php"
Create a new instance of Console_ProgressBar.
Call update() whenever some progress has been made.
(optional) erase() after you're finished.
Simple progressbar example
<?php
require_once 'Console/ProgressBar.php';
$bar = new Console_ProgressBar('[%bar%] %percent%', '=>', ' ', 80, 7);
//do some processing here
for ($i = 0; $i <= 7; $i++) {
$bar->update($i);
sleep(1);
}
echo "\n";
?>
[=======================================> ] 57.14%
In the example we create the progress bar instance with five parameters:
The format string
, bar string
,
empty string
, console width
and the
target number
.
The first parameter, format string
, defines the layout
of the whole progress bar. It may contain any character and has some
variables that get replaced:
%bar%
is the actual progress bar.
%current%
is the current value set via
update().
%max%
is replaced with the maximum value set
by the constructor (target number).
%fraction%
- the same as
%current%/%max%
.
%percent%
- status in percent.
%elapsed%
- elapsed time.
%estimate%
- an estimation of how long
the progress will take.
The second argument is the string that is going to fill the progress bar. In the above example, the string "=>" was used. If the string you pass is too short (like "=>" in this example), the leftmost character is used to pad it to the needed size. If the string you pass is too long, excessive characters are stripped from the left.
The third argument is the string that fills the "empty" space in the progress bar. In the above example, that would be "-". If the string you pass is too short (like "-" in this example), the rightmost character is used to pad it to the needed size. If the string you pass is too long, excessive characters are stripped from the right.
The fourth argument specifies the width of the display. A normal console/terminal is 80 chars, so pass 80 here. Currently there is no method to determine the current width of a terminal.
The fifth argument is the target number of the progress bar. For example, if you wanted to display a progress bar for a download of a file that is 115 KB big, you would pass 115 here.
For the full documentation of all parameters, see the API documentation of reset().
After setting up the progress bar, you can start doing the real work - up- or downloading files, calculating something etc. Whenever you did a step forward, call update() with the current step number.
When you're finished, you may want to remove the progress bar from screen - use erase() for this.
Console_Table provides methods to display tabular data in an ASCII terminal/shell.
Console_Table helps you to display tabular data on a terminal/shell/console. It provides features similar to those of HTML tables: Header and body (data) parts, alignment of columns, cell padding, multi line cells, border "styles" and column filters.
The class be filled with data by adding rows, adding cols or even importing whole arrays.
When adding whole arrays, e.g. a database result, you might want to format the data of certain columns before displaying them. Attaching a formatting callback to a column using Console_Table::addFilter() helps you with that task.
There might be times when you have an array and just want to get it out to the user, without caring about all the small details. Console_Table::fromArray() is the single-line-of-code tool in such cases.
In combination with PEAR's Console_Color package you can format cells by using e.g. different colors for different cell values - very convenient in combination with column filters.
To get the feeling how Console_Table is used, try out the following examples.
Here we are following the basic steps to get some data out on the shell:
Creating a Console_Table object
Adding the header row
Adding some data rows
Rendering the table to the shell
<?php
require_once 'Console/Table.php';
$tbl = new Console_Table();
$tbl->setHeaders(
array('Language', 'Year')
);
$tbl->addRow(array('PHP', 1994));
$tbl->addRow(array('C', 1970));
$tbl->addRow(array('C++', 1983));
echo $tbl->getTable();
?>
This examples would display as following:
+----------+------+
| Language | Year |
+----------+------+
| PHP | 1994 |
| C | 1970 |
| C++ | 1983 |
+----------+------+
In this example we use Console_Table::addFilter() to colorize our table cells according to their value. Colorization is very easy using PEAR's Console_Color package. Just make sure you set the "color" parameter of Console_Color's constructor - otherwise you will see weird column widths.
After filling our table object with headers and data, we add an output filter
by specifying a callback function.
The first parameter to addFilter
is the column
number that shall be formatted, beginning with 0.
Due to backwards compatibility with PHP4,
addFilter
requires a variable as second parameter,
even if you just want to specfiy a simple function name.
We also have Console_Table align the "Profit" column right so that the commas are aligned equally.
<?php
require_once 'Console/Table.php';
require_once 'Console/Color.php';
//those could come e.g. from database
$data = array(
array(2001, 128.23),
array(2002, 256.42),
array(2003, 10.21),
array(2004, -25.79),
array(2005, 0),
array(2006, 982.12),
);
//prepare table
$tbl = new Console_Table(
CONSOLE_TABLE_ALIGN_LEFT, CONSOLE_TABLE_BORDER_ASCII,
1, null,
true//this is important when using Console_Color
);
$tbl->setHeaders(
array('Year', 'Profit')
);
$tbl->addData($data);
//add filter callback to colorize our profit column values
$callback = 'colorize';
$tbl->addFilter(1, $callback);
//Values should be aligned right
$tbl->setAlign(1, CONSOLE_TABLE_ALIGN_RIGHT);
echo $tbl->getTable();
/**
* Wraps Console color codes around $value,
* depending if its larger or smaller 0.
*
* @param float $value Value (column 1)
*
* @return string Colorful value
*/
function colorize($value)
{
$str = number_format($value, 2, ',', '');
if ($value < 0) {
return Console_Color::convert('%r' . $str . '%n');
} else if ($value == 0) {
return $str;
} else {
return Console_Color::convert('%g' . $str . '%n');
}
}
?>
The code above creates the following output, except that "Profit" values larger than 0 are colored in green, while the ones smaller zero are in red.
+------+--------+
| Year | Profit |
+------+--------+
| 2001 | 128,23 |
| 2002 | 256,42 |
| 2003 | 10,21 |
| 2004 | -25,79 |
| 2005 | 0,00 |
| 2006 | 982,12 |
+------+--------+
Console_Table
Console_Table::Console_Table
(
string $align = CONSOLE_TABLE_ALIGN_LEFT
, string $border = CONSOLE_TABLE_BORDER_ASCII
, integer $padding = 1
, string $charset = null
, boolean $color = false
)
Constructor
void Console_Table::addCol (
array $col_data
, integer $col_id
, integer $row_id
)
Adds a column to the table
$col_data
The data of the column. Can be numeric or associative array
$col_id
The column index to populate
$row_id
If starting row is not zero, specify it here
No exceptions thrown.
This function can not be called statically.
void Console_Table::addData (
array $data
, integer $col_id
, integer $row_id
)
Adds data to the table. Argument should be a two dimensional array containing the data to be added.
$data
The data to add to the table
$col_id
Optional starting column ID
$row_id
Optional starting row ID
No exceptions thrown.
This function can not be called statically.
void Console_Table::addRow (
array $row
, array $append
= true
)
Adds a row to the table
$row
The row data to add
$append
Whether to append or prepend the row
No exceptions thrown.
This function can not be called statically.
void
Console_Table::getTable
(
)
Returns the table in wonderful ASCII art
No exceptions thrown.
This function can not be called statically.
void Console_Table::insertRow (
array $row
, integer $row_id
)
Inserts a row after a given row number in the table. If $row_id is not given it will prepend the row.
$row
The data to insert
$row_id
Row number to insert before
No exceptions thrown.
This function can not be called statically.
void Console_Table::setHeaders (
array $headers
)
Sets the headers for the columns
$headers
The column headers
No exceptions thrown.
This function can not be called statically.
Provides database-related Packages
A unified API for accessing SQL databases
This package has been superseded. Please use MDB2 for new projects.
To connect to a database through PEAR::DB, you have to create a valid DSN - data source name. This DSN consists in the following parts:
phptype
:
Database backend used in PHP (i.e. mysql
, odbc
etc.)
dbsyntax
:
Database used with regards to SQL syntax etc. When using ODBC as the
phptype
, set this to the DBMS type the ODBC
driver is connecting to. Examples: access
,
db2
, mssql
,
navision
, solid
, etc.
protocol
:
Communication protocol to use ( i.e. tcp
,
unix
etc.)
hostspec
:
Host specification (hostname[:port]
)
database
:
Database to use on the DBMS server
username
:
User name for login
password
:
Password for login
proto_opts
:
Maybe used with protocol
option
:
Additional connection options in URI query string format.
options get separated by &
The format of the supplied DSN is in its fullest form:
phptype(dbsyntax)://username:password@protocol+hostspec/database?option=value
Most variations are allowed:
phptype://username:password@protocol+hostspec:110//usr/db_file.db phptype://username:password@hostspec/database phptype://username:password@hostspec phptype://username@hostspec phptype://hostspec/database phptype://hostspec phptype:///database phptype:///database?option=value&anotheroption=anothervalue phptype(dbsyntax) phptype
The currently supported database backends are:
dbase -> dBase fbsql -> FrontBase (functional since DB 1.7.0) ibase -> InterBase (functional since DB 1.7.0) ifx -> Informix msql -> Mini SQL (functional since DB 1.7.0) mssql -> Microsoft SQL Server (NOT for Sybase. Compile PHP --with-mssql) mysql -> MySQL (for MySQL <= 4.0) mysqli -> MySQL (for MySQL >= 4.1) (requires PHP 5) (since DB 1.6.3) oci8 -> Oracle 7/8/9 odbc -> ODBC (Open Database Connectivity) pgsql -> PostgreSQL sqlite -> SQLite sybase -> Sybase
With an up-to-date version of DB, you can use a second DSN format
phptype(syntax)://user:pass@protocol(proto_opts)/database
If your database
, option
values,
username
or password
contain characters used to delineate DSN parts, you
can escape them via URI hex encodings:
: = %3a / = %2f @ = %40 + = %2b ( = %28 ) = %29 ? = %3f = = %3d & = %26
Please note, that some features may be not supported by all database backends. Please refer to the PEAR DB extensions status document located at:
/pear/base/dir/DB/doc/STATUS
to get a detailed list about what features are supported by which backend.
Connect to database through a socket
mysql://user@unix(/path/to/socket)/pear
Connect to database on a non standard port
pgsql://user:pass@tcp(localhost:5555)/pear
Connect to SQLite on a Unix machine using options
sqlite:////full/unix/path/to/file.db?mode=0666
Connect to SQLite on a Windows machine using options
sqlite:///c:/full/windows/path/to/file.db?mode=0666
Connect to MySQLi using SSL
mysqli://user:pass@localhost/pear?key=client-key.pem&cert=client-cert.pem
Connecting to MS Access sometimes requires admin
as the user name
odbc(access)://admin@/datasourcename
Connecting to ODBC with a specific cursor
odbc(access)://admin@/datasourcename?cursor=SQL_CUR_USE_ODBC
To connect to a database you have to use the function connect(), which requires a valid DSN as the first parameter. This parameter can either be a string or an array. If using an array, the array used gets merged with the default information:
$dsn = array( 'phptype' => false, 'dbsyntax' => false, 'username' => false, 'password' => false, 'protocol' => false, 'hostspec' => false, 'port' => false, 'socket' => false, 'database' => false, );
Any elements you set override the defaults and the remainder stay at their defaults.
The second parameter is the optional $options
array that can contain runtime configuration settings for this package.
See
setOption() for more information on the
available settings.
In case of success you get a new instance of the database class. It is strongly recommended to check this return value with isError().
To disconnect use the method disconnect() from your database class instance.
Connect and disconnect
<?php
require_once 'DB.php';
$dsn = 'pgsql://someuser:apasswd@localhost/thedb';
$options = array(
'debug' => 2,
'portability' => DB_PORTABILITY_ALL,
);
$db =& DB::connect($dsn, $options);
if (PEAR::isError($db)) {
die($db->getMessage());
}
// ...
$db->disconnect();
?>
Connect using an array for the DSN information
<?php
require_once 'DB.php';
$dsn = array(
'phptype' => 'pgsql',
'username' => 'someuser',
'password' => 'apasswd',
'hostspec' => 'localhost',
'database' => 'thedb',
);
$options = array(
'debug' => 2,
'portability' => DB_PORTABILITY_ALL,
);
$db =& DB::connect($dsn, $options);
if (PEAR::isError($db)) {
die($db->getMessage());
}
?>
When connecting to SQLite using a DSN array, the value
of the mode
element must be a string:
<?php
$dsn = array(
'phptype' => 'sqlite',
'database' => 'thedb',
'mode' => '0644',
);
?>
Connect to MySQLi via SSL using an array for the DSN information
The ssl
element of the
$options
array must be set to
TRUE in order for SSL to work. Each of the extra
elements in the $dsn
array
(key
through cipher
in the example below) are optional.
<?php
require_once 'DB.php';
$dsn = array(
'phptype' => 'mysqli',
'username' => 'someuser',
'password' => 'apasswd',
'hostspec' => 'localhost',
'database' => 'thedb',
'key' => 'client-key.pem',
'cert' => 'client-cert.pem',
'ca' => 'cacert.pem',
'capath' => '/path/to/ca/dir',
'cipher' => 'AES',
);
$options = array(
'ssl' => true,
);
$db =& DB::connect($dsn, $options);
if (PEAR::isError($db)) {
die($db->getMessage());
}
?>
Connect to a PostgreSQL database via a socket
<?php
require_once 'DB.php';
$dsn = 'pgsql://someuser:apasswd@unix(/tmp)/thedb';
$options = array(
'debug' => 2,
'portability' => DB_PORTABILITY_ALL,
);
$db =& DB::connect($dsn, $options);
if (PEAR::isError($db)) {
die($db->getMessage());
}
?>
PEAR DB provides several methods for querying databases. The most direct method is query(). It takes a SQL query string as an argument. There are three possible returns: a new DB_result object for queries that return results (such as SELECT queries), DB_OK for queries that manipulate data (such as INSERT queries) or a DB_Error object on failure.
Doing a query
<?php
// Create a valid DB object named $db
// at the beginning of your program...
require_once 'DB.php';
$db =& DB::connect('pgsql://usr:pw@localhost/dbnam');
if (PEAR::isError($db)) {
die($db->getMessage());
}
// Proceed with a query...
$res =& $db->query('SELECT * FROM clients');
// Always check that result is not an error
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
query() can be used instead of
prepare() and
execute(), if you
set the $params
parameter
and your query uses placeholders.
Using query in prepare/execute mode with a scalar parameter
<?php
// Once you have a valid DB object named $db...
$sql = 'select * from clients where clientid = ?';
$data = 53;
$res =& $db->query($sql, $data);
// Always check that result is not an error
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
Using query in prepare/execute mode with an array parameter
<?php
// Once you have a valid DB object named $db...
$sql = 'select * from clients where clientid = ? and statusid = ?';
$data = array(53, 4);
$res =& $db->query($sql, $data);
// Always check that result is not an error
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
The DB_result object provides two methods for fetching data from rows of a result set: fetchRow() and fetchInto().
fetchRow() returns the row's data. fetchInto() assigns the row's data to a variable you provide and returns DB_OK.
The result pointer gets moved to the next row each time these methods are called. NULL is returned when the end of the result set is reached.
DB_Error is returned if an error is encountered.
Fetching a result set
<?php
// Create a valid DB object named $db
// at the beginning of your program...
require_once 'DB.php';
$db =& DB::connect('pgsql://usr:pw@localhost/dbnam');
if (PEAR::isError($db)) {
die($db->getMessage());
}
// Proceed with getting some data...
$res =& $db->query('SELECT * FROM mytable');
// Get each row of data on each iteration until
// there are no more rows
while ($res->fetchInto($row)) {
// Assuming DB's default fetchmode is DB_FETCHMODE_ORDERED
echo $row[0] . "\n";
}
// Or, you could have done the same thing using fetchRow()
// while ($row =& $res->fetchRow()) {
// // Assuming DB's default fetchmode is DB_FETCHMODE_ORDERED
// echo $row[0] . "\n";
// }
?>
The data from the row of a query result can be placed into one of three constructs: an ordered array (with column numbers as keys), an associative array (with column names as keys) or an object (with column names as properties).
DB_FETCHMODE_ORDERED (default)
Array
(
[0] => 28
[1] => hi
)
DB_FETCHMODE_ASSOC
Array
(
[a] => 28
[b] => hi
)
DB_FETCHMODE_OBJECT
stdClass Object
(
[a] => 28
[b] => hi
)
NOTE: When a query contains the same column name more than once (such as when joining tables which have duplicate column names) and the fetch mode is DB_FETCHMODE_ASSOC or DB_FETCHMODE_OBJECT, the data from the last column with a given name will be the one returned. There are two immediate options to work around this issue:
People.Name AS PersonName
TIP: If you are running into this issue, it likely indicates poor planning of the database schema. Either data is needlessly being duplicated or the same names are being used for different kinds of data.
You can set the fetch mode each time you call a fetch method and/or you can set the default fetch mode for the whole DB instance by using the setFetchMode() method.
Determining fetch mode per call
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM users');
while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
echo $row['id'] . "\n";
}
?>
Changing default fetch mode
<?php
// Once you have a valid DB object named $db...
$db->setFetchMode(DB_FETCHMODE_ASSOC);
$res =& $db->query('SELECT * FROM users');
while ($res->fetchInto($row)) {
echo $row['id'] . "\n";
}
?>
The PEAR DB fetch system also supports an extra parameter to the fetch statement. So you can fetch rows from a result by number. This is especially helpful if you only want to show sets of an entire result (for example in building paginated HTML lists), fetch rows in an special order, etc.
Fetching by number
<?php
// Once you have a valid DB object named $db...
// the row to start fetching
$from = 50;
// how many results per page
$resPage = 10;
// the last row to fetch for this page
$to = $from + $resPage;
foreach (range($from, $to) as $rowNum) {
if (!$res->fetchInto($row, DB_FETCHMODE_ORDERED, $rowNum)) {
break;
}
echo $row[0] . "\n";
}
?>
The DB_common object provides several methods that make data retrieval easy by combining the processes of running of the query string you provide, putting the returned information into a PHP data structure and freeing the results. These methods include getOne(), getRow(), getCol(), getAssoc() and getAll().
Once you finish using a result set, if your script continues for a while, it's a good idea to save memory by freeing the result set via Use free().
Freeing
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT name, address FROM clients');
while ($res->fetchInto($row)) {
echo $row['name'] . ', ' . $row['address'] . "\n";
}
$res->free();
?>
With DB there are four ways to retrieve useful information about the query result sets themselves:
numRows() tells how many rows are in a SELECT query result
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM phptest');
echo $res->numRows();
?>
numCols() tells how many columns are in a SELECT query result
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM phptest');
echo $res->numCols();
?>
affectedRows() tells how many rows were altered by a data change query (INSERT, UPDATE or DELETE)
<?php
// remember that this statement won't return a result object
$db->query('DELETE * FROM clients');
echo 'I have deleted ' . $db->affectedRows() . ' clients';
?>
tableInfo() returns an associative array with information about the columns in a SELECT query result
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM phptest');
print_r($db->tableInfo($res));
// That usage works for DB 1.6.0 or later.
// Below is the syntax for earlier versions:
print_r($res->tableInfo());
?>
prepare() and execute*() give you more power and flexibilty for query execution. Prepare/execute mode is helpful when you have to run the same query several times but with different values in it, such as adding a list of addresses into a database.
Another place prepare/execute is useful is supporting databases which have different SQL syntaxes. Imagine you want to support two databases with different INSERT syntax:
Correspondending to create multi-lingual scripts you can create a array with queries like this:
<?php
$statement['db1']['INSERT_PERSON'] = 'INSERT INTO person
(surname, name, age) VALUES (?, ?, ?)';
$statement['db2']['INSERT_PERSON'] = 'INSERT INTO person
SET surname=?, name=?, age=?';
?>
To use the features above, you have to do two steps. Step one is to prepare the statement and the second is to execute it.
To start out, you need to prepare() a generic SQL statement. Create a generic statement by writing the SQL query as usual:
SELECT surname, name, age FROM person WHERE name = 'name_to_find' AND age < age_limit
Then substitute "placeholders" for the literal values which will be provided at run time:
SELECT surname, name, age FROM person WHERE name = ? AND age < ?
Then pass this SQL statement to prepare(), which returns a statement handle to be used when calling execute().
prepare() can handle different types of placeholders (a.k.a. wildcards).
?
- (recommended) stands for a scalar
value like strings or numbers. The value will be automatically
escaped and quoted according to the current DBMS's requirements.
!
- stands for a scalar value and
will inserted into the statement "as is".
&
- requires an existing filename,
the content of this file will be included into the statement
(i.e. for saving binary data of a graphic file in a database)
Use backslashes to escape placeholder characters if you don't want them to be interpreted as placeholders:
UPDATE foo SET col=? WHERE col='over \& under'
After preparing the statement, you can execute the query. This means to assign the variables to the prepared statement. To do this, execute() requires two arguments: the statement handle returned by prepare() and a scalar or array with the values to assign.
Passing scalars to execute()
<?php
// Once you have a valid DB object named $db...
$sth = $db->prepare('INSERT INTO numbers (number) VALUES (?)');
$db->execute($sth, 1);
$db->execute($sth, 8);
?>
When a prepared statement has multiple placeholders, you must use an array to pass the values to execute(). The first entry of the array represents the first placeholder, the second the second placeholder, etc. The order is independent of the type of placeholder used.
Passing an array to execute()
<?php
// Once you have a valid DB object named $db...
$sth = $db->prepare('INSERT INTO numbers VALUES (?, ?, ?)');
$data = array(1, 'one', 'en');
$db->execute($sth, $data);
?>
The values passed in
$data
must be literals. Do not submit SQL functions (for exampleCURDATE()
). SQL functions that should be performed at execution time need to be put in the prepared statement. Similarly, identifiers (i.e. table names and column names) can not be used because the names get validated during the prepare phase.
DB contains a process for executing several queries at once. So, rather than having to execute them manually, like this:
Passing arrays to execute()
<?php
// Once you have a valid DB object named $db...
$alldata = array(array(1, 'one', 'en'),
array(2, 'two', 'to'),
array(3, 'three', 'tre'),
array(4, 'four', 'fire'));
$sth = $db->prepare('INSERT INTO numbers VALUES (?, ?, ?)');
foreach ($alldata as $row) {
$db->execute($sth, $row);
}
?>
which would issue four queries:
INSERT INTO numbers VALUES ('1', 'one', 'en') INSERT INTO numbers VALUES ('2', 'two', 'to') INSERT INTO numbers VALUES ('3', 'three', 'tre') INSERT INTO numbers VALUES ('4', 'four', 'fire')
you can use executeMultiple() to avoid the explicit foreach in the eample above:
Using executeMultiple() instead of execute()
<?php
// Once you have a valid DB object named $db...
$alldata = array(array(1, 'one', 'en'),
array(2, 'two', 'to'),
array(3, 'three', 'tre'),
array(4, 'four', 'fire'));
$sth = $db->prepare('INSERT INTO numbers VALUES (?, ?, ?)');
$db->executeMultiple($sth, $alldata);
?>
The result is the same. If one of the records failed, the unfinished records will not be executed.
execute*() has three possible returns: a new DB_result object for queries that return results (such as SELECT queries), DB_OK for queries that manipulate data (such as INSERT queries) or a DB_Error object on failure
autoPrepare() and autoExecute() reduce the need to write boring INSERT or UPDATE SQL queries which are difficult to maintain when you add a field for instance.
Imagine you have a 'user' table with 3 fields (id, name and country). You have to write sql queries like:
INSERT INTO table (id, name, country) VALUES (?, ?, ?) UPDATE table SET id=?, name=?, country=? WHERE ...
If you add a field ('birthYear' for example), you have to rewrite your queries which is boring and can lead to bugs (if you forget one query for instance).
With autoPrepare(), you don't have to write your insert or update queries. For example:
<?php
// Once you have a valid DB object named $db...
$table_name = 'user';
$table_fields = array('id', 'name', 'country');
$sth = $db->autoPrepare($table_name, $table_fields,
DB_AUTOQUERY_INSERT);
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
?>
In this example, autoPrepare() will build the following SQL query:
INSERT INTO user (id, name, country) VALUES (?, ?, ?)
And then, it will call prepare() with it.
To add records, you have to use execute() or executeMultiple() like:
<?php
// ... contining from the example above...
$table_values = array(1, 'Fabien', 'France');
$res =& $db->execute($sth, $table_values);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
So, you don't have to write any SQL INSERT queries! And it works with UPDATE queries too. For flexibility reasons, you have only to write the WHERE clause of the query. For instance:
<?php
// Once you have a valid DB object named $db...
$table_name = 'user';
$table_fields = array('name', 'country');
$table_values = array('Bob', 'USA');
$sth = $db->autoPrepare($table_name, $table_fields,
DB_AUTOQUERY_UPDATE, 'id = 1');
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
$res =& $db->execute($sth, $table_values);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
autoPrepare() will build the following query:
UPDATE user SET name=?, country=? WHERE id=1
Then, it will call prepare() with it.
Be careful, if you don't specify any WHERE clause, all the records of the table will be updated.
Using autoExecute() is the easiest way to do insert or update queries. It is a mix of autoPrepare() and execute().
You only need an associative array (key => value) where keys are fields names and values are corresponding values of these fields. For instance:
<?php
// Once you have a valid DB object named $db...
$table_name = 'user';
$fields_values = array(
'id' => 1,
'name' => 'Fabien',
'country' => 'France'
);
$res = $db->autoExecute($table_name, $fields_values,
DB_AUTOQUERY_INSERT);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
And that's all! The following query is built and executed:
INSERT INTO user (id, name, country) VALUES (1, 'Fabien', 'France')
And it's the same thing for UPDATE queries:
<?php
// Once you have a valid DB object named $db...
$table_name = 'user';
$fields_values = array(
'name' => 'Fabien',
'country' => 'France'
);
$res = $db->autoExecute($table_name, $fields_values,
DB_AUTOQUERY_UPDATE, 'id = 1');
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
which prepares and executes the following query:
UPDATE user SET name='Fabien', country='France' WHERE id = 1
Be careful, if you don't specify any WHERE statement, all the records of the table will be updated.
The values passed in
$data
must be literals. Do not submit SQL functions (for exampleCURDATE()
). SQL functions that should be performed at execution time need to be put in the prepared statement.
Each database management system (DBMS) has its own behaviors. For example, some databases capitalize field names in their output, some lowercase them, while others leave them alone. These quirks make it difficult to port your scripts over to another server type. PEAR DB strives to overcome these differences so your program can switch between DBMS's without any changes.
You control which portability modes are enabled by using
the portability
configuration option.
Configuration options are set via
connect() and
setOption().
The portability modes are bitwised, so they can be combined using
|
and removed using ^
.
See the examples section below on how to do this.
DB_PORTABILITY_ALL
turn on all portability features
DB_PORTABILITY_DELETE_COUNT
force reporting the number of rows deleted.
Some DBMS's don't count the number of rows deleted when performing
simple DELETE FROM tablename
queries. This mode
tricks such DBMS's into telling the count by adding
WHERE 1=1
to the end of DELETE
queries.
DB_PORTABILITY_ERRORS
makes certain error messages in certain drivers compatible with those from other DBMS's
Driver | Description | Old Constant | New Constant |
---|---|---|---|
mysql, mysqli | unique and primary key constraints | DB_ERROR_ALREADY_EXISTS | DB_ERROR_CONSTRAINT |
mysql, mysqli | not-null constraints | DB_ERROR_CONSTRAINT | DB_ERROR_CONSTRAINT_NOT_NULL |
odbc(access) | MS's ODBC driver mistakenly reports 'no such field' as code 07001, which means 'too few parameters.' When this option is on, that code gets remapped. | DB_ERROR_MISMATCH | DB_ERROR_NOSUCHFIELD |
DB_PORTABILITY_LOWERCASE
convert names of tables and fields to lower case when using get*(), fetch*() and tableInfo()
DB_PORTABILITY_NONE (default)
turn off all portability features
DB_PORTABILITY_NULL_TO_EMPTY
convert null values to empty strings in data output by get*() and fetch*(). Needed because Oracle considers empty strings to be null, while most other DBMS's know the difference between empty and null.
DB_PORTABILITY_NUMROWS
enable hack that makes numRows() work in Oracle
DB_PORTABILITY_RTRIM
right trim the data output by get*() and fetch*()
Some of this functionality used to be handled by the now deprecated
optimize
option. For backwards compatibility, when
this option is set to portability
, the following
databases get these portability modes turned on:
oci8: DB_PORTABILITY_LOWERCASE and DB_PORTABILITY_DELETE_COUNT
fbsql, mysql, mysqli, sqlite: DB_PORTABILITY_DELETE_COUNT
When the optimize
option gets set to
performance
the portability mode is switched to
DB_PORTABILITY_NONE.
Turning on all portability options while connecting
<?php
require_once 'DB.php';
$dsn = 'mysql://user:password@host/database'
$options = array(
'debug' => 2,
'portability' => DB_PORTABILITY_ALL,
);
$db =& DB::connect($dsn, $options);
if (PEAR::isError($db)) {
die($db->getMessage());
}
?>
Using setOption() to enable portability for lowercasing and trimming
<?php
// Once you have a valid DB object named $db...
$db->setOption('portability',
DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);
?>
Using setOption() to enable all portability options except trimming
<?php
// Once you have a valid DB object named $db...
$db->setOption('portability',
DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);
?>
Sequences are a way of offering unique IDs for data rows. If you do most of your work with e.g. MySQL, think of sequences as another way of doing AUTO_INCREMENT.
It's quite simple, first you request an ID, and then you insert that value in the ID field of the new row you're creating. You can have more than one sequence for all your tables, just be sure that you always use the same sequence for any particular table. To get the value of this unique ID use nextId(), if a sequence doesn't exists, it will be created automatically.
The sequence is automatically incremented each time nextId() is called.
Using a sequence
<?php
// Once you have a valid DB object named $db...
$id = $db->nextId('mySequence');
if (PEAR::isError($id)) {
die($id->getMessage());
}
// Use the ID in your INSERT query
$res =& $db->query("INSERT INTO myTable (id, text) VALUES ($id, 'foo')");
?>
When using PEAR DB's sequence methods, we strongly advise using these methods for all procedures, including the creation of the sequences. Do not use PEAR DB's methods to access sequences that were created directly in the DBMS.
If you have a compelling reason to ignore this advice, be aware that the
$seq_name
argument given to all of PEAR DB's sequence methods are modified before DB calls the underlying DBMS.
$seq_name
is passed through PHP's sprintf() function using the value from theseqname_format
option as sprintf()'s format argument. The defaultseqname_format
is%s_seq
. So, for example, if you useperson_id_sequence
as the$seq_name
, PEAR DB will change that name toperson_id_sequence_seq
when querying the DBMS about creating/accessing/updating the sequence.The
seqname_format
can be modified when connect()'ing or via setOption().Please note that you may need to change the
seqname_format
option to represent a quoted form of the sequence name if the sequence names contain characters that cannot be used unquoted within queries. For example, PostgreSQL users with uppercase table names will probably need to use"%s_seq"
(note the quotes) for queries to work as expected.
This page gives an overview about the methods that allow you to fetch entire result sets without looping over single result rows: getOne(), getRow(), getCol(), getAssoc() and getAll().
Our data source is the following table:
id | nickname | |
---|---|---|
1 | pinky | pinky@domination.org |
2 | thebrain | brain@dominators.net |
A simple var_dump() is used to make clear in which use cases the methods can be used:
<?php
require_once 'DB.php';
$db = DB::connect('mysql://user:pass@localhost/example');
var_dump(
$db->getOne('SELECT nickname, id, email FROM users')
);
?>
getRow() fetches the first row.
array(3) {
[0] => string(5) "pinky"
[1] => string(1) "1"
[2] => string(20) "pinky@domination.org"
}
getCol() fetches the first column of all rows.
array(2) {
[0]=> string(5) "pinky"
[1]=> string(8) "thebrain"
}
getAssoc() fetches all rows into an array, using the first colum of the rows as array key. This column is removed from the array itself.
array(2) {
["pinky"] => array(2) {
[0] => string(1) "1"
[1] => string(20) "pinky@domination.org"
}
["thebrain"] => array(2) {
[0] => string(1) "2"
[1] => string(20) "brain@dominators.net"
}
}
getAll() fetches all columns from all rows into an array.
array(2) {
[0] => array(3) {
[0] => string(5) "pinky"
[1] => string(1) "1"
[2] => string(20) "pinky@domination.org"
}
[1] => array(3) {
[0] => string(8) "thebrain"
[1] => string(1) "2"
[2] => string(20) "brain@dominators.net"
}
}
The main DB class is simply a container class with some static methods for creating DB objects.
object connect (
mixed $dsn
,
array $options = array()
)
Creates a new DB connection object and connect to the specified database
$dsn
Data Source Name. String formats are described in the DSN section while array formats are covered in the "Intro - Connect" section.
$options
An optional argument can contain runtime configuration settings for this package. See setOption() for more information on the available settings.
object - a new DB object or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_NOT_FOUND | not found | The database specific class was not found. |
Check the $dsn and make sure
to have an complete installation of the
DB-package and that you database
is supported by DB.
|
This function should be called statically.
boolean isError (
mixed $value
)
Checks whether a result code from a DB method is a DB_Error object or not.
You're generally better off using PEAR::isError() instead of the DB specific isError().
$value
Variable to check
boolean - TRUE if $value
is a DB_Error object
This function should be called statically.
Using isError()
<?php
require_once 'DB.php';
$db =& DB::connect('pgsql://usr:pw@localhost/dbnam');
if (PEAR::isError($db)) {
die($db->getMessage());
}
?>
integer affectedRows (
)
Number of rows affected by a data manipulation query (for example INSERT, UPDATE or DELETE). Returns 0 for SELECT queries.
integer - number of rows or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_NOT_CAPABLE | DB backend not capable | Function is not supported by the database backend | Switch to another database system, if you really need this feature. |
This function can not be called statically.
Using affectedRows()
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('DELETE * FROM clients');
if (PEAR::isError($res)) {
die($res->getMessage());
}
echo 'I have deleted ' . $db->affectedRows() . ' clients';
?>
mixed autoCommit (
boolean $onoff
= false
)
Turns auto-commit on or off.
$onoff
TRUE to turn auto-commit on. FALSE to turn auto-commit off.
Error code | Error message | Reason | Solution |
---|---|---|---|
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. |
This function can not be called statically.
When using MySQL as your DBMS, transactions can only be used when the tables in question use the InnoDB format.
Using autocommit()
<?php
$db =& DB::connect('ibase(firebird)://user:pw@localhost/path/file');
$db->autoCommit(false);
$db->query('CREATE TABLE blah (a integer)');
$db->query('CREATE TABLE blue (b integer)');
$db->commit();
$db->query('INSERT INTO blah (a) VALUES (11)');
$db->query('INSERT INTO blah (a) VALUES (12)');
$res1 =& $db->query('SELECT a FROM blah');
if (DB::isError($res1)) {
echo $res1->getMessage() . "\n";
}
$i = 1;
while ($res1->fetchInto($row, DB_FETCHMODE_ORDERED)) {
echo "fetch data $row[0]\n";
echo "insert number $i...\n";
$res2 =& $db->query("INSERT INTO blue (b) VALUES ($i)");
if (DB::isError($res2)) {
echo $res2->getMessage() . "\n";
}
$i++;
}
$res1->free();
$db->query('DROP TABLE blah');
$db->query('DROP TABLE blue');
$db->commit();
?>
integer autoExecute (
string $table
,
array $fields_values
,
integer $mode
= DB_AUTOQUERY_INSERT
,
string $where
= false
)
Automatically prepares and executes INSERT or UPDATE queries.
This method builds a SQL statement using autoPrepare() and then executes the statement using execute() with it.
$table
name of the table
$fields_values
assoc (key => value), keys are fields names, values are values of these fields
Values are automatically escaped and quoted according to the current DBMS's requirements.
$mode
type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)
$where
a string to be used in the WHERE clause.
This is only used when $mode
is
DB_AUTOQUERY_UPDATE.
The string is put directly into the query, so you must
escape and quote literals according to the DBMS's standards.
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_NEED_MORE_DATA | insufficient data supplied | Your associative array, which has to contain fields names and their values, is empty. | Check and correct your fields_values array. |
DB_ERROR_SYNTAX | syntax error | You use an unknown mode. | Available modes are only DB_AUTOQUERY_INSERT for INSERT queries or DB_AUTOQUERY_UPDATE for UPDATE queries. |
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. |
This function can not be called statically.
The values passed in
$data
must be literals. Do not submit SQL functions (for exampleCURDATE()
). SQL functions that should be performed at execution time need to be put in the prepared statement.
Using autoExecute() in insert mode
<?php
// Once you have a valid DB object named $db...
$table_name = 'user';
$fields_values = array(
'id' => 1,
'name' => 'Fabien',
'country' => 'France'
);
$res = $db->autoExecute($table_name, $fields_values,
DB_AUTOQUERY_INSERT);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
Using autoExecute() in update mode
<?php
// Once you have a valid DB object named $db...
$table_name = 'user';
$fields_values = array(
'country' => 'France',
);
$res = $db->autoExecute($table_name, $fields_values,
DB_AUTOQUERY_UPDATE, "country = 'Japan'");
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
resource autoPrepare (
string $table
,
array $table_fields
,
integer $mode
= DB_AUTOQUERY_INSERT
,
string $where
= false
)
Automatically builds an INSERT or UPDATE SQL statement so it can later be used by execute() or executeMultiple().
$table
name of the table
$table_fields
ordered array containing the fields names
Be aware that these fields are assigned ?
placeholders, therefore the data you pass to them in the
execute() will be automatically escaped
and quoted according to the current DBMS's requirements.
$mode
type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)
$where
a string to be used in the WHERE clause.
This is only used when $mode
is
DB_AUTOQUERY_UPDATE.
The string is put directly into the query, so you must
escape and quote literals according to the DBMS's standards.
resource - resource handle for the query or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_NEED_MORE_DATA | insufficient data supplied | The ordered array, which has to contain fields names, is empty. | Check and correct your fields names array. |
DB_ERROR_SYNTAX | syntax error | You use an unknown mode. | Available modes are only DB_AUTOQUERY_INSERT for INSERT queries or DB_AUTOQUERY_UPDATE for UPDATE queries. |
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. |
This function can not be called statically.
Using autoPrepare() in insert mode
<?php
// Once you have a valid DB object named $db...
$table_name = 'user';
$table_fields = array('name', 'country');
$table_values = array('Zoe', 'France');
$sth = $db->autoPrepare($table_name, $table_fields,
DB_AUTOQUERY_INSERT);
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
$res =& $db->execute($sth, $table_values);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
Using autoPrepare() in update mode
<?php
// Once you have a valid DB object named $db...
$table_name = 'user';
$table_fields = array('name', 'country');
$table_values = array('Bob', 'USA');
$sth = $db->autoPrepare($table_name, $table_fields,
DB_AUTOQUERY_UPDATE, "country = 'Japan'");
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
$res =& $db->execute($sth, $table_values);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
mixed commit (
)
Commits the current transaction.
Error code | Error message | Reason | Solution |
---|---|---|---|
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. |
This function can not be called statically.
When using MySQL as your DBMS, transactions can only be used when the tables in question use the InnoDB format.
Using commit()
<?php
// Once you have a valid DB object named $db...
$db->autoCommit(false);
$db->query('CREATE TABLE blah (a integer)');
$db->commit();
$db->query('INSERT INTO blah (a) VALUES (11)');
$res =& $db->query('SELECT a FROM blah');
if (DB::isError($res)) {
echo $res->getMessage() . "\n";
}
while ($res->fetchInto($row, DB_FETCHMODE_ORDERED)) {
echo $row[0] . "\n";
}
$res->free();
$db->query('DROP TABLE blah');
$db->commit();
?>
integer createSequence (
string $seq_name
)
$seq_name
name of the new sequence to create
To avoid problems with various database systems, sequence names should start with a letter and only contain letters, numbers and the underscore character.
Error code | Error message | Reason | Solution |
---|---|---|---|
every error code | Database specific error | Check the name of the sequence. If correct, probably a bug in the sequence implementation |
This function can not be called statically.
When using PEAR DB's sequence methods, we strongly advise using these methods for all procedures, including the creation of the sequences. Do not use PEAR DB's methods to access sequences that were created directly in the DBMS. See the warning on the "Intro - Sequences" page complete information.
Using createSequence()
<?php
// Once you have a valid DB object named $db...
$tmp = $db->createSequence('mySequence');
if (PEAR::isError($tmp)) {
die($tmp->getMessage());
}
?>
boolean disconnect (
)
Disconnects from a database
boolean - Returns TRUE on success, FALSE on failure.
This function can not be called statically.
Using disconnect()
<?php
require_once 'DB.php';
$db =& DB::connect('pgsql://someuser:apasswd@localhost/thedb');
if (PEAR::isError($db)) {
die($db->getMessage());
}
$db->disconnect();
?>
integer dropSequence (
string $seqName
)
$seqName
name of the sequence to delete
Error code | Error message | Reason | Solution |
---|---|---|---|
every error code | Database specific error | Check the name of the sequence. If correct, probably a bug in the sequence implementation. |
This function can not be called statically.
When using PEAR DB's sequence methods, we strongly advise using these methods for all procedures, including the creation of the sequences. Do not use PEAR DB's methods to access sequences that were created directly in the DBMS. See the warning on the "Intro - Sequences" page complete information.
Using dropSequence()
<?php
// Once you have a valid DB object named $db...
$tmp = $db->dropSequence('mySequence');
if (PEAR::isError($tmp)) {
die($tmp->getMessage());
}
?>
string escapeSimple (
string $str
)
Escape a string according to the current DBMS's standards.
$str
the input to be escaped
string - the escaped string
This function can not be called statically.
Function available since: Release 1.6.0
Using escapeSimple()
<?php
// Once you have a valid DB object named $db...
$name = "all's well";
$sql = "SELECT * FROM clients WHERE name = '"
. $db->escapeSimple($name) . "'";
$res =& $db->query($sql);
?>
mixed &execute (
resource $stmt
,
mixed $data = array()
)
Merges the SQL statement you submitted to
prepare()
with the information in $data
and then sends the query to the database.
$stmt
query handle from prepare()
$data
array, string or numeric data to be added to the prepared statement. Quantity of items passed must match quantity of placeholders in the prepared statement: meaning 1 placeholder for non-array parameters or 1 placeholder per array element.
mixed - a new DB_result object for queries that return results (such as SELECT queries), DB_OK for queries that manipulate data (such as INSERT queries) or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_INVALID | invalid | SQL statement handle is not valid. | Check correct processing of the SQL statement with prepare(). Note that execute() requires a handle to the statement returned by prepare(), not the statement itself. |
DB_ERROR_MISMATCH | mismatch | Quantity of parameters didn't match quantity of placeholders in the prepared statement. |
Check that the number of placeholders in the
prepare() statement passed to
$query equals the count of entries
passed to $params .
|
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statment for an Oracle database. |
This function can not be called statically.
The values passed in
$data
must be literals. Do not submit SQL functions (for exampleCURDATE()
). SQL functions that should be performed at execution time need to be put in the prepared statement.
Passing a scalar to execute()
<?php
// Once you have a valid DB object named $db...
$sth = $db->prepare('INSERT INTO numbers (number) VALUES (?)');
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
$res =& $db->execute($sth, 1);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
Passing an array to execute()
<?php
// Once you have a valid DB object named $db...
$sth = $db->prepare('INSERT INTO numbers VALUES (?, ?, ?)');
$data = array(1, 'one', 'en');
$db->execute($sth, $data);
?>
integer executeMultiple (
resource $stmt
,
array $data
)
Automatically passes the information in
$data
(a multi-dimensional array) to
execute(), which then runs
the SQL statement you submitted to
prepare().
$stmt
query handle from prepare()
$data
a numeric array containing the data to insert into the query
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_INVALID | invalid | SQL statement handle is not valid. | Check correct processing of the SQL statement with prepare(). Note that executeMultiple() requires a handle to the statement returned by prepare(), not the statement itself. |
DB_ERROR_MISMATCH | mismatch | Quantity of parameters didn't match quantity of placeholders in the prepared statement. |
Check that the number of placeholders in the
prepare() statement passed to
$query equals the count of entries
passed to $params .
|
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
If an error occurs during execution, the function will be stopped. Possible remaining data will be unprocessed.
The values passed in
$data
must be literals. Do not submit SQL functions (for exampleCURDATE()
). SQL functions that should be performed at execution time need to be put in the prepared statement.
Using executeMultiple()
<?php
// Once you have a valid DB object named $db...
$sth = $db->prepare('INSERT INTO numbers VALUES (?, ?, ?)');
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
$alldata = array(array(1, 'one', 'en'),
array(2, 'two', 'to'),
array(3, 'three', 'tre'),
array(4, 'four', 'fire'));
$res =& $db->executeMultiple($sth, $alldata);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
boolean freePrepared (
resource $stmt
,
boolean $free_resource = true
)
Removes the memory occupied by the internal notations that keep track of prepared SQL statements. Does not delete the DB_result object itself.
$stmt
statement resource identifier returned from prepare()
$free_resource
should the PHP resource be freed too? Use false if you need to get data from the result set later.
Parameter available since Release 1.7.0.
boolean - Returns TRUE on success, FALSE on failure.
This function can not be called statically.
Using freePrepared()
<?php
// Once you have a valid DB object named $db...
$sth = $db->prepare('INSERT INTO numbers (number) VALUES (?)');
$db->execute($sth, 1);
$db->freePrepared($sth);
?>
array &getAll (
string $query
,
array $params = array()
,
integer $fetchmode
= DB_FETCHMODE_DEFAULT
)
Runs the query provided and puts the entire result set into a nested array then frees the result set.
$query
the SQL query or the statement to prepare
$params
array to be used in execution of the statement. Quantity of array elements must match quantity of placeholders in query.
If supplied, prepare()/ execute() is used.
This method does not allow scalars to be used for this argument.
$fetchmode
the fetch mode to use. The default is DB_FETCHMODE_DEFAULT, which tells this method to use DB's current fetch mode. The current fetch mode can be changed using setFetchMode(). Potential values include:
DB_FETCHMODE_ORDERED
DB_FETCHMODE_ASSOC
DB_FETCHMODE_OBJECT
DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED
DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED
array - a nested array or a DB_Error on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_INVALID | invalid | SQL statement for preparing is not valid. | See the prepare() documentation, if you want to use a SQL statemt using placeholders. |
DB_ERROR_MISMATCH | mismatch | Quantity of parameters didn't match quantity of placeholders in the prepared statement. |
Check that the number of placeholders in the
prepare() statement passed to
$query equals the count of entries
passed to $params .
|
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
Using getAll() to return an associative array by setting the default fetch mode first
<?php
// Once you have a valid DB object named $db...
$db->setFetchMode(DB_FETCHMODE_ASSOC);
$data =& $db->getAll('SELECT cf, nf, df FROM foo');
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => Array
(
[cf] => Juan
[nf] => 5
[df] => 1991-01-11 21:31:41
)
[1] => Array
(
[cf] => Kyu
[nf] => 10
[df] => 1992-02-12 22:32:42
)
)
Using getAll() to return an ordered array
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAll('SELECT cf, nf, df FROM foo',
array(), DB_FETCHMODE_ORDERED);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => Array
(
[0] => Juan
[1] => 5
[2] => 1991-01-11 21:31:41
)
[1] => Array
(
[0] => Kyu
[1] => 10
[2] => 1992-02-12 22:32:42
)
)
Using getAll() to return a flipped ordered array
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAll('SELECT cf, nf, df FROM foo',
array(), DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => Array
(
[0] => Juan
[1] => Kyu
)
[1] => Array
(
[0] => 5
[1] => 10
)
[2] => Array
(
[0] => 1991-01-11 21:31:41
[1] => 1992-02-12 22:32:42
)
)
Using getAll() to return an associative array
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAll('SELECT cf, nf, df FROM foo',
array(), DB_FETCHMODE_ASSOC);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => Array
(
[cf] => Juan
[nf] => 5
[df] => 1991-01-11 21:31:41
)
[1] => Array
(
[cf] => Kyu
[nf] => 10
[df] => 1992-02-12 22:32:42
)
)
Using getAll() to return a flipped associative array
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAll('SELECT cf, nf, df FROM foo',
array(), DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[cf] => Array
(
[0] => Juan
[1] => Kyu
)
[nf] => Array
(
[0] => 5
[1] => 10
)
[df] => Array
(
[0] => 1991-01-11 21:31:41
[1] => 1992-02-12 22:32:42
)
)
Using getAll() to return an array of objects
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAll('SELECT cf, nf, df FROM foo',
array(), DB_FETCHMODE_OBJECT);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => stdClass Object
(
[cf] => Juan
[nf] => 5
[df] => 1991-01-11 21:31:41
)
[1] => stdClass Object
(
[cf] => Kyu
[nf] => 10
[df] => 1992-02-12 22:32:42
)
)
Using getAll() in prepare/execute mode to return an associative array
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAll('SELECT cf, nf, df FROM foo WHERE nf = ?',
array(5), DB_FETCHMODE_ASSOC);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => Array
(
[cf] => Juan
[nf] => 5
[df] => 1991-01-11 21:31:41
)
)
array &getAssoc (
string $query
,
boolean $force_array
= false
,
mixed $params = array()
,
integer $fetchmode
= DB_FETCHMODE_DEFAULT
,
boolean $group
= false
)
Runs the query provided and puts the entire result set into an associative array then frees the result set.
If the result set contains more than two columns, the value
will be an array of the values from column 2 to n. If the result
set contains only two columns, the returned value will be a
scalar with the value of the second column (unless forced to an
array with the $force_array
parameter).
$query
the SQL query or the statement to prepare
$force_array
used only if the query returns exactly two columns. If TRUE, the values of the returned array will be one-element arrays instead of scalars.
$params
array, string or numeric data to be added to the prepared statement. Quantity of items passed must match quantity of placeholders in the prepared statement: meaning 1 placeholder for non-array parameters or 1 placeholder per array element.
If supplied, prepare()/ execute() is used.
$fetchmode
the fetch mode to use. The default is DB_FETCHMODE_DEFAULT, which tells this method to use DB's current fetch mode. DB's current default fetch mode can be changed using setFetchMode(). Potential values include:
DB_FETCHMODE_ORDERED
DB_FETCHMODE_ASSOC
DB_FETCHMODE_OBJECT
$group
if TRUE, the values of the returned array is wrapped in another array. If the same key value (in the first column) repeats itself, the values will be appended to this array instead of overwriting the existing values.
array - associative array with the query results or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_INVALID | invalid | SQL statement for preparing is not valid. | See the prepare() documentation, if you want to use a SQL statemt using placeholders. |
DB_ERROR_MISMATCH | mismatch | Quantity of parameters didn't match quantity of placeholders in the prepared statement. |
Check that the number of placeholders in the
prepare() statement passed to
$query equals the count of entries
passed to $params .
|
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
DB_ERROR_TRUNCATED | truncated | The result set contains fewer then two columns | Check the SQL query or choose another get*() function |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
All of the examples use the following data set:
INSERT INTO foo VALUES ('Juan', 5, '1991-01-11 21:31:41'); INSERT INTO foo VALUES ('Kyu', 10, '1992-02-12 22:32:42'); INSERT INTO foo VALUES ('Kyu', 15, '1993-03-13 23:33:43');
When using getAssoc() for results
which have two columns and
$force_array
= FALSE (the default)
changing $fetchmode
has no
impact on the format of the resulting array.
Using getAssoc() in default mode
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo');
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => 1991-01-11 21:31:41
[Kyu] => 1993-03-13 23:33:43
)
Using getAssoc() with
$group
= TRUE
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo',
false, array(), DB_FETCHMODE_ORDERED, true);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[0] => 1991-01-11 21:31:41
)
[Kyu] => Array
(
[0] => 1992-02-12 22:32:42
[1] => 1993-03-13 23:33:43
)
)
Using getAssoc() with
$force_array
= TRUE
and $fetchmode
=
DB_FETCHMODE_ORDERED
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo',
true, array(), DB_FETCHMODE_ORDERED);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[0] => 1991-01-11 21:31:41
)
[Kyu] => Array
(
[0] => 1993-03-13 23:33:43
)
)
Using getAssoc() with
$force_array
= TRUE
and $fetchmode
=
DB_FETCHMODE_ASSOC
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo',
true, array(), DB_FETCHMODE_ASSOC);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[df] => 1991-01-11 21:31:41
)
[Kyu] => Array
(
[df] => 1993-03-13 23:33:43
)
)
Using getAssoc() with
$force_array
= TRUE
and $fetchmode
=
DB_FETCHMODE_OBJECT
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo',
true, array(), DB_FETCHMODE_OBJECT);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => stdClass Object
(
[cf] => Juan
[df] => 1991-01-11 21:31:41
)
[Kyu] => stdClass Object
(
[cf] => Kyu
[df] => 1993-03-13 23:33:43
)
)
Using getAssoc() with
$force_array
= TRUE,
$fetchmode
=
DB_FETCHMODE_ORDERED
and $group
= TRUE
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo',
true, array(), DB_FETCHMODE_ORDERED, true);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[0] => Array
(
[0] => 1991-01-11 21:31:41
)
)
[Kyu] => Array
(
[0] => Array
(
[0] => 1992-02-12 22:32:42
)
[1] => Array
(
[0] => 1993-03-13 23:33:43
)
)
)
Using getAssoc() with
$force_array
= TRUE,
$fetchmode
=
DB_FETCHMODE_ASSOC
and $group
= TRUE
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo',
true, array(), DB_FETCHMODE_ASSOC, true);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[0] => Array
(
[df] => 1991-01-11 21:31:41
)
)
[Kyu] => Array
(
[0] => Array
(
[df] => 1992-02-12 22:32:42
)
[1] => Array
(
[df] => 1993-03-13 23:33:43
)
)
)
Using getAssoc() with
$force_array
= TRUE,
$fetchmode
=
DB_FETCHMODE_OBJECT
and $group
= TRUE
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo',
true, array(), DB_FETCHMODE_OBJECT, true);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[0] => stdClass Object
(
[cf] => Juan
[df] => 1991-01-11 21:31:41
)
)
[Kyu] => Array
(
[0] => stdClass Object
(
[cf] => Kyu
[df] => 1992-02-12 22:32:42
)
[1] => stdClass Object
(
[cf] => Kyu
[df] => 1993-03-13 23:33:43
)
)
)
Using getAssoc() with
$fetchmode
=
DB_FETCHMODE_ORDERED
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, nf, df FROM foo',
false, array(), DB_FETCHMODE_ORDERED);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[0] => 5
[1] => 1991-01-11 21:31:41
)
[Kyu] => Array
(
[0] => 15
[1] => 1993-03-13 23:33:43
)
)
Using getAssoc() with
$fetchmode
=
DB_FETCHMODE_ASSOC
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, nf, df FROM foo',
false, array(), DB_FETCHMODE_ASSOC);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[nf] => 5
[df] => 1991-01-11 21:31:41
)
[Kyu] => Array
(
[nf] => 15
[df] => 1993-03-13 23:33:43
)
)
Using getAssoc() with
$fetchmode
=
DB_FETCHMODE_OBJECT
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, nf, df FROM foo',
false, array(), DB_FETCHMODE_OBJECT);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => stdClass Object
(
[cf] => Juan
[nf] => 5
[df] => 1991-01-11 21:31:41
)
[Kyu] => stdClass Object
(
[cf] => Kyu
[nf] => 15
[df] => 1993-03-13 23:33:43
)
)
Using getAssoc() with
$fetchmode
=
DB_FETCHMODE_ORDERED
and $group
= TRUE
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, nf, df FROM foo',
false, array(), DB_FETCHMODE_ORDERED, true);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[0] => Array
(
[0] => 5
[1] => 1991-01-11 21:31:41
)
)
[Kyu] => Array
(
[0] => Array
(
[0] => 10
[1] => 1992-02-12 22:32:42
)
[1] => Array
(
[0] => 15
[1] => 1993-03-13 23:33:43
)
)
)
Using getAssoc() with
$fetchmode
=
DB_FETCHMODE_ASSOC
and $group
= TRUE
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, nf, df FROM foo',
false, array(), DB_FETCHMODE_ASSOC, true);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[0] => Array
(
[nf] => 5
[df] => 1991-01-11 21:31:41
)
)
[Kyu] => Array
(
[0] => Array
(
[nf] => 10
[df] => 1992-02-12 22:32:42
)
[1] => Array
(
[nf] => 15
[df] => 1993-03-13 23:33:43
)
)
)
Using getAssoc() with
$fetchmode
=
DB_FETCHMODE_OBJECT
and $group
= TRUE
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, nf, df FROM foo',
false, array(), DB_FETCHMODE_OBJECT, true);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[Juan] => Array
(
[0] => stdClass Object
(
[cf] => Juan
[nf] => 5
[df] => 1991-01-11 21:31:41
)
)
[Kyu] => Array
(
[0] => stdClass Object
(
[cf] => Kyu
[nf] => 10
[df] => 1992-02-12 22:32:42
)
[1] => stdClass Object
(
[cf] => Kyu
[nf] => 15
[df] => 1993-03-13 23:33:43
)
)
)
Using getAssoc() with one placeholder
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo WHERE nf = ?',
false, 5);
if (PEAR::isError($data)) {
die($data->getMessage());
}
?>
Using getAssoc() with two placeholders
<?php
// Once you have a valid DB object named $db...
$data =& $db->getAssoc('SELECT cf, df FROM foo WHERE nf IN (?, ?)',
false, array(5, 10));
if (PEAR::isError($data)) {
die($data->getMessage());
}
?>
array &getCol (
string $query
,
mixed $col = 0
,
mixed $params = array()
)
Runs the query provided and puts the first column of data into an array then frees the result set.
$query
the SQL query or the statement to prepare
$col
which column to return (integer [column number, starting at 0] or string [column name])
$params
array, string or numeric data to be added to the prepared statement. Quantity of items passed must match quantity of placeholders in the prepared statement: meaning 1 placeholder for non-array parameters or 1 placeholder per array element.
If supplied, prepare()/ execute() is used.
array - an ordered array containing data from a result set column or a DB_Error object on failure. The array's index starts at 0.
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_INVALID | invalid | SQL statement for preparing is not valid. | See the prepare() documentation, if you want to use a SQL statemt using placeholders. |
DB_ERROR_MISMATCH | mismatch | Quantity of parameters didn't match quantity of placeholders in the prepared statement. |
Check that the number of placeholders in the
prepare() statement passed to
$query equals the count of entries
passed to $params .
|
DB_ERROR_NOSUCHFIELD | no such field |
Invalid column number/name passed to $col
|
Use a column number or name that exists in the query result. |
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of PHP Manual to detect the reason for this error. In most cases a malformed SQL statement is the cause of the error. (Ie. using LIMIT in a SQL-Statement for an Oracle database.) |
This function can not be called statically.
Using getCol()
<?php
// Once you have a valid DB object named $db...
$data =& $db->getCol('SELECT cf, df FROM foo');
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => Juan
[1] => Kyu
)
Using getCol() to retrieve a numerically specified column
<?php
// Once you have a valid DB object named $db...
$data =& $db->getCol('SELECT cf, df FROM foo', 1);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => 1991-01-11 21:31:41
[1] => 1992-02-12 22:32:42
)
Using getCol() to retrieve a named column
<?php
// Once you have a valid DB object named $db...
$data =& $db->getCol('SELECT cf, df FROM foo', 'df');
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => 1991-01-11 21:31:41
[1] => 1992-02-12 22:32:42
)
Using getCol() in prepare/execute mode with one placeholder
<?php
// Once you have a valid DB object named $db...
$data =& $db->getCol('SELECT cf, df FROM foo WHERE nf = ?',
'df', 5);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => 1991-01-11 21:31:41
)
Using getCol() in prepare/execute mode with two placeholders
<?php
// Once you have a valid DB object named $db...
$data =& $db->getCol('SELECT cf, df FROM foo WHERE nf IN (?, ?)',
'df', array(5, 10));
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => 1991-01-11 21:31:41
[1] => 1992-02-12 22:32:42
)
array getListOf (
string $type
)
Retrieves information about database users, avaible databases, views and functions.
$type
type of requested info,
valid values for $type
are database dependent, ie:
tables
,
databases
, users
,
view
, functions
array - an ordered array containing the requested data or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_UNSUPPORTED | not supported | The requested information is not avaible. | Check your user permissions and the database system for support quering the requested information. |
This function can not be called statically.
Using getListOf()
<?php
// Once you have a valid DB object named $db...
$data = $db->getListOf('tables');
if (PEAR::isError($data)) {
die($data->getMessage());
}
echo implode("\n", $data);
?>
mixed &getOne (
string $query
,
mixed $params = array()
)
Runs the query provided and returns the data from the first column of the first row then frees the result set.
$query
the SQL query or the statement to prepare
$params
array, string or numeric data to be added to the prepared statement. Quantity of items passed must match quantity of placeholders in the prepared statement: meaning 1 placeholder for non-array parameters or 1 placeholder per array element.
If supplied, prepare()/ execute() is used.
mixed - the first column's data, NULL if there is no data, or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_INVALID | invalid | SQL statement for preparing is not valid. | See the prepare() documentation, if you want to use a SQL statemt using placeholders. |
DB_ERROR_MISMATCH | mismatch | Quantity of parameters didn't match quantity of placeholders in the prepared statement. |
Check that the number of placeholders in the
prepare() statement passed to
$query equals the count of entries
passed to $params .
|
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
Using getOne()
<?php
// Once you have a valid DB object named $db...
$data =& $db->getOne('SELECT cf FROM foo');
if (PEAR::isError($data)) {
die($data->getMessage());
}
echo "$data\n";
?>
Using getOne() with one placeholder
<?php
// Once you have a valid DB object named $db...
$data =& $db->getOne('SELECT cf FROM foo WHERE nf = ?',
5);
if (PEAR::isError($data)) {
die($data->getMessage());
}
echo "$data\n";
?>
Using getOne() with two placeholders
<?php
// Once you have a valid DB object named $db...
$data =& $db->getOne('SELECT cf FROM foo WHERE nf IN (?, ?)',
array(5, 10));
if (PEAR::isError($data)) {
die($data->getMessage());
}
echo "$data\n";
?>
mixed getOption (
string $option
)
Determines current state of a PEAR DB configuration option
$option
name of the option to examine
mixed the option's value
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | unknown option | The given option does not exist |
Check $option for typographical errors
|
This function can not be called statically.
Simple getOption() example
<?php
// Once you have a valid DB object named $db...
if ($db->getOption('autofree')) {
// do something...
}
?>
array &getRow (
string $query
,
array $params = array()
,
integer $fetchmode
= DB_FETCHMODE_DEFAULT
)
Runs the query provided and puts the first row of data into an array then frees the result set.
$query
the SQL query or the statement to prepare
$params
array to be used in execution of the statement. Quantity of array elements must match quantity of placeholders in query.
If supplied, prepare()/ execute() is used.
This method does not allow scalars to be used for this argument.
$fetchmode
the fetch mode to use. The default is DB_FETCHMODE_DEFAULT, which tells this method to use DB's current fetch mode. DB's current default fetch mode can be changed using setFetchMode(). Potential values include:
DB_FETCHMODE_ORDERED
DB_FETCHMODE_ASSOC
DB_FETCHMODE_OBJECT
array - the first row's data in an array
or a DB_Error object on failure.
The array may be ordered or associative depending on
$fetchmode
.
The column index starts at 0 for ordered arrays.
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_INVALID | invalid | SQL statement for preparing is not valid. | See the prepare() documentation, if you want to use a SQL statemt using placeholders. |
DB_ERROR_MISMATCH | mismatch | Quantity of parameters didn't match quantity of placeholders in the prepared statement. |
Check that the number of placeholders in the
prepare() statement passed to
$query equals the count of entries
passed to $params .
|
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
Using getRow() with
$fetchmode
=
DB_FETCHMODE_ORDERED
<?php
// Once you have a valid DB object named $db...
$data =& $db->getRow('SELECT cf, df FROM foo',
array(), DB_FETCHMODE_ORDERED);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[0] => Juan
[1] => 1991-01-11 21:31:41
)
Using getRow() with
$fetchmode
=
DB_FETCHMODE_ASSOC
<?php
// Once you have a valid DB object named $db...
$data =& $db->getRow('SELECT cf, df FROM foo',
array(), DB_FETCHMODE_ASSOC);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
Array
(
[cf] => Juan
[df] => 1991-01-11 21:31:41
)
Using getRow() with
$fetchmode
=
DB_FETCHMODE_OBJECT
<?php
// Once you have a valid DB object named $db...
$data =& $db->getRow('SELECT cf, df FROM foo',
array(), DB_FETCHMODE_OBJECT);
if (PEAR::isError($data)) {
die($data->getMessage());
}
print_r($data);
?>
Output:
stdClass Object
(
[cf] => Juan
[df] => 1991-01-11 21:31:41
)
Using getRow() with one placeholder
<?php
// Once you have a valid DB object named $db...
$data =& $db->getRow('SELECT cf, df FROM foo WHERE nf = ?',
array(5));
if (PEAR::isError($data)) {
die($data->getMessage());
}
?>
Using getRow() with two placeholders
<?php
// Once you have a valid DB object named $db...
$data =& $db->getRow('SELECT cf, df FROM foo WHERE nf IN (?, ?)',
array(5, 10));
if (PEAR::isError($data)) {
die($data->getMessage());
}
?>
mixed &limitQuery (
string $query
,
integer $from
,
integer $count
,
mixed $params = array()
)
Executes a SQL query, but fetches only the specificed count of rows. It is an emulation of the MySQL LIMIT option.
$query
the SQL query
$from
the row to start to fetch. Note that 0 returns the first row, 1 returns the second row, etc.
$count
the numbers of rows to fetch
$params
array, string or numeric data to be added to the prepared statement. Quantity of items passed must match quantity of placeholders in the prepared statement: meaning 1 placeholder for non-array parameters or 1 placeholder per array element.
mixed - a new DB_result object for queries that return results (such as SELECT queries), DB_OK for queries that manipulate data (such as INSERT queries) or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
Depending on the database you will not really get more speed compared to query(). The advantage of limitQuery() is the deleting of unneeded rows in the resultset, as early as possible. So this can decrease memory usage.
Also note that $from
and $count
are
not being escaped. You should take care of sanitizing input yourself,
or you are open to an SQL injection attack.
Using limitQuery()
<?php
// Once you have a valid DB object named $db...
$res =& $db->limitQuery('SELECT * FROM foo', 49, 10);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
integer nextId (
string $seq_name
,
boolean $onDemand = true
)
Returns the next available number from a sequence. The sequence is automatically incremented each time this method is called.
See "Intro - Sequences" for a more complete discussion.
$seq_name
name of the sequence
To avoid problems with various database systems, sequence names should start with a letter and only contain letters, numbers and the underscore character.
$onDemand
When TRUE, the sequence is automatically created if it does not exist yet.
The on demand creation process necessitates the database user specified in the script having the database permissions needed to create a table or sequence. The exact privileges required depends on the DBMS being used.
integer - a free id number or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_NOT_CAPABLE | DB backend not capable | Function is not supported by the database backend | Switch to another database system, if you really need this feature. |
DB_ERROR_NOT_LOCKED | not locked | Locking of sequence table fails | Database specific, check documentation of your database, |
DB_ERROR_NOSUCHTABLE | no such table | Sequence table was not found | Try to create a new sequence or if you are sure, a sequence was already create, check database integrity |
This function can not be called statically.
When using PEAR DB's sequence methods, we strongly advise using these methods for all procedures, including the creation of the sequences. Do not use PEAR DB's methods to access sequences that were created directly in the DBMS. See the warning on the "Intro - Sequences" page complete information.
Using nextId()
<?php
// Once you have a valid DB object named $db...
$id = $db->nextId('mySequence');
if (PEAR::isError($id)) {
die($id->getMessage());
}
// Use the ID in your INSERT query
$res =& $db->query("INSERT INTO myTable (id, text) VALUES ($id, 'foo')");
?>
void nextQueryIsManip (
boolean $manip
)
Informs the DB driver that the next query that is called will manipulate data within the database and will not return a result set, irrespective of the usual detection performed within DB.
$manip
When TRUE, the next query will always be treated as not returning a result set. When FALSE, the normal behaviour will be used, which will result in DB attempting to automatically detect whether the query will return a result set or not.
void - nothing is returned
This function can not be called statically.
resource prepare (
string $query
)
Gets a SQL statement ready so it can be run by execute().
$query
the query to prepare
resource - the query handle or a DB_Error object on failure
This function can not be called statically.
Using prepare()
<?php
// Once you have a valid DB object named $db...
$sth = $db->prepare('INSERT INTO numbers (number) VALUES (?)');
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
$res =& $db->execute($sth, 1);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
boolean provides (
string $feature
)
Checks if a feature is available for the chosen database type.
$feature
the feature to check
$feature value |
Meaning |
---|---|
prepare |
The database does a pre-check of the SQL statement |
pconnect |
The database supports persistent connections |
transactions |
The database supports transactions |
limit |
The database supports LIMITed SELECT statements |
boolean - TRUE if the feature is supported
This function can not be called statically.
The provided information are only hints. Check the documentation of your database system for the real supported features. I.e. MySQL supports transactions, but not for every table type.
Using provides()
<?php
// Once you have a valid DB object named $db...
if ($db->provides('pconnect')) {
echo "Persistent connections are allowed.\n";
}
?>
mixed &query (
string $query
,
mixed $params = array()
)
Runs a query
Can be used instead of
prepare() and
execute(), if you
set the $params
parameter
and your query uses placeholders.
See
"Intro - Prepare & Execute"
for more information on this mode.
$query
the SQL query or the statement to prepare
$params
array, string or numeric data to be added to the prepared statement. Quantity of items passed must match quantity of placeholders in the prepared statement: meaning 1 placeholder for non-array parameters or 1 placeholder per array element.
mixed - a new DB_result object for queries that return results (such as SELECT queries), DB_OK for queries that manipulate data (such as INSERT queries) or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_INVALID | invalid | SQL statement for preparing is not valid. | See the prepare() documentation, if you want to use a SQL statement using placeholders. |
DB_ERROR_MISMATCH | mismatch | Quantity of parameters didn't match quantity of placeholders in the prepared statement. |
Check that the number of placeholders in the
prepare() statement passed to
$query equals the count of entries
passed to $params .
|
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
every other error code | Database specific error | Check the database related section of the PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. I.e. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
string quote (
string $string
)
Quotes a string database-dependent, so it can be safely used in a query
This method has been deprecated. Use quoteSmart() or escapeSimple() instead.
$string
the input string to quote
string - the quoted string.
This function can not be called statically.
This function is deprecated. That means that future versions of this package may not support it anymore.
Deprecated in release 1.6.0.
string quoteIdentifier (
string $str
)
Format input so it can be safely used as a delimited identifier in a query. Identifiers are objects such as table or column names.
The format returned depends on the database type being used.
Delimited identifiers are known to generally work correctly under the following drivers:
InterBase doesn't seem to be able to use delimited identifiers via PHP 4. They work fine under PHP 5.
$str
the input to be quoted
string - the formatted string
This function can not be called statically.
Function available since: Release 1.6.0
Just because you CAN use delimited identifiers doesn't mean you SHOULD use them. In general, they end up causing way more problems than they solve.
Portability is broken by using the following characters inside delimited identifiers:
- backtick (
`
) -- due to MySQL- double quote (
"
) -- due to Oracle- brackets (
[
or]
) -- due to Access
Using quoteIdentifier()
<?php
// Once you have a valid DB object named $db...
$sql = 'SELECT ' . $db->quoteIdentifier('company name')
. ', address FROM clients';
$res =& $db->query($sql);
?>
mixed quoteSmart (
mixed $in
)
Format input so it can be safely used as a literal in a query. Literals are values such as strings or numbers which get utilized in places like WHERE, SET and VALUES clauses of SQL statements.
The format returned depends on the PHP data type of input and the database type being used.
$in
the input to be quoted
mixed - the formatted data
The format of the results depends on the input's PHP type:
input
-> returns
NULL -> the string NULL
integer or float -> the unquoted number
boolean -> output depends on the driver in use
Most drivers return integers: 1
if
true
or 0
if
false
.
Some return strings: TRUE
if
true
or FALSE
if
false
.
Finally one returns strings: T
if
true
or F
if
false
. Here is a list of each DBMS,
the values returned and the suggested column type:
dbase
-> T/F
(Logical
)
fbase
-> TRUE/FALSE
(BOOLEAN
)
ibase
-> 1/0
(SMALLINT
) [1]
ifx
-> 1/0
(SMALLINT
) [1]
msql
-> 1/0
(INTEGER
)
mssql
-> 1/0
(TINYINT
)
mysql
-> 1/0
(TINYINT(1)
)
mysqli
-> 1/0
(TINYINT(1)
)
oci8
-> 1/0
(NUMBER(1)
)
odbc
-> 1/0
(SMALLINT
) [1]
pgsql
-> TRUE/FALSE
(BOOLEAN
)
sqlite
-> 1/0
(INTEGER
)
sybase
-> 1/0
(TINYINT
)
[1] Accommodate the lowest common denominator because not all
versions of have BOOLEAN
.
other (including strings and numeric strings) -> a string which has been escaped in a DBMS specific way (using escapeSimple()) and then surrounded by single quotes
This function can not be called statically.
Function available since: Release 1.6.0
Using quoteSmart()
<?php
// Once you have a valid DB object named $db...
$name = "all's well";
$active = true;
$sql = 'SELECT * FROM clients WHERE name = '
. $db->quoteSmart($name)
. ' AND active = '
. $db->quoteSmart($active);
$res =& $db->query($sql);
?>
mixed rollback (
)
Rolls back the current transaction.
Error code | Error message | Reason | Solution |
---|---|---|---|
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. |
This function can not be called statically.
When using MySQL as your DBMS, transactions can only be used when the tables in question use the InnoDB format.
Using rollback()
<?php
// Once you have a valid DB object named $db...
$db->autoCommit(false);
$db->query('INSERT INTO blah (a) VALUES (11)');
$res =& $db->query('SELECT b FROM blue');
if (DB::isError($res)) {
echo $res->getMessage() . "\n";
}
while ($res->fetchInto($row, DB_FETCHMODE_ORDERED)) {
if ($row[0] == 'problem') {
$db->rollback();
}
}
$res->free();
$db->query('DROP TABLE blah');
$db->commit();
?>
void setFetchMode (
integer $fetchmode
,
string $object_class = stdClass
)
Sets the default fetch mode used by fetch*() and get*() methods.
$fetchmode
DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC or DB_FETCHMODE_OBJECT.
See the Examples section, below, for more information.
$object_class
This parameter is for use when
$fetchmode
is set to
DB_FETCHMODE_OBJECT.
You can set this parameter to DB_row
,
which then causes the resulting data to populate a
new instance of a DB_row object.
void - nothing is returned on success or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | invalid fetchmode mode | The given fetch mode does not exists or is not implement in your DB version. | Check writing of the argument and your used version of DB. |
This function can not be called statically.
DB_FETCHMODE_ORDERED (default)
Causes ordered arrays to be returned. The order is taken from the select statement.
<?php
// Once you have a valid DB object named $db...
$db->setFetchMode(DB_FETCHMODE_ORDERED);
$res =& $db->query('SELECT a, b FROM phptest WHERE a = 28');
$row =& $res->fetchRow();
print_r($row);
echo 'Column a is ' . $row[0];
?>
Output:
Array
(
[0] => 28
[1] => hi
)
Column a is 28
DB_FETCHMODE_ASSOC
Makes associative arrays, with the column names as the array keys.
<?php
// Once you have a valid DB object named $db...
$db->setFetchMode(DB_FETCHMODE_ASSOC);
$res =& $db->query('SELECT a, b FROM phptest WHERE a = 28');
$row =& $res->fetchRow();
print_r($row);
echo 'Column a is ' . $row['a'];
?>
Output:
Array
(
[a] => 28
[b] => hi
)
Column a is 28
DB_FETCHMODE_OBJECT
Returns objects with column names as properties.
<?php
// Once you have a valid DB object named $db...
$db->setFetchMode(DB_FETCHMODE_OBJECT);
$res =& $db->query('SELECT a, b FROM phptest WHERE a = 28');
$row =& $res->fetchRow();
print_r($row);
echo 'Column a is ' . $row->a;
?>
Output:
stdClass Object
(
[a] => 28
[b] => hi
)
Column a is 28
DB_FETCHMODE_OBJECT and DB_row
If setFetchMode()'s optional
$object_class
parameter
is set to DB_row
, DB_row
objects are returned.
<?php
// Once you have a valid DB object named $db...
$db->setFetchMode(DB_FETCHMODE_OBJECT, 'DB_row');
$res =& $db->query('SELECT a, b FROM phptest WHERE a = 28');
$row =& $res->fetchRow();
print_r($row);
echo 'Column a is ' . $row->a;
?>
Output:
db_row Object
(
[a] => 28
[b] => hi
)
Column a is 28
DB_FETCHMODE_OBJECT with your own object in PHP 4
<?php
// Once you have a valid DB object named $db...
class SomeResult {
function SomeResult($data) {
foreach ($data as $key => $value) {
$this->$key = $data[$key];
}
}
}
$db->setFetchMode(DB_FETCHMODE_OBJECT, 'SomeResult');
$res =& $db->query('SELECT a, b FROM phptest WHERE a = 28');
$row =& $res->fetchRow();
print_r($row);
echo 'Column a is ' . $row->a;
?>
Output:
SomeResult Object
(
[a] => 28
[b] => hi
)
Column a is 28
DB_FETCHMODE_OBJECT with your own object in PHP 5
<?php
// Once you have a valid DB object named $db...
class SomeResult {
public $row_data;
function __construct($data) {
$this->row_data = $data;
}
function __get($variable) {
return $this->row_data[$variable];
}
}
$db->setFetchMode(DB_FETCHMODE_OBJECT, 'SomeResult');
$res =& $db->query('SELECT a, b FROM phptest WHERE a = 28');
$row =& $res->fetchRow();
print_r($row);
echo 'Column a is ' . $row->a;
?>
Output:
SomeResult Object
(
[a] => 28
[b] => hi
)
Column a is 28
integer setOption (
string $option
,
mixed $value
)
Sets run-time configuration options for PEAR DB.
$option
name of the option to set
$value
value to set the option to
Option | Data Type | Default Value | Description | |
---|---|---|---|---|
autofree | boolean | FALSE | should results be freed automatically when there are no more rows? | |
debug | integer | 0 | debug level | |
persistent | boolean | FALSE | should the connection be persistent? | |
portability | integer | DB_PORTABILITY_NONE |
portability mode constant. These constants are bitwised, so
they can be combined using | and removed using
^ . See the examples below and the
"Intro - Portability" for more information.
|
|
seqname_format | string |
%s_seq
|
the sprintf() format string used on sequence names. This format is applied to sequence names passed to createSequence(), nextID() and dropSequence(). | |
ssl | boolean | FALSE | use ssl to connect? |
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | unknown option | The given option does not exist |
Check $option for typographical errors
|
This function can not be called statically.
Simple setOption() example
<?php
// Once you have a valid DB object named $db...
$db->setOption('autofree', true);
?>
Portability for lowercasing and trimming
<?php
// Once you have a valid DB object named $db...
$db->setOption('portability',
DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);
?>
All portability options except trimming
<?php
// Once you have a valid DB object named $db...
$db->setOption('portability',
DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);
?>
array tableInfo (
mixed $result
,
integer $mode
= null
)
Get information about columns in a table or a query result.
$result
DB_result object from a query or a string containing the name of a table
If the name of the table needs to be delimited (ie: the name is a reserved word or has spaces in it), use the quoteIdentifier() method on the table name when passing it.
This can also be a query result resource identifier, but doing so is deprecated.
$mode
one of the tableInfo mode constants
array - an associative array of the table's information or a DB_Error object on failure
The array's contents depends on the $mode
parameter.
The names of tables and columns will be lowercased if the DB_PORTABILITY_LOWERCASE portability mode is enabled.
The flags
element contains a space
separated list of extra information about the column.
If the DBMS is able to report a column's default value,
the value is passed through rawurlencode()
to avoid problems caused by potential spaces in the value.
Most DBMS's only provide the table
and
flags
elements if $result
is a table name. Only fbsql and mysql provide full
information from queries.
The type
element contains the type returned
by the DBMS. It varies from DBMS to DBMS.
This section describes the format of the returned array and how
it varies depending on which $mode
was used
when the function was called.
The sample output below is based on this query:
SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId FROM tblFoo JOIN tblBar ON tblFoo.fldId = tblBar.fldId;
0
In addition to the information found in the default output,
a notation of the number of columns is provided by the
num_fields
element while the
order
element provides an array with the column names as the keys and
their location index number (corresponding to the keys in the
default output) as the values.
If a result set has identical field names, the last one is used.
Similar to DB_TABLEINFO_ORDER but adds more dimensions to the array in which the table names are keys and the field names are sub-keys. This is helpful for queries that join tables which have identical field names.
Contains the information from both DB_TABLEINFO_ORDER and DB_TABLEINFO_ORDERTABLE
The tableInfo mode constants are bitwised, so they can be
combined using |
.
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_NOT_CAPABLE | DB backend not capable | Driver doesn't support this feature. | Switch to another database system, if you really need this feature. |
DB_ERROR_NEED_MORE_DATA | insufficient data supplied |
The data passed in the $result parameter
was not a valid table name or result identifier.
|
Check the table name for typographical errors or that the query ran correctly. |
DB_ERROR_NODBSELECTED | no database selected | No database was chosen. | Check the DSN in connect(). |
can't distinguish duplicate field names | The query result has multiple columns with the same name. PHP's Informix extension deals with columns having the same names by overwriting the prior columns information. Therefore, tableInfo() is unable to properly represent these result sets. | Use aliases for columns that have the same names. |
This function can not be called statically.
tableInfo() is not portable because not all drivers have this method, many DBMS's are unable to determine table names from query results and the metadata returned by each database system varies dramatically.
Finding information about a table
<?php
// Once you have a valid DB object named $db...
$info = $db->tableInfo('tablename');
print_r($info);
?>
Finding information about a query result
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM tablename');
$info = $db->tableInfo($res);
print_r($info);
?>
Prior to version 1.6.0, tableInfo() was a part of the DB_result class, so had to be called like this:
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM tablename');
$info = $res->tableInfo(); // <---- DEPRECATED
print_r($info);
?>
integer fetchInto (
array &$arr
,
integer $fetchMode
= DB_FETCHMODE_DEFAULT
,
integer $rowNum
= null
)
Places a row of data from a result set into a variable you provide then moves the result pointer to the next row. The data can be formatted as an array or an object.
$arr
reference to a variable to contain the row
$fetchMode
the fetch mode to use. The default is DB_FETCHMODE_DEFAULT, which tells this method to use DB's current fetch mode. DB's current default fetch mode can be changed using setFetchMode(). Potential values include:
DB_FETCHMODE_ORDERED
DB_FETCHMODE_ASSOC
DB_FETCHMODE_OBJECT
$rowNum
the row number to fetch. Note that 0 returns the first row, 1 returns the second row, etc.
integer - DB_OK if a row is processed, NULL when the end of the result set is reached or a DB_Error object on failure
This function can not be called statically.
Using fetchInto()
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM mytable');
while ($res->fetchInto($row)) {
// Assuming DB's default fetchmode is
// DB_FETCHMODE_ORDERED
echo $row[0] . "\n";
}
?>
mixed &fetchRow (
integer $fetchmode
= DB_DEFAULT_MODE
,
integer $rownum
= null
)
Returns a row of data from a result set then moves the result pointer to the next row. The data can be formatted as an array or an object.
$fetchmode
the fetch mode to use. The default is DB_FETCHMODE_DEFAULT, which tells this method to use DB's current fetch mode. DB's current default fetch mode can be changed using setFetchMode(). Potential values include:
DB_FETCHMODE_ORDERED
DB_FETCHMODE_ASSOC
DB_FETCHMODE_OBJECT
$rownum
the row number to fetch
mixed - an array or object containing the row's data, NULL when the end of the result set is reached or a DB_Error object on failure
This function can not be called statically.
Using fetchRow()
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM mytable');
while ($row =& $res->fetchRow()) {
// Assuming DB's default fetchmode is
// DB_FETCHMODE_ORDERED
echo $row[0] . "\n";
}
?>
boolean free (
)
Deletes the result set and frees the memory occupied by the result set. Does not delete the DB_result object itself.
boolean - Returns TRUE on success, FALSE on failure.
Using free()
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT name, address FROM clients');
while ($row =& $res->fetchRow()) {
echo $row['name'] . ', ' . $row['address'] . "\n";
}
$res->free();
?>
This function can not be called statically.
boolean nextResult (
)
Some database backends supports executing more then one query at the same time. With nextResult() you can go through the result sets.
This function has nothing to do with with executeMultiple().
boolean - TRUE if DB_result contains the next result set, FALSE if there is no further result set to fetch
This function can not be called statically.
integer numCols (
)
Get the number of columns of the rows in a result set.
integer - number of columns or a DB_Error object on failure
This function can not be called statically.
Using numCols()
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM phptest');
echo $res->numCols();
?>
integer numRows (
)
Get the number of rows in a result set.
For
ibase
,ifx
andoci8
, this method only works if the DB_PORTABILITY_NUMROWS portability option is enabled. In addition, foribase
andifx
, PEAR DB must be at version 1.7.0 or greater.Does not work for Microsoft Access.
integer - number of rows or a DB_Error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
DB_ERROR_NOT_CAPABLE | DB backend not capable | Driver doesn't support this feature. | Either switch to another database system or enable the DB_PORTABILITY_NUMROWS portability option. |
This function can not be called statically.
Using numRows()
<?php
// Once you have a valid DB object named $db...
$res =& $db->query('SELECT * FROM phptest');
echo $res->numRows();
?>
In case of failure, most of the DB functions return a DB_Error object which contains information about the error. DB_Error offers the same functions as PEAR_Error.
The text messages returned by DB_Error::getMessage() are consistent between each DBMS.
The error code integers returned by DB_Error::getCode()
are also consistent between each DBMS. The integers returned are based
on the DB_ERROR_* constants defined in
DB.php
.
DB_Error::getDebugInfo() and DB_Error::getUserInfo() return complete native DBMS error reports.
Trapping errors and determining what happened
<?php
require_once 'DB.php';
$db =& DB::connect('pgsql://wronguser:badpw@localhost/thedb');
if (PEAR::isError($db)) {
/*
* This is not what you would really want to do in
* your program. It merely demonstrates what kinds
* of data you can get back from error objects.
*/
echo 'Standard Message: ' . $db->getMessage() . "\n";
echo 'Standard Code: ' . $db->getCode() . "\n";
echo 'DBMS/User Message: ' . $db->getUserInfo() . "\n";
echo 'DBMS/Debug Message: ' . $db->getDebugInfo() . "\n";
exit;
}
?>
SQL Builder and Data Modeling Layer
This chapter describes how to use the DB_DataObject SQL Builder and Data Modeling layer
Zend Optimizer: due to a bug in the optimizer, you will have to either reduce the optimization level, or define the constant DB_DATAOBJECT_NO_OVERLOAD = 0 otherwise PHP may segfault
Pass by Reference, due to a unfixable bug in PHP4, you can not use overload with pass-by-reference arguments (It works OK in PHP5), If you need pass-by-reference, define the constant DB_DATAOBJECT_NO_OVERLOAD = 0
DB_DataObject is a SQL Builder and Data Modeling Layer built on top of PEAR::DB. Its main purpose is to
So what does that mean in English? Well, if you look around at some of the better written PHP applications and frameworks out there, you will notice a common approach to using classes to wrap access to database tables or groups. The prime example of this is the person object, which would frequently look something like this.
A Classic Data Object or Container
<?php
require_once 'DB.php';
$db = DB::connect('mysql://someuser:somepass@localhost/pear_dbdo');
$db->setFetchMode(DB_FETCHMODE_ASSOC);
class MyPerson
{
// gets an array of data about the seleted person
function getPerson($id)
{
global $db;
$result = $db->query('SELECT * FROM person WHERE id=' . $db->quote($id));
return $result->fetchRow();
}
// example of checking a password.
function checkPassword($username, $password)
{
global $db;
$hashed = md5($password);
$result = $db->query(
'SELECT name FROM person WHERE name=' . $db->quote($username)
. ' AND password = ' . $db->quote($hashed)
);
return $result->fetchRow();
}
}
// get the persons details..
$array = MyPerson::getPerson(12);
echo $array['name'] . "\n";
?>
The examples operate on the following SQL database table:
CREATE TABLE IF NOT EXISTS `person` ( `id` int(11) NOT NULL auto_increment, `name` varchar(64) NOT NULL, `password` varchar(32) NOT NULL, `birthDate` date NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=13 ; INSERT INTO `person` (`id`, `name`, `password`, `birthDate`) VALUES (12, 'John', '098f6bcd4621d373cade4e832627b4f6', '1984-02-23');
The key benefit of this approach is that you are grouping similar actions on a single table in one place, and are more likely to spot duplicated code (eg. two methods that do similar things). You will also notice the global $db variable used here - the fact is that most of the time you will use a common database connection for all your classes, so how should this be dealt with?
The next step on this road is to use the objects variables as a storage mechanism.
A Classic Data Object or Container
<?php
require_once 'DB.php';
$db = DB::connect('mysql://someuser:somepass@localhost/pear_dbdo');
$db->setFetchMode(DB_FETCHMODE_ASSOC);
class MyPerson
{
var $id;
var $name;
var $birthDate;
// gets an array of data about the seleted person
function get($id) {
global $db;
$result = $db->query('SELECT * FROM person WHERE id=' . $db->quote($id));
$array = $result->fetchRow();
foreach ($array as $key => $value) {
$this->$key = $value;
}
}
function getAge() {
return date('Y') - date('Y', strtotime($this->birthDate));
}
}
// now get the person and display the age.
$person = new MyPerson();
$person->get(12);
echo "{$person->name} is ". $person->getAge() . " years old\n";
?>
As you can see, the current row of data is now stored in the Data Container, and additional methods can be added to manipulate the data in the object or even call other related objects (eg. tables relationships in databases)
As a next step, why not utilize the member variables to perform searches or gets on the database.
A Classic Data Object or Container
<?php
require_once 'DB.php';
$db = DB::connect('mysql://someuser:somepass@localhost/pear_dbdo');
$db->setFetchMode(DB_FETCHMODE_ASSOC);
class MyPerson
{
var $id;
var $name;
var $birthDate;
// does the query based on the value of $this->name
function find() {
global $db;
$this->result = $db->query('SELECT * FROM person WHERE name=' . $db->quote($this->name));
}
// fetches a row of data and sets the object variables to match it.
function fetch() {
$array = $this->result->fetchRow();
if (empty($array)) {
return false;
}
foreach ($array as $key=>$value) {
$this->$key = $value;
}
return true;
}
}
// now get the person and display the age.
$person = new MyPerson();
$person->name = "John";
$person->find();
while ($person->fetch()) {
echo "a {$person->name} has a birthday on {$person->birthDate}<br/>\n";
}
?>
As you can see, by assigning values to the object before the find method is called, you can set conditions for the query. DB_DataObjects behaves in a similar way to this, however, you can also add more conditions with the whereAdd() method, or restrict the selection using the selectAdd() method.
Obviously you can carry on down this road and create lots of little containers for each table in your database, and all the code will nicely relate to each table. However, you can see from the examples above that all classes are likely to end up with a common set of methods.
So to improve on this, DB_DataObject was born, it started life as a common answer to the issues above, however as most problems grow in complexity as the problem is examined in finer detail, so has DataObjects. It has grown to include
So what do my classes look like?
At last some real DataObject Code..
<?php
// this is the common configuration code - place in a general site wide include file.
// this the code used to load and store DataObjects Configuration.
$options = &PEAR::getStaticProperty('DB_DataObject','options');
// the simple examples use parse_ini_file, which is fast and efficient.
// however you could as easily use wddx, xml or your own configuration array.
$config = parse_ini_file('example.ini',TRUE);
// because PEAR::getstaticProperty was called with an & (get by reference)
// this will actually set the variable inside that method (a quasi static variable)
$options = $config['DB_DataObject'];
// this is normally contained in your DataObjects file (autogenerated by the generator)
require_once 'DB/DataObject.php';
// by extending the base class DB_DataObject - you inherit all the common methods defined in it.
class DataObjects_Person extends DB_DataObject {
var $id; // this is a primary id (it's specified in a config file - explained later)
var $name;
var $friend;
// this is a simple function to get the persons friends..?
function getFriends()
{
$personObject = $this->factory('person');
// look for all people with their friend number matching this persons id.
$personObject->friend = $this->id;
// do the select query.
$personObject->find();
$array = array();
// fetch the results into the object.
while ($personObject->fetch()) {
// use the clone to copy - not really needed but get used to it for PHP5
$array[] = clone($personObject);
}
// return the results.
return $array;
}
}
// and this goes on your display code
// create a new person class..
$person = DB_DataObject::Factory('person');
// get the person using the primary key.
$person->get(12);
// get the friends.
$friends = $person->getFriends();
// DB_DataObjects is designed to make print_r useable to debug your applications.
print_r($friends);
?>
The above example illustrates the components of the DB_DataObject, by setting the options, all the core objects will be able to auto load the data definitions from a generated ini file, and know how to access the database. (multiple databases are supported - see section on configuration)
The class definition illustrates how you only need to define the data specific code in your class, ignoring all the common methods, along with showing one of the methods for retrieveing multiple rows of data.
The later half illustrates how to query and get the result for a single row. The $person->get() would connect to the database, perform the query, fetch the result and assign the objects variables to the data returned from the query.
In the above example, this query would be performed.
SELECT * FROM person WHERE id=12; SELECT * FROM person WHERE friend=12;
To make a change to the Database you would just change the value of the objects variables and call the update method.
<?php
$person = DB_DataObject::factory('person');
$person->get(12);
$person->name = 'Fred';
$person->update();
?>
As a general rule of thumb method names are usually the same as the SQL statement they relate to.
DB_DataObject needs to be configured before using it and auto generating classes and definitions. The easiest way to configure DB_DataObject is to use ini files (although you may also like to consider the PEAR::Config class, or your own configuration system)
This is a typical configuration file for DB_DataObject
[DB_DataObject] database = mysql://user:password@localhost/vending schema_location = /home/me/Projects/myapplication/DataObjects class_location = /home/me/Projects/myapplication/DataObjects require_prefix = DataObjects/ class_prefix = DataObjects_ db_driver = MDB2 #Use this if you wish to use MDB2 as the driver quote_identifiers = 1
To use this ini file with DB_DataObject, (and possibly any other classes that use options like this)
Setting the default options
<?php
$config = parse_ini_file('example.ini',TRUE);
foreach($config as $class=>$values) {
$options = &PEAR::getStaticProperty($class,'options');
$options = $values;
}
// or you can do without an ini file, and configure it in PHP..
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$options = array(
'database' => 'mysql://user:password@localhost/vending',
'schema_location' => '/home/me/Projects/myapplication/DataObjects',
'class_location' => '/home/me/Projects/myapplication/DataObjects',
'require_prefix' => 'DataObjects/',
'class_prefix' => 'DataObjects_',
'db_driver' => 'MDB2', //Use this if you wish to use MDB2 as the driver
'quote_identifiers' => true
);
?>
database
DSN
This is the default data source name (DSN) to connect to your database. See the DSN configuration page for DB or the DSN configuration page for MDB2 for more details.
schema_location
directory
The directory where the DB_DataObject database schema file is store.
DB_DataObject stores the description of the database (Tables and Columns) in an .ini file, in this directory. This information is used to determine if the column is a string and needs quotes, or should be a number (and is checked) at SQL building time. It is quite common to store the schema in the same directory as your DataObject Classes.
require_prefix
directory
The Path absolute, or relative to your default include path(s), where your extended classes can be found.
This is used by the staticGet() method and the getLinks() method to auto load classes,
class_prefix
string
All the generated Classes are named
{class_prefix}ucfirst($table_name)
. Use this to
alter the prefixed name, this is used by the staticGet() and getLinks() methods
db_driver
string
default = DB
, Use this configuration option to set the database abstraction
driver used by DB_DataObject.
using MDB2 as the driver
db_driver = MDB2
sequence_{table}
string
To Hard code the key (autoincrement/nextval() for a table to a specific key, overriding anything in the keys definition of the file. Normally used on databases that are not able to be queried correctly for their structure
using login as the key for the person table
sequence_person = login
ignore_sequence_keys
string
If you do not want to use pear's nextval(), for automatically filling in sequences, this can disable it for "ALL", or a list of tables "person,cart,group"
debug
integer
The default debugging level (default 0=off), 1= basic sql logging,2=result logging, 3=everything
debug_ignore_updates
boolean
default FALSE, if set, then updates on the database are disabled.
dont_die
boolean
default FALSE, The standard behaviour of dataobjects is to issue a PEAR_ERROR_DIE (eg. exiting PHP), when a fatal error occurs, like database connection failure or sending an invalid object type to a method. However if you need to run it on a live server you will probably want to set this to TRUE and define a PEAR error handler to catch these errors and show a nice friendly 'sorry we are down for maintenence' message page.
quote_identifiers
boolean
To force the quotation of identifiers in the SQL statements, set this to 1. This is useful if any table names use hyphens.
Statement Generated with and without quote_identifiers
quote_identifiers = 1; SELECT 'somecol' FROM 'sometable' WHERE 'somevalue'=1; quote_identifiers = 0; SELECT somecol FROM sometable WHERE somevalue=1;
Note: This will not affect data sent to methods such as whereAdd(), orderBy(), and groupBy(), which expect raw data.
proxy
string
This enables the building of classes and ini classes on the fly, rather than forcing you to generate the code forhand. (currently the only value supported is "full", which will generate both schema data and default classes when using factory)
database_*
string
When you have multiple databases you can use the database_* to specify the DSN for each database
using multiple databases - database passwords
database_authentication = mysql://user:password@localhost/authentication database_sales = mysql://user:password@localhost/sales
table_*
string
When you have multiple databases you can use the table_* configuration variables to map individual tables to different databases, for example
using multiple databases - table settings
table_users = authentication table_saleslog = sales table_stock = sales
class_location
directory
The Directory where your DataObject extended Classes are.
Used by the Class Auto Builder when updating/writing to your class definitions.
extends
string
The Name of your Base Class (usually DB_DataObject) is located.
If you wish to add a common layer of useful methods for all classes, you can set the extends_location and
extends settings to a different class. the default is
'DB_DataObject'
extends_location
directory
The Directory where your Base Class (usually DB_DataObject) is located.
If you wish to add a common layer of useful methods for all classes, you can set the extends_location and
extends settings to a different class. the default is
'DB/DataObject.php'
generator_class_rewrite
boolean
Normally when you recreate a class from the database, it will only alter the variables, and staticGet, - with this set, it will also update the extends field
build_views
boolean
Postgres (and maybe some others), allow you to treat views just like normal tables (eg. insert/update/delete etc. work on them), you can use this option to generate files for all the views in the database.
Note: You will have to specify keys manually in the generated classes (eg. define the methods keys() and sequenceKey(), as the generator can not guess which ones are likely to be the key.
generator_include_regex
string
If you only want to generate classes and ini entries for specific tables, you can use this to build a regex, only tables with names matching the regex will be generated, for example /mytables_.*/
generator_exclude_regex
string
If you only want to explicitly prevent the generation of classes and ini entries for specific tables, you can use this to build a regex, any tables that match the regex, will not be generated, for example /private_tables_.*/
generator_strip_schema
boolean
postgresql has a wierd concept of schemas which end up prefixed to the list of tables. - this makes a mess of class/schema generation setting this to 1, makes the generator strip the schema from the table name
generator_novars
boolean
If True, the generator does not write a private or var's definition for the columns so you can overload get/set.
generator_add_validate_stubs
boolean
If True, the generator will insert / (or add to existing files) stubs for validate methods.
One of the essential features of an SQL building tool is to to have some understanding of the database structure, So that Integers can be checked, and strings can be escaped. There a few ways that Querying the database for the table structure could be accomplished
DB_DataObject uses the last of these normally (see the section below on Alternatives), it utilizes the parse_ini_file function to read the file, so it should be reasonably fast. However this does involve a stage of setting up DB_DataObject prior to use.
The other key concept of DB_DataObject is that you work with extended classes of DB_DataObject, which do all the 'table' related work. Setting up these classes for a large database can be time consuming, so the createTables.php file will automatically build the skeletons for all these class files.
To start the auto builder simply go to the
pear/DB/DataObject/
directory, and type
c:\php4\php.exe createTables.php myconfig.ini this will read your configuration file
and generate all the base classes, along with the data definition file.
As of Version 1.5, you can use the option "proxy = full", which will cause DataObjects to create classes and schema on the fly, rather than using an ini file or prebuilt classes.
The default generated class looks like this.
an generated extended class
<?php
/*
* Table Definition for group
*/
class DataObjects_Grp extends DB_DataObject {
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
var $__table='group'; // table name
var $id; // int primary_key
var $name; // string
var $grp_owner; // int
var $official; // string
var $street; // string
var $postcode; // string
var $city; // string
var $homepage; // string
var $email; // string
var $extra; // blob
/* Static get */
function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('DataObjects_Grp',$k,$v); }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
}
?>
The class defines the table name, and comments some of table columns for your reference, It also adds the staticGet() method, which can be used to quickly get single rows as Objects. You should add your table related code after the ###END_AUTOCODE.
Prior to version 1.6 a method __clone was created, since PHP5 changed to use $x = clone($y);, this method has been removed. DataObjects now creates a dummy function clone (if necessary), to enable forward compatibility
The default generated database definition file looks like this, it is located in the directory set by the "schema_location" configuration option, and is named the same as the database.
If you rename the database, you will either have to regenerate the file or copy it to match the same name.
If you change the database schema (eg. add a column or change the type), you will have to regenerate the schema file, using createTables.php, At present, this is not done automatically (although this may be added in the future..)
You should not edit this file by hand (as any changes will be lost when regenerating it). You can override the keys of a table by using a configuration option "sequence_{table} = key", or by defining the sequenceKey() method in your extended class.
Database configuration file
[group] id = 129 name = 130 grp_owner = 129 official = 130 street = 130 postcode = 130 city = 130 homepage = 130 email = 130 extra = 130 [group__keys] id = N
The blocks indicate either tables and the type of field with binary addition (1=integer,2=string,128=not null, so 129=integer and not null), or a list of keys for each table. You should not need to edit file.
It is possible to use DataObjects without a schema file, this can be achieved in 2 ways
The second of this is demonstrated on the keys() and table() page.
Below is a hand coded class which does not use a schema.ini file.
It was designed to be a return value from a method, rather than an object variable, so that the output of print_r(), would not include extra information (and would be smaller when dumping a large result set.
A Hand Coded extended class
<?php
/*
* Table Definition for group
*/
class DataObjects_Grp extends DB_DataObject {
// you can define these yourself
var $__table='group'; // table name
var $id; // int primary_key
var $name; // string
var $bday; // string
var $last; // datetime
var $active; // tinyint(1)
var $desc; // text
var $photo; // blob
// these are usefull to be consistant with a autogenerated file.
/* Static get */
function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('DataObjects_Grp',$k,$v); }
// now define your table structure.
// key is column name, value is type
function table() {
return array(
'id' => DB_DATAOBJECT_INT,
'name' => DB_DATAOBJECT_STR,
'bday' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE,
'last' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
'active' => DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL,
'desc' => DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT,
'photo' => DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB,
);
}
// now define the keys.
function keys() {
return array('id');
}
}
?>
mixed DB_DataObject::factory (
string $table
)
This is the recommended way to autoload a class, and instantate it. The class is loaded based on the configuration settings (class_location and class_prefix) for table to class naming.
string $table
- the table you want to load
([From Version 1.7.2] if blank, and called on an an instance of a dataobject, it will create a new
instance of that object)
object mixed
-
DB_DataObject_Error or the object
Error code | Error message | Meaning | Solution |
---|---|---|---|
DB_DATAOBJECT_ERROR_NOCLASS | "could not autoload $class" |
This method can be called statically or dynamically.
Simple fetch of data based on Primary Key
<?php
// set up our options
$opts = &PEAR::getStaticProperty('DB_DataObject','options');
$opts = array(
'class_location' => '/home/me/Projects/myapplication/DataObjects',
'class_prefix' => 'DataObjects_'
);
// loads the file: /home/me/Projects/myapplication/DataObjects/Person.php
// and checks that the class DataObjects_Person exists, and returns an
// instance of it.
$person = DB_DataObject::factory('person');
if ($person->get(12)) {
print_r($person);
} else {
echo "NO person 12 exists";
}
// it can also be used in a dynamically
class DataObjects_MyTable {
function anExample() {
$person = $this->factory('person');
// supported in version 1.7.2
$another_mytable = $this->factory();
$another_person = $person->factory();
}
}
?>
int $DB_DataObject->get (
mixed Key or Value
,
mixed value
)
Get a result using key, value. Returns Number of rows located (usually 1) for success, and puts all the table columns into this classes variables. If only one parameter is used, it is assumed that first parameter is a value and get() will use the primary key.
mixed $key or $value
- column (or value if only one parameter)
mixed $value
- value
int
- Number of rows
Error code | Error message | Meaning | Solution |
---|---|---|---|
DB_DATAOBJECT_ERROR_INVALIDCONFIG | "No Keys available for $table" | ||
DB_DATAOBJECT_ERROR_INVALIDARGS | "No Value specified for get" |
This function can not be called statically.
You should avoid calling get on the same object instance twice, as this will result in unexpected results.
Simple fetch of data based on Primary Key
<?php
$person = new DataObjects_Person;
$person->get(12);
print_r($person);
?>
Resulting SQL
Simple fetch of data based on Key and Value
<?php
$person = new DataObjects_Person;
$person->get('email','test@example.com');
print_r($person);
?>
Resulting SQL
Results of example code
<?php
Object (DataObjects_Person) =>
[N] => 1
[id] => 12
[group] => 5
[has_glasses] => 1
[name] => 'fred blogs'
[password] => '**testing'
[email] => 'test@example.com'
?>
mixed DB_DataObject::staticGet (
string $class
,
mixed $key or $value
,
mixed $value
)
This method is depreciated, it is recommended to use ::factory() and ->get()
The static method is a combination of factory and get(). staticGet() will cache the returned data in a global variable for quick access within the same request (any data modification query will clear the cache).
string $class
-
class name
string $key
- column (or value if only 2 parameters are given)
mixed $value
- value
object mixed
- FALSE or the object
Error code | Error message | Meaning | Solution |
---|---|---|---|
DB_DATAOBJECT_ERROR_NOCLASS | "could not autoload $class" | ||
DB_DATAOBJECT_ERROR_NOCLASS | "Error creating $newclass" | ||
DB_DATAOBJECT_ERROR_NODATA | "No Data return from get $key $value" |
This method must be called statically.
Simple fetch of data based on Primary Key or column and value
<?php
$person = DB_DataObject::staticGet('DataObjects_Person', 12);
print_r($person);
$person = DB_DataObject::staticGet('DataObjects_Person', 'name', 'fred');
print_r($person);
?>
object {Child Class}::staticGet (
mixed $key or $value
,
mixed $value
)
The static method is similar to the get request, however it does not require the initial instantiation of the class. staticGet() can optionally cache the results. (see the configuration section)
Note: This can only be used for tables with a single column primary key.
string $key
- column or value if only one parameter is given
mixed $value
- value
object mixed
- FALSE or the object
Error code | Error message | Meaning | Solution |
---|---|---|---|
DB_DATAOBJECT_ERROR_NOCLASS | "could not autoload $class" | ||
DB_DATAOBJECT_ERROR_NOCLASS | "Error creating $newclass" | ||
DB_DATAOBJECT_ERROR_NODATA | "No Data return from get $key $value" |
This method must be called statically.
Simple fetch of data based on Primary Key or column and value
<?php
$person = DataObjects_Person::staticGet(12);
print_r($person);
$person = DataObjects_Person::staticGet('name', 'fred');
print_r($person);
?>
int $DB_DataObject->find (
boolean $autoFetch
)
The find method builds and executes the current Query, based on the object variables and any WhereAdd() conditions , If the AutoFetch is TRUE, then it will also call the fetch method automatically.
boolean $autoFetch
- fetch first result
int
- number of rows found, only if the database backend supports the numRows() method.
Otherwise 1 (or in versions after 1.7.13, true will returned)
This function can not be called statically.
Simple find() of data based on Object Vars
<?php
$person = new DataObjects_Person;
$person->hair = 'red';
$person->has_glasses = 1;
$number_of_rows = $person->find();
?>
Resulting SQL
<?php
SELECT * FROM person WHERE hair='red' and has_glasses = 1
?>
boolean $DB_DataObject->fetch (
)
The fetch method gets the next row and sets the objects variables to the rows data. It returns TRUE if data has been collected, and FALSE when there is no more data.
boolean
- TRUE on success and FALSE on failure.
This function can not be called statically.
Fetch is called by staticGet and get calls, so you can override this method in your classes, to add extra data to your object (like formated dates etc)
Simple find and fetch of data based on object Vars
<?php
$person = new DataObjects_Person;
$person->hair = 'red';
$person->has_glasses = 1;
$number_of_rows = $person->find();
$people = array();
while ($person->fetch()) {
/* store the results in an array */
$people[] = clone($person);
echo "GOT {$person->name}<BR>";
}
?>
Overriding fetch to add extra data.
<?php
function fetch() {
$ret = parent::fetch();
if ($ret === false) {
return false;
}
$this->dateFormated = date('d/M/Y', $this->date);
return true;
}
?>
int $DB_DataObject->count (
string|boolean $countWhat|$whereAddOnly
, boolean $whereAddOnly
)
It performs a select count() request on the tables key column and returns the number of resulting rows. The default condition applied to the count() is a combination of the object variables and whereAdd settings. If the constant DB_DATAOBJECT_WHEREADD_ONLY is passed in as the first parameter then only the whereAdd settings will be used.
string $countWhat
- by default count will count on the primary key,
if you need to count something else, if you just say DISTINCT,
it will count the primiary key prefixed with distinct, or put your own value in
(don't forget to escape it if necessary)
boolean $useWhereAddOnly
- use only the
whereAdd conditions (by default, count will only use both the object settings and
the whereAdd conditions)
int|false
- number of results or false on an error
This function can not be called statically.
Various examples of using count()
<?php
/* using property values */
$person = new DataObjects_Person;
$person->name = "test"
$total = $person->count();
echo "There are {$total} people with a name like test";
/* using countWhat */
$person = new DataObjects_Person;
$total = $person->count('DISTINCT name');
echo "There are {$total} names in the database";
/* using countWhat value = DISTINCT */
$person = new DataObjects_Person;
$total = $person->count('DISTINCT');
echo "There are {$total} names in the database";
/* using whereOnly */
$person = new DataObjects_Person;
$person->name = "test";
$person->whereAdd("name like '%test%'");
$total = $person->count(DB_DATAOBJECT_WHEREADD_ONLY);
echo "There are {$total} names in the database";
?>
Resulting SQL
mixed $DB_DataObject->insert (
)
Insert the data into the database, based on the variable values of the current object and returns the ID of the inserted element if sequences or primary keys are being used. The values are correctly quoted, and some limited type checking is done.
With mysql, the mysql_next_id() method is used, on other databases, PEAR DB sequence method is used.
Note, insert() may not return the ID correctly in quite a few situations:
mixed
- Id or key
Error code | Error message | Meaning | Solution |
---|---|---|---|
DB_DATAOBJECT_ERROR_INVALIDCONFIG | "insert:No table definition for $table" | ||
DB_DATAOBJECT_ERROR_NODATA | "insert: No Data specifed for query" | ||
DB_* | * | see PEAR::DB | see PEAR::DB |
This function can not be called statically.
Simple insert
<?php
$person = new DataObjects_Person;
$person->name='fred';
$id = $person->insert();
?>
Resulting SQL
<?php
INSERT INTO person (name) VALUES ('fred');
?>
int $DB_DataObject->update (
dataobject|boolean $original|$useWhere
)
Updates current objects variables into the database. if you supply it with a dataObject, as an argument, it will only update the differences between the new and old.
if called with DB_DATAOBJECT_WHEREADD_ONLY as the argument, the update request is built based on the whereAdd values, rather than the primary key. This enables global updates to be performed, rather than single row ones.
DataObject $original
-
if provided the update query will be built from the difference between the current and
original dataobject.
int
number of rows affected or FALSE on failure
Error code | Error message | Meaning | Solution |
---|---|---|---|
DB_DATAOBJECT_ERROR_INVALIDCONFIG | "update:No table definition for $table" | ||
DB_DATAOBJECT_ERROR_NODATA | "update: No Data specifed for query $settings" |
This function can not be called statically.
Simple fetch and update
<?php
$person = new DataObjects_Person;
$person->get(12);
$person->name='fred';
$person->update();
$person = new DataObjects_Person;
$person->get(12);
$original = clone($person); // clone is emulated in php4 for compatibility reasons.
$person->name='fred';
$person->update($original);
?>
Resulting SQL
Simple fetch and update
<?php
$person = new DataObjects_Person;
$person->removed=1;
$person->whereAdd('age > 21');
$person->update();
?>
Resulting SQL
int $DB_DataObject->delete (
boolean $useWhere
)
Deletes data from the database, either using primary key or based on a whereAdd() method call. By default the delete will base its query on the set variables, however if you wish to use the whereAdd() method you should set the $useWhere parameter to DB_DATAOBJECT_WHEREADD_ONLY.
boolean $use_where
-
use the
whereAdd() conditions (by default, delete will only use primary keys)
int
number of rows affected or FALSE on failure
Error code | Error message | Meaning | Solution |
---|---|---|---|
DB_* | "*" | see PEAR::DB | see PEAR::DB |
DB_DATAOBJECT_ERROR_NODATA |
"delete: No Data specifed for query
$condition "
|
This function can not be called statically.
Simple Delete
<?php
$person = new DataObjects_Person;
$person->get(12);
$person->delete();
$person = new DataObjects_Person;
$person->name = 'test';
$person->age = 21;
$person->delete();
$person = new DataObjects_Person;
$person->whereAdd('age < 21');
$person->delete(DB_DATAOBJECT_WHEREADD_ONLY);
?>
Resulting SQL
<?php
SELECT * FROM person WHERE person.id = 12
DELETE FROM person WHERE ( person.id = 12 )
DELETE FROM person WHERE ( person.name = 'test' ) AND ( person.age = 21 )
DELETE FROM person WHERE ( age < 21 )
?>
The basic features allow most simple queries to be done very quickly, however building more complex queries can be done using the methods listed below, which append or set conditions that build the query. You will find that there is a fine balance between using these builder methods and just using raw SQL.
The DB_DataObject can now handle JOIN queries, however please read the introduction to linking and Joining before jumping in and using the Join feature, as you may be trading the use of a cool feature, against the readibility of your code.
void $DB_DataObject->query (
string $string
)
Sends a raw query to database. Often you may consider that using a large number of whereAdd's and orderBy method calls are not necessary, and it would be simpler to just write the query, to make the code clearer and shorter.
string $string
- SQL Query
This function can not be called statically.
raw queries on the database
<?php
$person = new DataObjects_Person;
$person->query("SELECT * FROM {$person->__table} WHERE id > 12 ORDER BY id");
while ($person->fetch()) {
echo $person->name;
}
?>
object $DB_DataObject->free (
)
DataObjects stores result set's as a private global variable, normally this is free'ed after you have run through the results, or at the end of the request. However in some situations, like running queries directly, inserting data, some data is unnecessarly cached.
Using this method will Free All results sets! (so be carefull) it may break running fetch() loops..
You normally only need to use this if you are doing a large number of inserts or queries.
This function can not be called statically.
Freeing resources in a loop
<?php
for ($i = 0; i< 10000; $i++) {
$person = new DataObjects_Person;
$person->query(' ... do something ... ');
$person->free();
}
?>
void $DB_DataObject->selectAdd
(
string $condition
)
Adds a selected columns. By default a select query will request all items (eg. SELECT * FROM table), to change this behavior you can first call selectAdd() without any arguments to clear the current request and then add the specific items you require.
You can also set up a default selection query by adding SelectAdd() method calls in the object constructor method (the one with the same name as the class)
resource $key
-
table column name
This function can not be called statically.
Using selectAdd()
<?php
$person = new DataObjects_Person;
$person->selectAdd();
$person->selectAdd('id,name');
while ($person->fetch()) {
echo "{$person->id} {$person->name}<BR>";
}
$person = new DataObjects_Person;
$person->selectAdd("DATE_FORMAT(birthday,'%d %m %Y') as birthday_formated ");
$person->id = 12;
$person->find(TRUE);
echo "{$person->name} {$person->birthday_formated}<BR>";
?>
Resulting SQL
void $DB_DataObject->whereAdd (
string $where
,
string $logic
)
Adds items to the where part of a SQL query. Calling this without any arguments clears the where condition. The default behavior is to add 'AND' to the existing conditions, use the $logic parameter to append OR conditions.
string $cond
- condition to add, or blank to reset the conditions
string $logic
- optional logic "OR" (defaults to "AND")
This function can not be called statically.
The quote_identifiers configuration option will not affect data sent to whereAdd.
Using whereAdd()
<?php
$person = new DataObjects_Person;
$person->whereAdd('age > 12');
$person->whereAdd('age < 30');
$person->find();
while ($person->fetch()) {
echo "{$person->id} {$person->name}<br />";
}
$person = new DataObjects_Person;
$person->whereAdd('age < 12');
$person->whereAdd('age > 30', 'OR');
$person->find();
while ($person->fetch()) {
echo "{$person->id} {$person->name}<br />";
}
?>
Resulting SQL
void $DB_DataObject->escape (
string $value
)
Similar to Pear DB's quote it will escape a value, without the quotes, so it can be used with a LIKE query.
string $value
- the string you want to escape
This function can not be called statically.
Escaping a LIKE string
<?php
$person = new DataObjects_Person;
$person->whereAdd("name LIKE '%" . $person->escape("o'brian") . "%'");
$person->find();
?>
Sample SQL
void $DB_DataObject->limit (
int $from
,
int $number
)
Sets the limit for a query. (this only works on databases that support the LIMIT clause), without parameters, it will clear the current limit.
int $from
- limit start (or number), or blank to reset
int $number
- limit results to number
This function can not be called statically.
Since postgres and mysql only really support limit directly - calling this on an unsupported database will emit a PEAR::Error and die.
Setting the Limit
<?php
$person = new DataObjects_Person;
$person->limit(2);
$person->find();
while ($person->fetch()) {
echo "{$person->id} {$person->name}<BR>";
}
$person = new DataObjects_Person;
$person->limit(2,4);
$person->find();
while ($person->fetch()) {
echo "{$person->id} {$person->name}<BR>";
}
?>
Resulting SQL
void $DB_DataObject->orderBy (
string $order
)
Adds a order by condition. Calling this without any arguments clears the current order condition.
string $order
- Order
This function can not be called statically.
The quote_identifiers configuration option will not affect data sent to orderBy.
Setting the order by
<?php
$person = new DataObjects_Person;
$person->orderBy('name');
$person->orderBy('age, eye');
$person->find();
// or with direction:
$person = new DataObjects_Person;
$person->orderBy('name ASC');
$person->orderBy('age DESC, eye');
$person->find();
?>
Resulting SQL
void $DB_DataObject->groupBy (
string $group
)
Adds a group by condition. Calling this without any arguments clears the Group Condition
string $group
- Grouping condition
This function can not be called statically.
The quote_identifiers configuration option will not affect data sent to groupBy.
Setting the Group by
<?php
$person = new DataObjects_Person;
$person->groupBy('name');
$person->groupBy('age, eye');
$person->find();
?>
Resulting SQL
When designing a database, often some tables are related to others - a membership table would contain a reference to a person's id and the group id that they are a member of. Using the Link methods, you can automatically fetch objects into the parents variables.
Automated links are supported by a databasename.links.ini file. which stores the relations ship between tables, maping one tables column to anothers. This databasename.links.ini file is used by the getLinks() and joinAdd() Method, to either retrieve related information of a primary object, or quickly build complex multitable queries.
The other way of using linking is via the getLink() method, which you can manually use without a database.links.ini file to specify a column, and how it relates to another table and column.
The goal of getlinks and joinAdd is to make connecting two tables as simple and fast as possible, while still ensuring that the code is reasonably comprehensable. In the example below, It is demostrated how getlinks() can be used to fetch more data about an object after the initial fetch, and it can also be used to test the links file prior to building a full blown join Query.
A simple introduction to links and joins
<?php
// just loop and fetch more information
$person = new DataObjects_Person;
$person->eyeColour = 'red';
$person->find();
while ($person->fetch()) {
// this will look up in the cars table for the id matching the person->car value.
$car = $person->getLink('car','cars','id');
echo "{$person->name} has red eyes and owns a {$car->type}\n";
}
// now if you create a database.links.ini file with the car example
// the example would look like this.
$person = new DataObjects_Person;
$person->eyeColour = 'red';
$person->find();
while ($person->fetch()) {
// use the links.ini file to automatically load
// the car object into $person->_car
$person->getLinks();
echo "{$person->name} has red eyes and owns a {$person->_car->type}\n";
}
// and finally the most complex, using SQL joins and select as.
// the example would look like this.
$person = new DataObjects_Person;
$person->eyeColour = 'red';
// first, use selectAs as to make the select clauses match the column names.
$person->selectAs();
// now lets create the related element
$car = new DataObjects_Car;
// and for fun.. lets look for red eys and red cars..
$car->colour = 'red';
// add them together.
$person->joinAdd($car);
// since both tables have the column id in them, we need to reformat the query so
// that the car columns have a different name.
$person->selectAs($car,'car_%s');
$person->find();
while ($person->fetch()) {
echo "{$person->name} has red eyes and owns a {$person->car_type}\n";
}
?>
DB_DataObject Version 0.3 introduced the ability to create link ini files so you can map column to other database columns using an ini file this ini file should have the name 'databasename.links.ini', and be placed in the same folder as the database schema ini file 'databasename.ini' file that is created automatically by createTables.php
The databasename.links.ini file contains a section for each table, then the column that is linked equal to the table and column that it should locate the column from. It assumes the relationships are non-primary id to primary id, as the example below shows, the person.owner is linked to grp.id. This indicates that running getLinks() on the person object, will fetch a single bit of data from 3 tables - colurs,grp,attachments.
If you use a 'full stop' in the key (link from column), getLinks() will look up in the table with the column name matching the string to the left of the 'full stop', and replace the 'full stop' with and underscore and assign the object variable to that name. Or you may wish to use the selectAs() method to decide how you want columns from different objects to be returned (when using joinAdd())
An example databasename.links.ini file
; for table person [person] ; link value of eycolor to table colors, match name column eyecolor = colors:name ; link value of owner to table grp, match id column owner = grp:id ; link value of picture to table attachments, match id column picture = attachments:id ; for a sales example with multiple links of a single column [sales] ; for autoloading the car object into $sales->_car_id car_id = car:id ; for autoloading the part number object into $sales->_car_id_partnum car_id.partnum = part_numbers:car_id
It is also possible to create joins on keys consisting of more than one column. Use the following syntax:
Linking tables on composite keys
[table_b] field1,field2 = table_a:field1,field2
This will lead to the following select statement (here using the INNER JOIN syntax):
Resulting SQL
<?php
SELECT * FROM table_b INNER JOIN table_a ON table_b.field1 = table_a.field1 AND table_b.field2 = table_a.field2
?>
mixed $DB_DataObject->getLink (
string $column
,
string $table
,
string $key
)
Fetch a related object. This should be used in conjunction with a <dbname>.links.ini file configuration (see the introduction on linking for details on this).
You may also use this with all parameters to specify, the column, and related table and column.
string $column
-
either column or column.xxxxx
string $table
- name of table to look up value in
string $link
- name of column in other table to match
mixed
- object on success or FALSE on failure.
This function can not be called statically.
Getting the related object
<?php
$person = new DataObjects_Person;
$person->get(12);
$group = $person->getLink('group_owner');
echo $group->name;
$group = $person->getLink('colourid','hair');
?>
Resulting SQL
boolean $DB_DataObject->getLinks (
string $variableFormat
)
Loads the all the related objects into the main object, by using the links.ini
relationships, and sets the calling objects variables with the row name prefixed
with an underscode (_
) to the resulting objects.
Using this with the earlier column naming convention is depreciated, and links.ini files should be used.
string $variableFormat
- the default behavior is to assign the resulting
objects to variables with the row name prefixed with an underscode (_
), however,
you can use this value to format the variable differently
examples of formaters
if room.occupied_by is linked to a person.id without a modifier - eg _%s results in the equivilant of $object->_occupied_by = $object->getLink('occupied_by'); with a modifier - eg link_%s results in the equivilant of $object->link_occupied_by = $object->getLink('occupied_by');
boolean
- TRUE on success and FALSE on failure
This function can not be called statically.
Two Example Tables
Loading all the related objects
<?php
$person = new DataObjects_Person;
$person->get(1079);
$person->getLinks();
print_r($person);
?>
Resulting SQL
Resulting Output
Example with three tables join
<?php
/**
* The following example show a three tables join.
*
* More joins can be nested as you see fit.
*/
$person = new DataObjects_Person;
$data = array();
if ($person->find()) {
while ($person->fetch()) {
$person->getLinks();
// Following is another call to getLinks for the second join
$person->_convention_id->getLinks();
$data[] = $person->_convention_id->_room_id->ToArray();
}
}
print_r($data);
?>
databasename.links.ini
void $DB_DataObject->selectAs (
object | array $columns_or_object
, string $format
, string $table_name
)
Auto creates select items based on either the current objects column names, the supplied object, or an array.
This is primarily used in conjunciton with joinAdd, to enable the renaming of columns into a fixed format when they are likely to have naming conflicts (like both tables have an 'id' column).
Sending no arguments to selectAs, will reset the current select (usually removing the default *), and build a select list based on the current objects column names.
object | array $column_or_object
- a dataobject or array of column names
string $format
- the format the columns will appear, using sprintf format,
the %s is replaced with the column name so car_%s would result in the SQL 'car.name as car_name'
being generated.
string $tableName
- this is used either when use send an array as the first
argument, or when you are joining a table 'as' another name,
This function can not be called statically.
Using selectAs()
<?php
// ok lets see what is going on..
DB_DataObject::debugLevel(1);
$person = new DataObjects_Person;
// to generate "person.id as id , person.name as name ......."
$person->selectAs();
// to generate a restricted list.. "person.age as age , person.name as name"
$person->selectAs(array('age','name'));
// using another object.
$car = new DataObjects_Car;
// this is the first car (
$car->use = 'first';
// using the joinadd add the car..
$person->joinAdd($car);
// now add all the select columns for the car eg. "car.id as car_id, car.name as car_name ...."
$person->selectAs($car, 'car_%s');
// select only a few columns from the car table.
// note you have to use the table name at the end..
$person->selectAs(array('color','topspeed'), 'car_%s','car');
// now the user can have a second car....
$secondcar = new DataObjects_Car;
$secondcar->use = 'second';
// now since we alreay have one car, the sql statement would get messy
// so we are joining this as the second car "FROM person INNER JOIN car ..... , car as secondcar WHERE .....
$person->joinAdd($secondcar,'','secondcar');
// and we can now add all the columns
// "secondcar.id as secondcar_id, secondcar.name as secondcar_name ........
// note that you must use the last field as the SECONDCAR.ID format uses the 'AS' name, rather than the
// objects real table name 'car'
$person->selectAs($secondcar, 'secondcar_%s','secondcar');
// ok fire of a query...
$person->find();
while ($person->fetch()) {
......
}
?>
void $DB_DataObject->joinAdd (
object $dataobject
,
string $joinType
,
string $joinAs
,
string $joinCol
)
Builds a Join Query, by adding another dataobject to this one. Be careful when using this, raw queries may be clearer than using joinAdd.
Thanks to Stijn de Reede for the implementation of this.
object $obj
- joining object (no value resets the join)
string $joinType
- "LEFT" | "INNER " | "RIGHT" | ""
INNER is default, "" indicates just select ... from a,b,c with no join and links are added as where items.
Note: 'LEFT' is the same as LEFT OUTER.
string $joinAs
- if you want to select the table as anther name
useful when you want to select multiple columns from a secondary table.
string $joinCol
- The column on This objects table to match,needed
if this table links to the child object in
multiple places eg.
using specific join Columns
user->friend (is a id of a person) user->mother (is a id of another person)
This function can not be called statically.
The Examples below are not tested, use DB_DataObject::debugLevel(1), to see what exactly is going on when you use this, and send the author some better examples..
Simple simple Join
<?php
// (requires links.ini to be set up correctly)
// get all the images for product 24
$i = new DataObject_Image();
$pi = new DataObjects_Product_image();
$pi->product_id = 24; // set the product id to 24
$i->joinAdd($pi); // add the product_image connectoin
$i->find();
while ($i->fetch()) {
// do stuff
}
?>
Resulting SQL
More Complex Join query
<?php
// an example with 2 joins
// get all the images linked with products or productgroups
$i = new DataObject_Image();
$pi = new DataObject_Product_image();
$pgi = new DataObject_Productgroup_image();
$i->joinAdd($pi);
$i->joinAdd($pgi);
$i->find();
while ($i->fetch()) {
// do stuff
}
?>
Resulting SQL
true | string $DB_DataObject->set* (
mixed $value
)
mixed $DB_DataObject->get* (
)
From version PHP 4.3.2RC2 onwards, DB_DataObject is automatically overloaded, providing access to all variables using $object->set{ColumnName}() and $object->set{ColumnName}($value) even if you have not defined the method.
It is assumed that set methods return strings as errors or TRUE, so that it can interact with setFrom and return array's of error strings.
The get Methods are used by toArray(), if defined they can be used to alter the appearance of columns ,like making dates human readable
The logic is very simple, if you call $object->setXXX() and it is not defined, it will just set the value, if you define a method setXXXX, that will be called instead of the default handler, same applies to getXXX().
Due to the naming conflict possiblity of a column named from, the associated method for column 'from' is set_from, rather than setFrom()
mixed $value
- on setters only (the value to assign to the column), on getters you may like to implement date formating
or sprintf formating as the argument.
mixed
- setters will return TRUE from the default method,
in your implementations of setters. It is expected that setXXX($value) will return
a string (the error) if it is invalid or TRUE on success.
getXXX may return the value or a formated value, remember though it affects
$object->toArray().
This function can not be called statically.
Warning: This is experimental, its behavour may change slightly in the future.
Simple find and fetch of data based on Object Vars
<?php
class DataObjects_Person extends DB_DataObject {
var $id;
var $name;
var $date_of_birth;
// you can define this method after you implement a call to it!
// as it is automatically implemented to return $this->date_of_birth by
// the overload __call() method.
function getDate_of_Birth() {
return date('d M Y', strtotime($this->date_of_birth));
}
}
$person = new DataObjects_Person;
$person->get(12);
// now lets use the getters and setters even though some are not defined.
echo $person->getName() . ' was born on ' . $person->getDate_of_Birth();
?>
Resulting Output
boolean $DB_DataObject->setFrom (
array or object $from
,
string $format = '%s'
,
bool $skipEmpty
= false
)
Copies items that are in the table definitions from an array or object into the current object (It will not override key values). This can be used to process form posts (if the field names match the database), or cloning similar objects.
You can not set the value of a key column using setFrom, It is silently ignored for security reasons. (you can still assign it manually)
setFrom will attempt to call the setters set{columnname} methods if they exist, It will also call fromValue(), which formats dates correctly.
You may realize that this method overlaps the overloaded method for the column name from, due to this, the associated methods for the column name 'from' are set_from and getFrom.
array or Object $from
- what to copy from
string $format
- the format of the array or object variables
and how they relate to this object.
For example if your input array is in the format prefix_COLNAME,
then you can use 'prefix_%s'.
bool $skipEmpty
- If set to true,
DB_DataObject will not assign empty values
if a column is empty (eg. '' / 0 etc)
array or boolean
- TRUE on success or an array of set*() return values in PHP4.3.2 upwards
This function can not be called statically.
Using setFrom()
<?php
// Person contains name,age
// $_POST contains 'name'=>'fred', 'age'=>'22'
$person = new DataObjects_Person;
$person->get(12);
$person->setFrom($_POST);
$person->update();
// or using the formating
// person contains name,age
// $_POST contains 'person_name'=>'fred', 'person_age'=>'22'
$person = new DataObjects_Person;
$person->get(12);
$ret = $person->setFrom($_POST,'person_%s');
// use the return value from setFrom to test for errors (PHP4.3.2)
if ($ret === true) {
$person->update();
} else {
// $ret is an array or strings..
echo 'There were some errors: ' . implode(', ', $ret);
}
// or copying from another object
$personA = new DataObjects_Person;
$personA->get(12);
$personB = new DataObjects_Person;
$personB->get(14);
$personA->setFrom($personB);
$person->update();
?>
array $DB_DataObject->toArray (
string $format = '%s'
,
bool $hideEmpty
= false
)
Allows the fetching of an associate array (with optional key formating) for use with other packages, like HTML_QuickForm.
From PHP4.2.3RC2 onwards, The values of each column are retrieved using getXXXX() methods so you can change the formating of a row by defining a getter method.
string $format
- a sprintf string eg. 'form[%s]'
bool $hideEmpty
- If set to true, empty elements (no value/null) will not be returned
This function can not be called statically.
getting arrays
<?php
$person = new DataObjects_Person;
$person->get(2);
print_r($person->toArray());
print_r($person->toArray('user[%s]'));
print_r($person->toArray('user[%s]', true));
?>
Sample Output
array $DB_DataObject->validate (
)
Check all the objects variables to see if they are valid, by default this means is a column an integer or string, if you define methods like validateEmail(), in your extended class then it will be called to validate the row called 'email'. This may be useful if called prior to an update or insert.., to generate error messages.
override this to set up your validation rules.
array
- of validation results or TRUE
This function can not be called statically.
the examples below utilize the PEAR validation package
validate usage
<?php
$person = new DataObjects_Person;
$person->get(12);
$person->setFrom($_POST['input']);
$val = $person->validate();
if ($val === TRUE) {
$person->update();
} else {
foreach ($val as $k=>$v) {
if ($v == false) {
echo "There was something wrong with ($k)\n";
} else {
echo "($k) validated OK\n";
}
}
}
?>
validate methods
<?php
/* inside class DataObject_Person */
function validateEmail() {
return Validate::email($this->email, true);
}
function validateHomepage() {
return Validate::url($this->homepage, true);
}
function validateDate() {
return Validate::date($this->date, "%d-%m-%Y", array(01,01,1970), array(01,01,2030));
?>
object $DB_DataObject->tableName (
string $name
)
Without any argument, it returns the table name that the object deals with. With a string, it will set the table name for the instance of the object.
This function can not be called statically.
getting and setting the tablename
<?php
$person = new DataObjects_Person;
echo $person->tableName();
// echo's person
// now use the same object to query the extra_people table
$person->tableName('extra_people');
$person->id = 12;
$person->find(true);
// you can also use this in conjunction with table(), to create dataobjects for random tables..
$d = new DB_DataObject;
$d->tableName('person');
$d->table(array(
'id' => DB_DATAOBJECT_INT,
'name' => DB_DATAOBJECT_STRING,
));
$d->keys(array('id'));
$d->id = 12;
$d->find(true);
// should do the same as above..!
?>
object $DB_DataObject->database (
string $name
)
Without any argument, it returns the database that the object deals with. With a string, it will set the database for the instance of the object.
This function can not be called statically.
Getting the database name
<?php
$person = new DataObjects_Person;
echo $person->database();
// echo's mydatabase
// now use the same object to check on a mirror..
$person->database('mirror1');
$person->id = 12;
$person->find(true);
?>
object $DB_DataObject->table (
array $schema
)
Without any arguments, table() returns the table schema that the object deals with. With an array passed as the first argument, it will set the table schema for the current instance of the object.
The default schema is normally stored in the database.ini file described in the Autobuilding section.
This function can not be called statically.
getting the connection
<?php
$person = new DataObjects_Person;
print_r($person->table());
//
// array(
// 'id' => 1 // == DB_DATAOBJECT_INT
// 'name' => 2 // == DB_DATAOBJECT_STR
// 'bday' => 6 // == DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE
// 'last' => 14 // == DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME
// 'active' => 17 // == DB_DATAOBJECT_INT + DB_DATAOBJECT_BOOL
// 'desc' => 34 // == DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT
// 'photo' => 64 // == DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB
// )
//
// now use it to define a on the fly database table...
$d = new DB_DataObject;
$d->tableName('person');
$d->table(array(
'id' => DB_DATAOBJECT_INT,
'name' => DB_DATAOBJECT_STRING,
));
$d->keys(array('id'));
$d->id = 12;
$d->find(true);
// should do the same as above..!
?>
array $DB_DataObject->keys (
string $keys
...
)
Without any arguments, keys() returns an array of the keys used by the object (the generator builds these and guesses them based on finding things like primary key, unique, or nextval). Calling it with a value, or mulitple values, sets the keys for the current instance of the object.
The default keys are normally stored in the database.ini file described in the Autobuilding section.
This function can not be called statically.
getting the connection
<?php
$person = new DataObjects_Person;
print_r($person->keys());
//
// array(
// 0 => 'id',
// )
//
// now use it to define a on the fly database table...
$d = new DB_DataObject;
$d->tableName('person');
$d->table(array(
'id' => DB_DATAOBJECT_INT,
'name' => DB_DATAOBJECT_STRING,
));
$d->keys('id');
// if you have multiple keys
// $d->keys('id','key2','key2');
$d->id = 12;
$d->find(true);
// should do the same as above..!
?>
object $DB_DataObject->getDatabaseConnection (
)
Fetch the pear database connection that the object uses - so you can find information or send queries directly to it.
This function can not be called statically.
getting the connection
<?php
$person = new DataObjects_Person;
$db= &$person->getDatabaseConnection();
echo $db->phptype;
?>
Sample Output
object $DB_DataObject->getDatabaseResult (
)
Fetch the pear database result object - so you can use it with things like the Pager or HTML_Select classes
This function can not be called statically.
getting the result object
<?php
$person = new DataObjects_Person;
$person->hair = 'green';
$person->selectAdd();
$person->selectAdd('id,name');
$person->find();
$res= $person->getDatabaseResult();
$quickFormSelect->loadDbResult($res,'id','name');
?>
void DB_DataObject::debugLevel (
integer $level
)
Sets and returns debug level. So you can see the queries and connections being built and executed.
integer $level
- level, without any parameters
it will disable the debugging output. 1 give a general output, 5 includes
things like passwords for connections.
This function can not be called statically.
Using debugLevel()
<?php
// turn debugging high
DB_DataObject::debugLevel(5);
$person = new DataObjects_Person;
$person->get(12);
$person->setFrom($_POST['input']);
$person->update();
// turn debugging off
DB_DataObject::debugLevel();
?>
void $DB_DataObject->debug (
string $message
,
string $logPrefix
,
integer $level=1
)
Debugger - you can use this in your extended classes to output debugging information. Uses DB_DataObject::DebugLevel(x) to turn it on, and can be completly turned off by using the production setting in the configuration file
string $message
- message to output
string $logPrefix
- A bold prefix string
integer $level
- output level, 1 is general,
5 tends to reveal things like database connection passwords..
This function can not be called statically.
In production mode, the debugger is disabled
Setting the debugging level
<?php
$person = new DataObjects_Person;
$person->get(12);
// always prints
$person->debug('just got the person, about to set stuff', 'my application',0);
$person->setFrom($_POST['input']);
// only prints if debuglevel is set
$person->debug('just set the variables, about to update', 'my application',1);
$person->update();
?>
object DB_DataObject::raiseError (
int $message
,
resource $type
= null
,
resource $behaviour
= null
)
Default error handling is to create a PEAR::Error, but never return it. If you need to handle errors you should look at setting the PEAR_Error callback this is due to the fact it would wreck havoc on the internal methods!
int $message
-
message
resource $type
-
type
resource $behaviour
-
behaviour (die or continue!);
object [unknown]
-
This function can not be called statically.
This is experimental!, although it is documented, it currently only supports a limited amount of databases (send me fixes if you want it to support your favorite database), and the internal operations/API may change in the future..
DataObjects is a very easy way to work with databases that are focused on numbers and strings. You can also use it on date fields (although you must format your strings correctly), and you can use it with other types by using raw SQL query(), and the string value "null" is automatically converted to NULL in the database.
In an effort to provide a cleaner way to code to the richer database types, the DB_DataObject_Cast object was created. It's purpose is to simply create an object to represent some of the more unusual types. Below is an example of using it to create a few simple types.
Cast Objects can be used in both building queries, and assigning values
Cast Objects for building and assigning values
<?php
// using Cast Objects for building a query.
$person = DB_DataObject::factory('person');
// assign the value of birthday to a Cast object with a date.
$person->birthday = DB_DataObject_Cast::date(2000,12,30);
$person->find();
while ($person->fetch()) {
echo "{$person->name} has a birthday on 30 december 2002<BR>";
}
// use Cast Objects for assigning values.
$person = DB_DataObject::factory('person');
$person->get(12);
// set the persons's birthday to 30 december 2000
$person->birthday = DB_DataObject_Cast::date(2000,12,30);
// now update the database.
$person->update();
?>
As you can see, This component is in it"s infancy, so if you have any feature requests, ideas, please do not hesitate to contact me at alan_k at php dot net.
Blobs are fields which can store large amounts of binary data in the databases.
At present only blobs is only supported in postgres using the bytea type. (please email me with code for other databases.)
Inserting a photo and a big text file
<?php
$person = DB_DataObject::factory('person');
$person->name = 'fred'
// use blob for binary data.
$person->photo = DB_DataObject_Cast::blob(file_get_contents('xxx.jpg'));
// use string for textural data into a blob type.
$person->xmldocs = DB_DataObject_Cast::string(file_get_contents('xxx.xml'));
// now insert into the database.
$person->insert();
?>
Most dates are stored in a database in ISO standard format, this method, allows you to create date types, from either Year,month,day, Human readable day/month/year, or standard iso format year-month-day. It fills in the remaining values based on simple rules.
Inserting a date in various formats
<?php
$person = DB_DataObject::factory('person');
$person->name = 'fred'
// use a human readable date
// full format
$person->birthday = new DB_DataObject_Cast::date('21/12/2003');
// use only a month/year - actually sets to 1 december 2003
$person->expires = DB_DataObject_Cast::date('12/2003');
// use only a year only - actually sets to 1 jan 2003
$person->expires = DB_DataObject_Cast::date(2003);
// use a iso formated
// full formated
$person->birthday = DB_DataObject_Cast::date('2003-12-21');
// use only a year-month - actually sets to 1 december 2003
$person->expires = DB_DataObject_Cast::date('2003-12');
// using array syntax
// full formated
$person->birthday = DB_DataObject_Cast::date(2003,12,21);
// use only a year-month - actually sets to 1 december 2003
$person->expires = DB_DataObject_Cast::date(2003,12);
// the real values are stored in object variables
echo $person->birthday->year; // prints 2003
echo $person->birthday->month; // prints 12
echo $person->birthday->day; // prints 21
// you can do simple date addition (similar to mktime)
$d = DB_DataObject_Cast::date('01/12/2003');
$nextMonth = DB_DataObject_Cast::date($d->year,$d->month+1,1);
?>
Some types are sql specific, or may even be database specific, you can use the sql type to put raw strings as part of the sql statement.
using raw sql
<?php
$person = DB_DataObject::factory('person');
$person->get(12);
// set the birthday to null.
$person->birthday = DB_DataObject_Cast::sql('NULL');
// do a sql cast statement (postgres specific)
$data = DB_DataObject_Cast::sql('cast("123123",datetime)');
// now insert into the database.
$person->insert();
?>
A package for building HTML forms from DataObject classes.
DB_DataObject_FormBuilder will aid you in rapid application development using the packages DB_DataObject and HTML_QuickForm. For having a quick but working prototype of your application, simply model the database, run DataObject createTable script over it and write a script that passes one of the resulting objects to the FormBuilder class. The FormBuilder will automatically generate a simple but working HTML_QuickForm object that you can use to test your application. It also provides a processing method that will automatically detect if an insert() or update( )command has to be executed after the form has been submitted. If you have set up DataObject links.ini file correctly, it will also automatically detect if a table field is a foreign key and will populate a selectbox with the linked tables entries. There are many optional parameters that you can place in your DataObjects.ini or in the properties of your derived classes, that you can use to fine-tune the form-generation, gradually turning the prototypes into fully-featured forms, and you can take control at any stage of the process. Basic usage:
<?php
$do =& new MyDataObject();
// Insert "$do->get($some_id);" here to edit an
// existing object instead of making a new one
$fg =& DB_DataObject_FormBuilder::create($do);
$form =& $fg->getForm();
if ($form->validate()) {
$form->process(array(&$fg,'processForm'), false);
$form->freeze();
}
$form->display();
?>
With this package, one can easily create trees with infinite depth inside a relational database.
With this package, one can easily create trees with infinite depth inside a relational database. The package provides a way to
Creates some root and subnodes
In this example, one rootnode and two subnodes are created, saved to the database and displayed.
<?php
require_once 'DB/NestedSet.php';
require_once 'DB/NestedSet/Output.php';
require_once 'HTML/Menu.php';
$DatabasePointer = mysql_connect("localhost", "user", "pwd");
mysql_select_db("database", $DatabasePointer);
$dsn = 'mysql://user:pwd@localhost/database';
// needed colums in table:
$params = array(
'id' => 'id',
'parent_id' => 'rootid',
'left_id' => 'l',
'right_id' => 'r',
'order_num' => 'norder',
'level' => 'level',
'name' => 'name',
);
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->setAttr(array(
'node_table' => 'nested_set',
'lock_table' => 'nested_set_locks',
'secondarySort' => 'name',
)
);
$parent = $nestedSet->createRootNode(array('name' =>'root 1'), false, true);
$nestedSet->createSubNode($parent, array('name' => 'node 1.1'));
$nestedSet->createSubNode($parent, array('name' =>'node 1.2'));
$data = $nestedSet->getAllNodes(true);
foreach ($data as $id => $node) {
$data[$id]['url'] = 'index.php?nodeID=' . $node['id'];
}
$params = array(
'structure' => $data,
'titleField' => 'name',
'urlField' => 'url');
$output =& DB_NestedSet_Output::factory($params, 'Menu');
$structure = $output->returnStructure();
$menu = & new HTML_Menu($structure, 'sitemap');
$menu->forceCurrentUrl($currentUrl);
$menu->show();
?>
string
DB_NestedSet::addListener
(
string
$event
,
&$listener
,
string
$listener
)
Adds an event listener and returns an ID for it
Known events are
nodeCreate
nodeDelete
nodeUpdate
nodeCopy
nodeLoad
$event
The event name
$listener
The listener object
throws no exceptions thrown
This function can not be called statically.
Add Listener
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->AddListener("nodeCreate", "listener");
?>
void
DB_NestedSet::apiVersion
(
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
Get API-version
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->apiVersion();
?>
bool
DB_NestedSet::convertTreeModel
(
&$orig
,
&$copy
,
integer
$_parent
= false
,
object
$
)
This will convert the tree into a format needed for some new features in 1.3. Your <1.3 tree will still work without converting but some new features like preorder sorting won't work as expected.
Usage:
- Create a new node table (tb_nodes2) from the current node table (tb_nodes1) (only copy the structure).
- Create a nested set instance of the 'old' set (NeSe1) and one of the new set (NeSe2)
- Now you have 2 identical objects where only node_table differs
- Call DB_NestedSet::convertTreeModel(&$orig, &$copy);
- After that you have a cleaned up copy of tb_nodes1 inside tb_nodes2
&$orig
&$copy
$_parent
ID of the parent node (private)
$copy
Object where the new tree is copied to
returns True uns success
throws no exceptions thrown
This function can not be called statically.
mixed
DB_NestedSet::createLeftNode
(
int
$id
,
array
$values
,
bool
$returnID
)
+-- root1
|
+-\ root2
| |
| |-- subnode2 [new]
| |-- subnode1 [target]
| |-- subnode3
|
+-- root3
$id
Target node ID
$values
Hash with param => value pairs of the node (see $this->params)
$returnID
Tell the method to return a node id instead of an object. ATTENTION: That the method defaults to return an object instead of the node id has been overseen and is basically a bug. We have to keep this to maintain BC. You will have to set $returnID to TRUE to make it behave like the other creation methods. This flaw will get fixed with the next major version.
returns The node id or false on error
throws no exceptions thrown
This function can not be called statically.
Create left node
Firstly, we connect to NestedSet using Factory. Then we create some nodes and finally we create a LeftNode. The LeftNode will be placed leftsided to the first root-node.
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$nestedSet->createSubNode($parent, array('name' => 'sub1'));
$nestedSet->createSubNode($parent, array('name' => 'sub2'));
$nestedSet->createLeftNode($parent, array('name' => 'left node'), true);
?>
mixed
DB_NestedSet::createRightNode
(
int
$id
,
array
$values
,
bool
$returnID
)
+-- root1
|
+-\ root2
| |
| |-- subnode1 [target]
| |-- subnode2 [new]
| |-- subnode3
|
+-- root3
$id
Target node ID
$values
Hash with param => value pairs of the node (see $this->params)
$returnID
Tell the method to return a node id instead of an object. ATTENTION: That the method defaults to return an object instead of the node id has been overseen and is basically a bug. We have to keep this to maintain BC. You will have to set $returnID to TRUE to make it behave like the other creation methods. This flaw will get fixed with the next major version.
returns The node id or false on error
throws no exceptions thrown
This function can not be called statically.
Create right node
Firstly, we connect to NestedSet using Factory. Then we create some nodes and finally we create a RightNode. The RightNode will be placed rightsided to the first root-node.
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$nestedSet->createSubNode($parent, array('name' => 'sub1'));
$nestedSet->createSubNode($parent, array('name' => 'sub2'));
$nestedSet->createRightNode($parent, array('name' => 'right node'), true);
?>
mixed
DB_NestedSet::createRootNode
(
array
$values
,
integer
$id
= false
,
bool
$first
= false
,
string
$pos = NESE_MOVE_AFTER
)
Optionally it deletes the whole tree and creates one initial rootnode
+-- root1 [target]
|
+-- root2 [new]
|
+-- root3
$values
Hash with param => value pairs of the node (see $this->params)
$id
ID of target node (the rootnode after which the node should be inserted)
$first
Danger: Deletes and (re)inits the whole tree - sequences are reset
$pos
The position in which to insert the new node.
returns The node id or false on error
throws no exceptions thrown
This function can not be called statically.
Create rootnodes
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->createRootNode(array('name' => 'rootnode'), false, true);
?>
mixed
DB_NestedSet::createSubNode
(
integer
$id
,
array
$values
)
+-- root1
|
+-\ root2 [target]
| |
| |-- subnode1 [new]
|
+-- root3
$id
Parent node ID
$values
Hash with param => value pairs of the node (see $this->params)
returns The node id or false on error
throws no exceptions thrown
This function can not be called statically.
Create subnodes
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'rootnode'), false, true);
$nestedSet->createSubNode($parent, array('name' => "node"));
?>
bool
DB_NestedSet::deleteNode
(
int
$id
)
This package is not documented yet.
$id
ID of the node to be deleted
returns True if the delete succeeds
throws no exceptions thrown
This function can not be called statically.
Delete nodes
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$nestedSet->deleteNode($id);
?>
object The&
DB_NestedSet::factory
(
string
$driver
,
string
$dsn
,
array
$params = array()
)
If the class given by $driver allready exists it will be used. If not the driver will be searched inside the default path ./NestedSet/
$driver
The driver, such as DB or MDB
$dsn
The dsn for connecting to the database
$params
The field name params for the node table
returns DB_NestedSet object
throws no exceptions thrown
This function can not be called statically.
Factory
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
?>
mixed
DB_NestedSet::getAllNodes
(
bool
$keepAsArray
= false
,
bool
$aliasFields
= true
,
array
$addSQL = array()
)
This package is not documented yet.
$keepAsArray
(optional) Keep the result as an array or transform it into a set of DB_NestedSet_Node objects?
$aliasFields
(optional) Should we alias the fields so they are the names of the parameter keys, or leave them as is?
$addSQL
(optional) Array of additional params to pass to the query.
returns False on error, or an array of nodes
throws no exceptions thrown
This function can not be called statically.
Get all nodes
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$nestedSet->createSubNode($parent, array('name' => 'sub1');
$nestedSet->createSubNode($parent, array('name' => 'sub2');
$data = $nestedSet->getAllNodes(true);
?>
mixed
DB_NestedSet::getBranch
(
int
$id
,
bool
$keepAsArray
= false
,
bool
$aliasFields
= true
,
array
$addSQL = array()
)
This package is not documented yet.
$id
The node ID
$keepAsArray
(optional) Keep the result as an array or transform it into a set of DB_NestedSet_Node objects?
$aliasFields
(optional) Should we alias the fields so they are the names of the parameter keys, or leave them as is?
$addSQL
(optional) Array of additional params to pass to the query.
returns False on error, or an array of nodes
see _addSQL
throws no exceptions thrown
This function can not be called statically.
Get Branch
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$parent2 = $nestedSet->createSubNode($parent, array('name' => 'sub-node));
$nestedSet->createSubNode($parent2, array('name' => 'sub1'));
$nestedSet->createSubNode($parent2, array('name' => 'sub2'));
$data = getBranch($parent2);
?>
mixed
DB_NestedSet::getChildren
(
int
$id
,
bool
$keepAsArray
= false
,
bool
$aliasFields
= true
,
bool
$forceNorder
= false
,
array
$addSQL = array()
)
This package is not documented yet.
$id
The node ID
$keepAsArray
(optional) Keep the result as an array or transform it into a set of DB_NestedSet_Node objects?
$aliasFields
(optional) Should we alias the fields so they are the names of the parameter keys, or leave them as is?
$forceNorder
(optional) Force the result to be ordered by the norder param (as opposed to the value of secondary sort). Used by the move and add methods.
$addSQL
(optional) Array of additional params to pass to the query.
returns False on error, or an array of nodes
see _addSQL
throws no exceptions thrown
This function can not be called statically.
Get Children
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$parent2 = $nestedSet->createSubNode($parent, array('name' => 'sub-node));
$nestedSet->createSubNode($parent2, array('name' => 'sub1'));
$nestedSet->createSubNode($parent2, array('name' => 'sub2'));
$data = $nestedSet->getChildren($parent2);
?>
mixed
DB_NestedSet::getParent
(
int
$id
,
bool
$keepAsArray
= false
,
bool
$aliasFields
= true
,
array
$addSQL = array()
,
$useDB
= true
)
This package is not documented yet.
$id
The node ID
$keepAsArray
(optional) Keep the result as an array or transform it into a set of DB_NestedSet_Node objects?
$aliasFields
(optional) Should we alias the fields so they are the names of the parameter keys, or leave them as is?
$addSQL
(optional) Array of additional params to pass to the query.
$useDB
returns False on error, or the parent node
see _addSQL
throws no exceptions thrown
This function can not be called statically.
Get Parent
Fetch the immediate parent of a node given by id. GetParent will return $parent2 as $data.
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$parent2 = $nestedSet->createSubNode($parent, array('name' => 'sub-node));
$nestedSet->createSubNode($parent2, array('name' => 'sub1'));
$nestedSet->createSubNode($parent2, array('name' => 'sub2'));
$data = $nestedSet->getParent($parent2);
?>
mixed
DB_NestedSet::getParents
(
int
$id
,
bool
$keepAsArray
= false
,
bool
$aliasFields
= true
,
array
$addSQL = array()
)
This package is not documented yet.
$id
The node ID
$keepAsArray
(optional) Keep the result as an array or transform it into a set of DB_NestedSet_Node objects?
$aliasFields
(optional) Should we alias the fields so they are the names of the parameter keys, or leave them as is?
$addSQL
(optional) Array of additional params to pass to the query.
returns False on error, or an array of nodes
see _addSQL
throws no exceptions thrown
This function can not be called statically.
Get Parents
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$parent2 = $nestedSet->createSubNode($parent, array('name' => 'sub-node));
$parent3 = $nestedSet->createSubNode($parent2, array('name' => 'sub-node'));
$nestedSet->createSubNode($parent3, array('name' => 'sub1'));
$nestedSet->createSubNode($parent3, array('name' => 'sub2'));
$data = $nestedSet->getParents($parent3);
?>
mixed
DB_NestedSet::getRootNodes
(
bool
$keepAsArray
= false
,
bool
$aliasFields
= true
,
array
$addSQL = array()
)
This package is not documented yet.
$keepAsArray
(optional) Keep the result as an array or transform it into a set of DB_NestedSet_Node objects?
$aliasFields
(optional) Should we alias the fields so they are the names of the parameter keys, or leave them as is?
$addSQL
(optional) Array of additional params to pass to the query.
returns False on error, or an array of nodes
see _addSQL
throws no exceptions thrown
This function can not be called statically.
Get Rootnodes
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$nestedSet->createSubNode($parent, array('name' => 'sub1'));
$parent2 = $nestedSet->createRootNode($parent, array('name' => 'sub-node), '1', false);
$nestedSet->createSubNode($parent2, array('name' => 'sub2'));
$data = $nestedSet->getRootNodes();
?>
mixed
DB_NestedSet::getSiblings
(
int
$id
,
bool
$keepAsArray
= false
,
bool
$aliasFields
= true
,
array
$addSQL = array()
)
This package is not documented yet.
$id
The node ID
$keepAsArray
(optional) Keep the result as an array or transform it into a set of DB_NestedSet_Node objects?
$aliasFields
(optional) Should we alias the fields so they are the names of the parameter keys, or leave them as is?
$addSQL
(optional) Array of additional params to pass to the query.
returns False on error, or the parent node
see _addSQL
throws no exceptions thrown
This function can not be called statically.
Get Siblings
<?php
require_once 'DB/NestedSet.php';
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$node = $nestedSet->createSubNode($parent, array('name' => 'sub1'));
$nestedSet->createSubNode($parent, array('name' => 'sub2'));
$nestedSet->createSubNode($parent, array('name' => 'sub3'));
$data = $nestedSet->getSiblings($node);
?>
mixed
DB_NestedSet::getSubBranch
(
string
$id
,
bool
$keepAsArray
= false
,
bool
$aliasFields
= true
,
array
$addSQL = array()
)
getChildren only queries the immediate children getSubBranch returns all nodes below the given node
$id
The node ID
$keepAsArray
(optional) Keep the result as an array or transform it into a set of DB_NestedSet_Node objects?
$aliasFields
(optional) Should we alias the fields so they are the names of the parameter keys, or leave them as is?
$addSQL
(optional) Array of additional params to pass to the query.
returns False on error, or an array of nodes
see _addSQL
throws no exceptions thrown
This function can not be called statically.
Get SubBranch
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$parent2 = $nestedSet->createSubNode($parent, array('name' => 'sub-node));
$parent3 = $nestedSet->createSubNode($parent2, array('name' => 'sub-node));
$nestedSet->createSubNode($parent3, array('name' => 'sub1'));
$nestedSet->createSubNode($parent3, array('name' => 'sub2'));
$data = $nestedSet->getSubBranch($parent2);
?>
bool
DB_NestedSet::isParent
(
mixed
$parent
,
mixed
$child
)
A node is considered to be a parent if it resides above the child So it doesn't mean that the node has to be an immediate parent. To get this information simply compare the levels of the two nodes after you know that you have a parent relation.
$parent
The parent node as array or object
$child
The child node as array or object
returns True if it's a parent
throws no exceptions thrown
This function can not be called statically.
IsParent
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'));
$node = $nestedSet->createSubNode($parent, array('name' => 'sub1'));
$boolparent = $nestedSet->isParent($parent, $node);
?>
int
DB_NestedSet::moveTree
(
int
$id
,
$targetid
,
constant
$pos
,
bool
$copy
= false
,
int
$target
)
This package is not documented yet.
$id
Source ID
$targetid
Target ID
$pos
Position (use one of the NESE_MOVE_* constants)
$copy
Shall we create a copy
$target
Target ID
returns ID of the moved node or false on error
see _moveInsideLevel
see _moveRoot2Root
see _moveAcross
throws no exceptions thrown
This function can not be called statically.
Get SubBranch
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$parent2 = $nestedSet->createSubNode($parent, array('name' => 'sub-node));
$parent3 = $nestedSet->createSubNode($parent2, array('name' => 'sub-node));
$nestedSet->createSubNode($parent3, array('name' => 'sub1'));
$node = $nestedSet->createSubNode($parent3, array('name' => 'sub2'));
$nestedSet->moveTree($node, $parent, NESE_MOVE_AFTER);
?>
mixed
DB_NestedSet::pickNode
(
int
$id
,
bool
$keepAsArray
= false
,
bool
$aliasFields
= true
,
string
$idfield = 'id'
,
array
$addSQL = array()
)
This package is not documented yet.
$id
The node id of the node to fetch
$keepAsArray
(optional) Keep the result as an array or transform it into a set of DB_NestedSet_Node objects?
$aliasFields
(optional) Should we alias the fields so they are the names of the parameter keys, or leave them as is?
$idfield
(optional) Which field has to be compared with $id? This is can be used to pick a node by other values (e.g. its name).
$addSQL
(optional) Array of additional params to pass to the query.
returns False on error, or an array of nodes
see _addSQL
throws no exceptions thrown
This function can not be called statically.
Pick Node
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$parent = $nestedSet->createRootNode(array('name' => 'root-node'), false, true);
$nestedSet->createSubNode($parent, array('name' => 'sub1'));
$data = $nestedSet->getBranch($id);
?>
bool
DB_NestedSet::removeListener
(
string
$event
,
string
$listenerID
)
Removes the event listener with the given ID
$event
The ivent name
$listenerID
The listener's ID
throws no exceptions thrown
This function can not be called statically.
Remove Listener
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->AddListener("nodeCreate", "listener");
$nestedSet->RemoveListener("nodeCreate", "listener");
?>
bool
DB_NestedSet::setAttr
(
array
$attr
)
This package is not documented yet.
$attr
An associative array with attributes
throws no exceptions thrown
This function can not be called statically.
Set attributes
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->setAttr(array(
'node_table' => 'nested_set',
'lock_table' => 'nested_set_locks',
'secondarySort' => 'name'
)
);
?>
void
DB_NestedSet::setDbOption
(
$option
,
$val
)
This package is not documented yet.
$option
$val
throws no exceptions thrown
This function can not be called statically.
Current
DB_NestedSet::setsortMode
(
constant
$sortMode
= false
)
This package is not documented yet.
$sortMode
returns sortMode
throws no exceptions thrown
This function can not be called statically.
void
DB_NestedSet::testLock
(
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
Test lock
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->testLock();
?>
bool
DB_NestedSet::triggerEvent
(
string
$event
,
&$node
,
array
$eparams
= false
,
object
$
)
This package is not documented yet.
$event
The Event that occured
&$node
Node
$eparams
A associative array of params which may be needed by the handler
$node
A Reference to the node object which was subject to changes
throws no exceptions thrown
This function can not be called statically.
Trigger Event
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->AddListener("nodeCreate", "listener");
$parent = $nestedSet->createRootNode(array('name' => 'rootnode'), false, true);
$node = $nestedSet->createSubNode($parent, array('name' => "node"));
$nestedSet->TriggerEvent("nodeDelete", $node);
?>
bool
DB_NestedSet::updateNode
(
int
$id
,
array
$values
,
$_internal
= false
,
bool
$_intermal
)
This package is not documented yet.
$id
Node ID
$values
Hash with param => value pairs of the node (see $this->params)
$_internal
Internal use only.
$_intermal
Internal use only. Used to skip value validation. Leave this as it is.
returns True if the update is successful
throws no exceptions thrown
This function can not be called statically.
Update nodes
<?php
require_once('DB/NestedSet.php');
$nestedSet =& DB_NestedSet::factory('DB', $dsn, $params);
$nestedSet->createSubNode($id, array('name' => "rootnode"));
$nestedSet->updateNode($id, array('name' => "new name"));
?>
DB_Pager handles all the stuff needed for displaying paginated results from a db query of PEAR::DB, including fetching only the needed rows and giving extensive information for helping build an HTML or GTK query result display.
NB: This package is deprecated in favour of the Pager package. In particular, have a look at the Pager_Wrapper example included with Pager, and read this paragraph or this tutorial.
This package is an OO-abstraction to the SQL-Query language, it provides methods such as setWhere, setOrder, setGroup, setJoin, etc. to easily build queries.
This package has been superseded. Please use MDB_QueryTool for new projects.
This package is an OO-abstraction to the SQL-Query language, it provides methods such as setWhere, setOrder, setGroup, setJoin, etc. to easily build queries. It also provides an easy to learn interface that interacts nicely with HTML-forms using arrays that contain the column data, that shall be updated/added in a database. This package bases on an SQL-Builder which lets you easily build SQL-Statements and execute them.
You can find some useful usage examples and docs in the MDB_QueryTool documentation. Everything there holds true for DB_QueryTool too, except for the class name, of course.
An object-oriented interface to a database
The DB_Table package provides an object oriented interface to a database. It contains three main classes:
The package grew around the DB_Table class, which was written by Paul M. Jones. The Database and Generator class were added in releases 1.5.0RC1 and 1.5.0RC2, respectively. These manual pages document release 1.5.0.
The properties of the core DB_Table class contain the schema for a table, defined using portable data types. The class methods provides a convenient API for constructing and submitting INSERT, UPDATE, DELETE, and SELECT SQL commands, and for creation of a RDBMS database table from the defined schema. Simple data type validation is provided for data to be inserted or updated. The class provides methods to automatically generate HTML_QuickForm input forms that match the column definitions.
Each instance of DB_Table_Database contains a model of relationships between tables in a database, in which each table is represented by an instance of DB_Table. The autoJoin() method can automatically construct join conditions for queries that join any number of tables. The class also provides optional PHP validation of foreign key validity, and optional PHP emulation of actions triggered on delete or update of referenced rows, such as cascading deletes.
The DB_Table_Generator class may be used to automatically generate the PHP code necessary to use the DB_Table package as an interface to an existing database. It is used (if at all) only during set-up of the interface for a database.
Each of these three classes is discussed in a separate tutorial in the following manual pages.
This tutorial uses an extended example to introduce the use of the DB_Table class as an interface to a single database table. This class provides:
This class tutorial contains two manual pages: This page documents all of the features of DB_Table except those involving HTML_Quick form generation, which are discussed in the next page. The tutorial is based closely upon the original external documentation for DB_Table, by Paul M. Jones, which is still available here. The original documentation includes some examples of how to customize a DB_Table subclass definition that are not included here.
The DB_Table and DB_Table_Database classes both extend an abstract base class named DB_Table_Base. Methods and properties that are inherited from DB_Table_Base are thus available via either a DB_Table or a DB_Table_Database object, with the same interface. These shared methods and properties will be identified as such in these manual pages.
You generally do not usually create instances of DB_Table directly. Instead, for each table in a database, you define a a subclass of DB_Table, and instantiate one object of that subclass. The table schema is embedded in the properties of that object: column definitions are declared in the $col property array and indices in the $idx property array. Normally, the values of these property arrays are defined as part of the subclass definition, which thus serves as a record of the table schema.
One advantage of any database gateway that associates a class with each RDBMS table is that the subclass definition provides a natural place to specify properties and behaviors that are specific to that table. Custom behaviors and validations may be implemented by overriding the class methods, or by defining new methods. DB_Table also allows commonly used or baseline SELECT queries to be stored in the $sql property array.
The package provides two ways to create both a RDBMS table and a corresponding DB_Table subclass, without writing redundant specifications:
In this tutorial, we demonstrate the former method, which requires us to write subclass definitions manually. A discussion of the latter method is given in the DB_Table_Generator class tutorial.
The $col property is an associative array in which each key is a column name, and each value is an associative array containing a column definition. The value of the required 'type' element of a column definition must be the name of one of the DB_Table data types, e.g., 'integer', 'decimal', 'boolean', etc. The 'char' and 'varchar' string types, and the 'decimal' numerical type, all require a 'size' element, for which the value is an integer number of characters or digits. The 'decimal' type also requires a 'scope' element, which is the number of digits after the decimal point. A 'require' element with a boolean true value is equivalent to NOT NULL in SQL, and indicates that NULL values are not allowed in that column.
As an example, let's say we're going to create a GuestBook table. This table will store the first and last name of visitors, their email address, and the date and time they signed the guestbook. In addition, we're going to want a unique ID for each row. The columns in our table will be:
These column definitions must be declared in the $col property array. Shown below is the required declaration of $col for a subclass of DB_Table, named GuestBook_Table, that is associated with a database table named GuestBook:
GuestBook column definitions
<?php
class GuestBook_Table extends DB_Table
{
var $col = array(
// unique row ID
'id' => array(
'type' => 'integer',
'require' => true
),
// first name
'fname' => array(
'type' => 'varchar',
'size' => 32
),
// last name
'lname' => array(
'type' => 'varchar',
'size' => 64
),
// email address
'email' => array(
'type' => 'varchar',
'size' => 128,
'require' => true
),
// date signed
'signdate' => array(
'type' => 'date',
'require' => true)
);
?>
The $idx property array is used to declare indices. Each index can be declared to be of type 'primary', 'unique', or 'normal'. Primary and unique indices both define constraints that require that each key value be unique within the table. Normal indices are introduced purely for efficiency. Both single- and multi-column indices may be defined.
In our GuestBook example, we want two single column indices: First, we will define a primary index for the "id" identifier column. Second, for purposes of the example, we'll define a normal index on the "signdate" column, because we expect to search for visitors by date and time. The required $idx property declaration is given below:
Index Definitions for GuestBook
<?php
class GuestBook_Table extends DB_Table
{
// Column definitions - see above
// var $col = array(...);
// Index definitions
var $idx = array(
'id' => array(
'type' => 'primary',
'cols' => 'id'
),
'signdate' => array(
'type' => 'normal',
'cols' => 'signdate'
)
);
}
?>
The key of each element in the $idx array is an index name, and the value is generally an array containing a definition for the index. An index definition array must contain a 'type' element, for which the value must be 'primary', 'unique' or 'normal', and 'cols' element. The value of 'cols' element must be either the name of a single column, for a single-column array, or a sequential array of column names, for a multi-column index. There is also an alternative shorthand syntax for single-column indices, which is discussed below.
As an example of a multi-column index, here is the definition for a multi-column index named "namefl" on the fname and lname columns.
Defining A Multi-Column Index
<?php
class GuestBook_Table extends DB_Table
{
// Column definitions
// var $col = array( ... )
// Index definitions
var $idx = array(
// above single-column indices, plus
'namefl' = array(
'type' => 'normal',
'cols' => array('fname', 'lname')
)
);
}
?>
There is also a shorthand way to declare a single-column index: If you want single-column index for which the index name is the same as the column name, use the column name as the key of $idx, and the index type string (rather than an index definition array) as the value.
Shorthand for Single-Column Indices:
<?php
class GuestBook_Table extends DB_Table
{
// Column definitions
// var $col = array(...);
// Index definitions
var $idx = array(
// primary index called 'id' based on the 'id' column
'id' => 'primary',
// normal index called 'signdate' based on the 'signdate' column
'signdate' => 'normal'
);
}
?>
One integer column of each table may be declared to be an auto-increment column. This column is normally a primary key identifier. The only effect of this declaration is to change the behavior of the insert() method: If the insert() method is used to insert a row in which the value of an auto-increment column is not set or NULL, then an auto-incremented integer will be inserted automatically.
A column is declared to be auto-increment by setting the value of the $auto_inc_col property to the column name. In the following example, we declare the "id" primary key column of the GuestBook table to be auto-increment:
An Auto-Incrementing Identifier
<?php
class GuestBook_Table extends DB_Table
{
// Column definitions
// var $col = array(...);
// Index definitions
var $idx = array(...);
// Auto-increment declaration
var $auto_inc_col = 'id';
}
?>
Each DB_Table object wraps a DB or MDB2 database connection object. This object is passed to the DB_Table constructor as its first parameter. A single DB/MDB2 object may be shared by any number of DB_Table objects. The second parameter of the constructor is the name of the associated RDBMS table. The optional third parameter is a string $create that can be used to specify whether the table should be created by the constructor if it does not already exist, or whether its structure should be verified if it does (as discussed in more detail below).
In the example shown below, an instance of the GuestBook subclass is created, which binds to the GuestBook table. The use of a value $auto_create = 'safe' specifies that the GuestBook table should be created if a table of that name does not already exist, but not otherwise.
Constructing the GuestBook_Table Object
<?php
// Include basic classes
require_once 'MDB2.php';
require_once 'DB/Table.php';
require_once 'Guestboook_Table.php';
// create a PEAR MDB2 (or DB) object
$dsn = "phptype://username:password@localhost/database";
$conn = MDB2::connect($dsn);
// set up for the GuestBook and create it
$table = 'GuestBook';
$create = 'safe';
$GuestBook =& new GuestBook_Table($conn, $table, $create);
// print out results
if ($GuestBook->error) {
echo "Failure! Try again.";
print_r($GuestBook->error);
} else {
echo "Success!";
print_r($GuestBook);
}
?>
The $conn connection object may be either a DB or MDB2 object, and is passed by reference.
The allowed values of the $create argument are:
The DB_Table insert(), update(), and delete() methods provide a convenient interface for inserting, updating, and deleting rows of data. A row of data to be inserted or updated is passed to the insert or update method as an associative array in which the keys are column names.
To insert a row into the GuestBook table, you use the insert() method. The only parameter is an associative array in which the keys are column names, and the values are column values to be inserted.
Inserting a Row
<?php
// [snip] create the $GuestBook object
// assign fields and values
$row = array(
'fname' => 'Thomas',
'lname' => 'Anderson',
'signdate' => '2003-10-12',
'email' => 'neo@matrix.net'
);
// insert into the table and print results
$result = $GuestBook->insert($row);
if (PEAR::isError($result)) {
// Error handling code
}
?>
By default, the insert method will automatically validate that the data you are inserting is of the expected type, and that values have been provided for all required columns other than auto-increment columns. If either of these validations fails, the method returns a PEAR Error, and does not attempt to insert a row. In the above example, no value has been provided for the required 'id' column because this is an auto-increment column, as discussed below.
DB_Table can use sequences created by the underlying DB or MDB2 layer to generate auto-increment integer identifiers. If the value of a column that has been declared to be auto-increment is not set or set to PHP NULL in the array of values that is passed to the insert method, then an auto-incremented sequence value will be generated and inserted for that column. In the above example, a sequence value is generated and inserted for the 'id' column. If an integer value is set for such a column, however, the provided value will instead be inserted.
The sequence generation features of DB or MDB2 may also be accessed explictly via the DB_Table::nextID() method. This method is simply a wrapper that calls the corresponding DB or MDB2 method internally. The following example shows how to use nextID() to explicitly generate and insert an auto-incremented value for an identifier.
Using nextID() to access a sequence
<?php
// [snip] create the $GuestBook object
// get the next ID in a sequence associated with the table
$id = $GuestBook->nextID();
// assign fields and values
$row = array(
'id' => $id,
'fname' => 'Thomas',
'lname' => 'Anderson',
'signdate' => '2003-10-12',
'email' => 'neo@matrix.net'
);
// insert into the table and print results
$result = $GuestBook->insert($row);
if (PEAR::isError($result)) {
// ... error handling code ...
}
?>
Because the 'id' column has been declared to be auto-increment, this code snippet is functionally equivalent to that given in the preceding example.
The update() method is used to update the data in a row or a set of rows. The method takes two parameters. Its first parameter is an associative array in which keys are names of columns to be updated, and values are new data values. The second is the text of the WHERE clause of the corresponding SQL UPDATE statement, excluding the WHERE keyword.
For example to change all the rows with the last name "Smith" to "Jones":
Updating a Set of Rows
<?php
// [snip] create the $GuestBook object
$values = array(
'lname' => 'Jones'
);
// assign the WHERE clause
$where = "lname = 'Smith'";
// attempt the update
$result = $GuestBook->update($values, $where);
if (PEAR::isError($result)) {
// ... error handling code ...
}
?>
As for insertion, if you attempt to update with values that do not validate against the declared column types, then the update will fail, and the method will return a PEAR Error.
The delete() method is used to delete a row or set of rows specified by a logical condition. Its only parameter is a string containing the logical condition of the WHERE clause needed to identify which rows to delete, excluding the WHERE keyword.
For example, to delete all rows that were entered before today:
Deleting a Set of Rows
<?php
// [snip] create the $GuestBook object
// a WHERE clause
$today = date('Y-m-d'); // formatted as yyyy-mm-dd
$where = "signdate < '$today'";
// attempt the update and print the results
$result = $GuestBook->delete($where);
if (PEAR::isError($result)) {
// ... error handling code ...
}
?>
DB_Table uses an array syntax for constructing SQL SELECT statements, in which each clause of an SQL SELECT statement (e.g., SELECT, FROM, WHERE clauses) is stored in a different element. This representation makes it relatively easy to modify a query by, e.g., adding additional conditions to the WHERE clause to further limit the returned set of rows. Query arrays may be stored in the $sql property array.
Queries may be submitted to the database using either the select() method, which generally returns the result set as an array, or the selectResult() method, which returns a DB_Result or MDB2_Result_Common result set object. These three methods are all inherited from the DB_Table_Base base class, and thus are also available as methods of a DB_Table_Database object.
Each SQL select statement is defined by a "query array". For most of the allowed elements of a query array, the key is a lower case version of the keyword that begins a clause in the SELECT statement (e.g., 'select', 'from', 'where', etc.), and the value is the text of the remainder of that clause, excluding the keyword. The allowed keys of a query array that define clauses of the SELECT command are:
The values of the 'select', 'from', 'where', 'group', 'having', and 'order' elements are strings that contain the text of corresponding clause of the desired SQL statement, excluding the keywords SELECT, FROM, WHERE, GROUP BY, HAVING, or ORDER, respectively. The value of the 'join' element, if present, is simply concatenated onto the end of the 'FROM' element, and should include the keywords, e.g., 'JOIN', 'INNER JOIN', or 'LEFT JOIN'.
The other allowed keys of a query array may be used to specify how the results of query should be returned by the select() method. These are:
These three elements effect only the behavior of the select() method, and have no effect upon the behavior of the selectResult() method. The allowed values of the 'get' element are:
Each allowed value of the 'get' element corresponds to the name of the get* method of DB and MDB2 that is actually called internally by DB_Table::select(): If the value of the 'get' element is 'all', the query is submitted by calling the DB/MDB2::getAll() method, using an SQL query constructed from the query array, and DB_Table::select() simply returns the return value of getAll(). Similarly, a 'get' element value of 'assoc' causes a call to getAssoc(), 'col' calls getCol(), 'one' calls getOne(), and 'row' calls getRow().
When the 'get' element is set to 'all', explicitly or by default, each row in the array returned by the DB_Table::select() method returned as an associative array in which keys are column names, as a sequential array, or as an object in which column names map to property names. The choice of data structure for each row may be controlled by the 'fetchmode' element of the query or, if this is not is set, by the $fetchmode property of the DB_Table object. Similarly, when rows are returned as objects, the name of the object class may be specified by the 'fetchmode_object_class' element of the query or, if this is not set, by the $fetchmode_object_class property of the DB_Table object.
The values of the 'fetchmode' and 'fetchmode_object_class' elements of the query or the $fetchmode and $fetchmode_object_class properties of DB_Table are used to temporarily reset the values of the 'fetchmode' and 'fetchmode_object_class' properties (for DB) or options (for MDB2) of the underlying DB or MDB2 object. These values should be set equal to the desired DB_FETCHMODE_* or MDB2_FETCHMODE_* constant appropriate for the underlying backend. The original values of the DB/MDB2 object properties or options are restored before the select() method returns.
Commonly used queries, and "baseline" queries that are useful as a starting point for construction of more complicated ones, may be stored in the $sql public property array. The $sql property is an associative array in which keys are query names, and values are query arrays. Stored queries can be submitted by passing the name key for the query as an argument to any of the select*() methods.
The $sql property is inherited from the DB_Table_Base class. Queries may thus be stored either the $sql property of a DB_Table that provides an interface to a specific table, or in the $sql property of a DB_Table_Database object that provides an interface to the entire database. For applications that involve only one table, or queries that involve only one table, it may be convenient to use the $sql property of the DB_Table object, as in the above example, and to then use the select*() methods of that object to access these queries by name. For more complicated queries and applications, however, it may be more convenient to store all queries in the $sql property of a DB_Table_Database object, and submit them by name via the select*() methods of that object.
Defining Baseline Queries in a DB_Table Subclass Definition
For our GuestBook table, let's say we define three stored SELECT statements, which may be used as a basis of more specific queries:
In the following program snippet, we declare these queries as elements of the $sql property within the GuestBook_Table class definition.
<?php
class GuestBook_Table extends DB_Table
{
// [snip] var $col = array( ... );
// [snip] var $idx = array( ... );
var $sql = array(
// multiple rows for a list
'list' => array(
'select' => "id, signdate, CONCAT(fname, ' ', lname) AS fullname",
'order' => 'signdate DESC'
),
// one row for an item detail
'item' => array(
'select' => 'id, fname, lname, email, signdate',
'get' => 'row'
),
// email => fullname (unique rows only)
'emails' => array(
'select' => "DISTINCT email, CONCAT(fname, ' ', lname) AS fullname",
'order' => 'lname, fname',
'get' => 'assoc'
)
);
}
?>
The select() method submits a query and returns the result set as an array. The selectResult() method returns the result as a DB_Result/MDB2_Result_Common object. The selectCount() method returns an integer equal to the number of rows that would be returned by a query, without returning the actual result set.
The interface is the same for all three of these methods. In each, the first parameter, which is required, can be either a query array or a key of the $sql property array, for which the corresponding value is a stored query array. The remaining parameters, which are all optional, may be used to modify the query before submission to the database. The second parameter, $filter, is a string that contains an SQL logical expression that will be ANDed with the WHERE clause of the query before the SELECT command is submitted. The third parameter, $order, if present, is used to construct the 'ORDER BY' clause, and may be used to override the 'order' element of the query. The fourth and fifth parameters, $start and $count, are integers that may be used to specify the first row of the result set that should be returned, and the maximum number of rows to be returned.
Submitting a Stored Query
<?php
// [snip] create a DB/MDB2 object and connect to the database
// [snip] create the GuestBook_Table object $GuestBook
// Submit the stored 'list' query, which returns an array of rows
$rows = $GuestBook->select('list');
// Print the result set
print_r($rows);
?>
The next example shows how to modify a stored query with the filter, order, start and count parameters:
Using Filter, Order, and Limit Parameters
<?php
$filter = "signdate = '2003-08-14'";
$order = 'lname, fname';
$start = 7;
$count = 12;
// Return results as an array of rows
$rows = $GuestBook->select($view, $filter, $order, $start, $count);
print_r($rows);
// Return results as a DB_Result/MDB2_Result_Common object
$result = $GuestBook->selectResult($view, $filter, $order, $start, $count);
print_r($result);
?>
To have select() return rows as associative arrays, we must set either the 'fetchmode' element of a specific query array or the $fetchmode property of the DB_Table object to the DB_FETCHMODE_ASSOC or MDB2_FETCHMODE_ASSOC constant values, as appropriate:
Setting Fetchmode to Return Rows as Associative Arrays
<?php
// [snip] create the $GuestBook GuestBook_Table object
// set the fetch mode to "associative"
$GuestBook->fetchmode = DB_FETCHMODE_ASSOC;
// now all rows arrays will be returned by DB_Table::select() as associative
// arrays with the column names as the array keys
?>
To have select() return rows as objects, with properties that correspond to column names, we must set either the 'fetchmode' element of the query array or the $fetchmode property of the DB_Table object to the DB_FETCHMODE_OBJECT or MDB2_FETCHMODE_OBJECT constant. If no user-defined class is specified, all rows are returned as instances of stdClass, as in the following example:
Returning Rows as stdClass Objects
<?php
// [snip] create the $GuestBook GuestBook_Table object
// set the fetch mode to the "object" constant for DB or MDB2
$GuestBook->fetchmode = DB_FETCHMODE_OBJECT;
// now each row in the results array will be a stdClass object,
// and the object properties will be named for the columns
// now all column arrays will come with the column names as the array keys
?>
To specify a class to be used to encapsulate rows of a return set, set the value of the 'fetchmode_object_class' element of the query array or the $fetchmode_object_class property to the name of the desired class.
Returning Rows as Objects of a User-Defined Class
<?php
// [snip] create the $GuestBook GuestBook_Table object
// set the fetch mode to "object"
$GuestBook->fetchmode = DB_FETCHMODE_OBJECT;
// set the fetched row class to "myRowClass" ...
// of course, you will need to include the "myRowClass" file
// before this.
$GuestBook->fetchmode_object_class = 'myRowClass';
// now each row in the results array will be a myRowClass object,
// and the object properties will be named for the columns
?>
DB_Table can automatically validate that the format of data to be inserted or updated is consistent with the declared types for the corresponding columns. Validation of the data type of both inserted and updated data is on by default. Validation upon insertion and updating can be turned on or off with the autoValidInsert() and autoValidUpdate() methods, respectively. Each of these methods takes a single boolean parameter, and turns validation on if its parameter is true and off if it is false.
Data type validation for a row of data to be inserted or updated is actually carried out by the validInsert() or validUpdate() method, respectively. Each of these methods takes one parameter, which is an associative array of data in which keys are column names. Each returns boolean true on success or a PEAR_Error object on failure. These methods are called internally by the insert() and update() methods, respectively, when auto-validation is enabled. Customized automatic validations may thus be implemented by overriding one or both of these methods.
Validation of the type of a single column value is carried out by the isValid() method. This method takes the value to be validated as its first parameter, and the name of the required DB_Table data type as its second parameter. This method is called internally by the two row validation methods.
Method | Description |
---|---|
quote() | Enquotes and escapes a value in a form appropriate for inclusion in an SQL query. A simple wrapper for the DB::smartQuote() or MDB2::quote() method, which it calls internally. |
recast() | Takes an associative array of data (keys are column names and values are column values) and re-casts each value to the proper format for its column. |
getBlankRow() | Returns an associative array with column name keys and a blank value for each column. Each value will be in the proper format for the associated column type. |
This page documents the use of the DB_Table class to create HTML_QuickForm form elements appropriate for the columns of a table, and to create entire data entry forms. DB_Table can use the column definitions to automatically create default form elements for you. Although your plain column definitions will do fine at first, you will want to customize the labels and options for your form elements. You can do so by adding 'qf_*' keys to your column definitions
Note: The underlying HTML_QuickForm package is a very powerful library with many options. While DB_Table automates away much of the complexity for simple forms, the full power of HTML_QuickForm can only be realized by combining the automatic element creation from DB_Table with custom-created HTML_QuickForm objects. DB_Table makes it easy to get started with HTML_QuickForm, but does not replace it.
Once you have defined your columns and instantiated an object, you can generate a complete HTML_QuickForm object using the getForm() method. If getForm() is called with no parameters, it will return a form with all of the $GuestBook object columns as input fields.
A default form with all columns
<?php
// [snip] create the GuestBook_Table object ($GuestBook)
// create the <classname>HTML_QuickForm</classname> object
$form =& $GuestBook->getForm(); // note the "=&" -- very important
// display the form
$form->display();
?>
To limit the fields you want to display, or to change the order, pass a sequential array of column names as the first parameter to getForm()
Specifying column names in getForm()
<?php
// [snip] create the Guesbook object ($GuestBook)
// only show the first name, last name, and email in the form
$cols = array('fname', 'lname', 'email');
// create the <classname>HTML_QuickForm</classname> object
$form =& $GuestBook->getForm($cols);
// display the form
$form->display();
?>
Unfortunately, the form only has the input fields; it does not yet have "Submit" or "Reset" buttons, which are not automatically generated.
Adding "Submit" and "Reset" buttons with getForm()
<?php
// [snip] create the $GuestBook GuestBook_Table object
// create the HMTL_QuickForm object
$form =& $GuestBook->getForm($cols);
// add a "submit" button named "op" that says "Go!"
$form->addElement('submit', 'op', 'Go!');
// add a reset button
$form->addElement('reset');
// display the form
$form->display();
?>
The basic form is nice, but it's very generic. It doesn't have descriptive labels for the input fields, all the fields are text boxes, and so on. This section shows you how to include form element properties along with your column definitions so that your forms are customized in a somewhat consistent fashion.
In every case you will modify the $col property by adding 'qf_*' keys and values.
When a column appears in a form, you can make sure the same label is always applied to it. Let's say we have a column called "other" defined as a 32-character string. We always want it to be labelled as "Other Information" in our forms; use 'qf_label' key like this:
Setting 'qf_label'
<?php
class GuestBook_Table extends DB_Table
{
var $col = array(
// ...
'example' => array(
// table column definition
'type' => 'varchar',
'size' => 32,
// form element definition
'qf_label' => 'Other Information'
)
);
}
?>
By default, DB_Table will try to figure out what kind of form field input type to use for your column. Most column types translate into text fields, but there are some exceptions.
To explicitly set the HTML_QuickForm element type for a column, rather than relying on the above defaults, you add a 'qf_type' element to your column definition. You can tell DB_Table to create any of several supported HTML_QuickForm element types by setting the 'qf_type' element to one of these string values:
If you use any other element name, DB_Table will attempt to map to the proper HTML_QuickForm element (thanks to Moritz Heidkamp for the patch), but it's not guaranteed to work.
In this example, we'll make the 'example' column a text field:
Setting an element type
<?php
class GuestBook_Table extends DB_Table
{
var $col = array(
// ...
'example' => array(
// table column definition
'type' => 'varchar',
'size' => 32,
// form element definition
'qf_label' => 'Other Information',
'qf_type' => 'text'
)
);
}
?>
If your column form element is 'checkbox', 'radio', or 'select', you can set the values of the available choices for the form element. You do so via an 'sq_vals' element in the corresponding column definition.
Setting CheckBox Values
For a checkbox, use a sequential array of two elements: the first is the value if the box is not checked, and the second is the value if it is checked.
In this example, the checkbox values are 0 if not checked,and 1 if checked; these are the values that will be stored in the column.
<?php
class GuestBook_Table extends DB_Table
{
var $col = array(
// ...
'example' => array(
// table column definition
'type' => 'varchar',
'size' => 32,
// form element definition
'qf_label' => 'Other Information',
'qf_type' => 'text',
'qf_vals' => array(0, 1)
)
);
}
?>
Radio and Select Values
For radio buttons and select menus, use an associative array. Each array key is the value that will be stored in the column, and each array value is the text that will be displayed to the user.
This example creates a select menu with three possible values ('a', 'b', and 'c') and the corresponding labels:
<?php
class GuestBook_Table extends DB_Table
{
var $col = array(
// ...
'example' => array(
// table column definition
'type' => 'varchar',
'size' => 32,
// form element definition
'qf_label' => 'Other Information',
'qf_type' => 'text',
'qf_vals' => array(
'a' => 'This is the letter "a"',
'b' => 'I choose "b"',
'c' => 'No, "c" is always the right answer'
)
)
);
}
?>
If you like, you can add HTML attributes to the form element using the 'qf_attrs' keys. Attributes are assigned as key-value pairs in an associative array; the keys is the attribute name, and the value is the attribute value. For example, to set the number of rows and columns for a textarea element:
Setting rows and column number for a textarea
<?php
class GuestBook_Table extends DB_Table
{
var $col = array(
// ...
'example' => array(
// table column definition
'type' => 'varchar',
'size' => 32,
// form element definition
'qf_label' => 'Other Information',
'qf_type' => 'textarea',
'qf_attrs' => array(
'rows' => 24,
'cols' => 80
)
)
);
}
?>
DB_Table automatically adds a "maxlength" attribute to text and password elements based on the column size, so that users cannot type in values longer than the column allows.
HTML_QuickForm allows you to add validation rules to the input form. These rules are not the same as the DB_Table automated validations; the HTML_QuickForm rules apply only to the values as they relate to the form itself, not the values related to the table proper. This is an important distinction that will become apparent as you use HTML_QuickForm.
The available rules are:
DB_Table will automatically add HTML_QuickForm rules for you in most cases.
If you want to use a specific HTML_QuickForm rule for a column when that column appears in a form, set 'qf_rules' key to the name of the rule and set the value for that rule; in most cases, the value is an error message, but in some cases the value is a sequential array. You can add as many different rules as you like, but you can add only one of each type.
QuickForm Rules
<?php
class GuestBook_Table extends DB_Table
{
var $col = array(
// ...
'example1' => array(
// table column definition
'type' => 'integer',
// form element definition
'qf_label' => 'Information',
'qf_type' => 'text',
// a set of QuickForm rules
'qf_rules' => array(
'required' => 'This field is required.',
'numeric' => 'Please use only numbers, no letters.'
)
),
'example2' => array(
// table column definition
'type' => 'varchar',
'size' => 10,
// form element definition
'qf_label' => 'Something:',
'qf_type' => 'text',
// a set of QuickForm rules
'qf_rules' => array(
'minlength' => array('Minimum length is 6 characters.', 6),
'maxlength' => array('Maximum length is 10 characters.', 10),
'regex' => array(
'Must be only upper-case letters and underscores.',
'/^[A-Z_]+$/'
)
);
)
)
}
?>
HTML_QuickForm supports rules for groups and entire forms, but DB_Table does not automate these; you will need to add them to the form object yourself.
Sometimes you will want to populate your input form with default values, such as the current values from the database. Doing so is easy.
When you call getForm(), instead of passing a sequential array of column names, pass an associative array in which the key is the column name and the value is the column value.
Setting Default Values for Form Fields
<?php
// [snip] create the GuestBook_Table object ($GuestBook)
// Show only the first name, last name, and email in the form
// Provide default values for the form elements
$cols = array(
'fname' => 'Thomas',
'lname' => 'Anderson',
'email' => 'neo@matrix.net'
);
// create the HTML_QuickForm object
$form =& $GuestBook->getForm($cols);
// display the form
$form->display();
?>
By default, when you call getForm(), the form elements are named for their columns. For example, the column 'fname' is called 'fname' in the form.
<!-- [snip] the beginning of the form --> <input type="text" name="fname" ... /> <!-- [snip] the end of the form -->
However, often you will want the form elements to be keys in any array instead of their own separate variables. This is particularly useful when you are going to use the values in a DB_Table insert() or update() call.
To do so, pass the name of an array as the second argument to getForm() (after the list of columns to use in the form). For example, if you want the column names to be keys in an array called 'new_row', do this:
Returning form values in an array
<?php
// [snip] create the $GuestBook object of class GuestBook_Table
// choose which columns to display in the form
$cols = array('fname', 'lname', 'email');
// create the HTML_QuickForm object, with elements
// named as part of an array called 'new_row'
$form = $GuestBook->getForm($cols, 'new_row');
// display the form
$form->display()
?>
Although the getForm() method supports all HTML_QuickForm parameters (such as the name of the form, the method, the action, and so on), you don't need to use DB_Table to create the entire form. If you like, you can create your own HTML_QuickForm object, and add DB_Table columns to it one-by-one or in groups.
DB_Table provides these methods to add automatically-defined elements to pre-existing HTML_QuickForm objects:
Here are example uses of each:
Custom HTML Element
<?php
// [snip] Create the GuestBook_Table object $GuestBook
// load the class file and create a QuickForm object
require_once 'HTML/QuickForm.php';
$form =& new HTML_QuickForm();
// add one or more elements from the GuestBook
// object to the QuickForm object, along with
// the rules for those elements
$cols = array('email', 'signdate');
$GuestBook->addFormElements($form, $cols);
// add an element group of GuestBook columns
// to the QuickForm object (does not add rules)
$cols = array('fname', 'lname');
$group =& $GuestBook->getFormGroup($cols);
$form->addGroup($group);
// get back a single form element object with a
// custom name, then add it to the form
$col = 'id';
$name = 'new_row[id]';
$element =& $GuestBook->getFormElement($col, $name);
$form->addElement($element);
?>
DB_Table_Database is an database abstraction class for a relational database. It is a layer built on top of the DB_Table class: Each table in a DB_Table_Database object is represented by a DB_Table object. The most important difference between a DB_Table_Database object and a collection of DB_Table objects is that the properties of a parent DB_Table_Database object contain a model of the entire database, including relationships between tables.
DB_Table_Database provides:
Like DB_Table, DB_Table_Database wraps a DB or MDB2 database connection object. The class is compatible with both PHP 4 and PHP 5, with the exception of one non-essential method: The fromXML() method, which creates a DB_Table_Database object from an XML database schema, requires PHP 5.
Class DB_Table_Database extends abstract base class DB_Table_Base. Methods or properties that are inherited from DB_Table_Base are noted as such, and are indicated in the table of contents of this page with the notation "(from DB_Table_Base)".
This tutorial uses an extended example to introduce the use of the DB_Table_Database class to create a model of an interface to a relational database.
Throughout this tutorial, our examples will refer to a DB_Table_Database object for an example database named TestDB, which is described below. The child DB_Table objects that are associated with RDBMS tables must all be instantiated first, and then added to (i.e., linked with) a parent DB_Table_Database object.
The example database TestDB stores names, numbers, and addresses for a set of people, and contains 4 tables. Peoples names, phone numbers, and addresses are stored in three tables named Person, Phone, and Address, respectively. To allow for the fact that several people may share a phone number, and that a person may have more than one phone number, the database allows the creation of a many-to-many relationship between Person and Phone. This relationships are established by an additional linking table, named PersonPhone, which contains foreign key references to Person and Phone.
DB_Table objects must be instantiated before they can be added to a parent DB_Table_Database instance. The usual way of creating a DB_Table object (as discussed in the tutorial for that class) is to create one subclass of DB_Table for each table, and create one instance of each such subclass. In this tutorial, we use a convention in which the subclass of DB_Table associated with a database table named "Entity" is Entity_Table, and in which the single object of this class is $Entity. This is also the convention used in code generated by the DB_Table_Generator class.
The following code defines a subclass Person_Table that represents a database table Person:
<?php
require_once 'DB/Table.php'
class Person_Table extends DB_Table
{
// Define columns
$col = array (
'PersonID' => array('type' => 'integer', 'require' => true),
'FirstName' => array('type' => 'char', 'size' => 32, 'require' => true),
'MiddleName' => array('type' => 'char', 'size' => 32),
'LastName' => array('type' => 'char', 'size' => 64, 'require' => true),
'NameSuffix' => array('type' => 'char', 'size' => 16),
'AddressID' => array('type' => 'integer')
);
// Define indices. PersonID is declared to be the primary index
$idx = array(
'PersonID' => array('cols' => 'PersonID', 'type' => 'primary'),
'AddressID' => array('cols' => 'AddressID', 'type' => 'normal')
);
// Declare 'PersonID' to be an auto-increment column
$auto_inc_col = 'PersonID';
}
?>
Here, 'PersonID' is the primary index of the Person table. Column AddressID is a foreign key that references the primary key of the Address table (defined below).
Note the assignment of a value for the $auto_inc_col property, which is a recent addition to DB_Table: The value of $auto_inc_col is the name of a column that is declared to be 'auto increment'. Auto incrementing of this column is now implemented in the insert method of DB_Table using DB or MDB2 sequences.
The following code uses the same method to create subclasses of DB_Table associated with the remaining Phone, Address, and PersonPhone tables of database TestDB:
<?php
class Address_Table extends DB_Table
{
$col = array(
'AddressID' => array('type' => 'integer', 'require' => true),
'Building' => array('type' => 'char', 'size' =>16),
'Street' => array('type' => 'char', 'size' => 64),
'UnitType' => array('type' => 'char', 'size' => 16),
'Unit' => array('type' => 'char', 'size' => 16),
'City' => array('type' => 'char', 'size' => 64),
'StateAbb' => array('type' => 'char', 'size' => 2),
'ZipCode' => array('type' => 'char', 'size' => 16)
);
$idx = array(
'AddressID' => array('cols' => 'AddressID', 'type' => 'primary'),
);
$auto_inc_col = 'AddressID';
}
?>
<?php
class Phone_Table extends DB_Table
{
$col = array(
'PhoneID' => array('type' => 'integer', 'require' => true),
'PhoneNumber' => array('type' => 'char', 'size' => 16, 'require' => true),
'PhoneType' => array('type' => 'char', 'size' => 4)
);
$idx = array(
'PhoneID' => array('cols' => 'PhoneID', 'type' => 'primary')
);
$auto_inc_col = 'PhoneID';
}
?>
<?php
class PersonPhone_Table extends DB_Table
{
$col = array(
'PersonID' => array('type' => 'integer', 'require' => true),
'PhoneID' => array('type' => 'integer', 'require' => true)
);
$idx = array(
'PersonID' => array('cols' => 'PersonID', 'type' => 'normal'),
'PhoneID' => array('cols' => 'PhoneID', 'type' => 'normal')
);
}
?>
In ths example, the PhoneType column of table Phone is used to distinguish home, work, and cell phones, and so must have one of the 4 character values 'HOME', 'WORK' or 'CELL'.
The following code instantiates one object of each DB_Table subclass, which is associated with the corresponding table:
<?php
$Person = new Person_Table($conn, 'Person', 'safe');
$Address = new Address_Table($conn, 'Address', 'safe');
$Phone = new Phone_Table($conn, 'Phone', 'safe');
$PersonPhone = new PersonPhone_Table($conn, 'PersonPhone', 'safe');
?>
Here, because we have used the value 'safe' for the optional third parameter of the DB_Table constructor in each statement, each table will be created in the RDBMS only if and only if a table of that name does not already exist in the database.
It is recommended that constructor statements be placed in a separate file from any of the DB_Table subclass definitions. Doing so makes it easier to serialize and unserialize the DB_Table and DB_Table_Database objects, because a php file in which an instance of a DB_Table subclass is unserialized must have access to the subclass definition, but should not include the constructor statements. Putting each DB_Table subclass definition in a separate file, with a name that is the subclass name with a .php extension, also allows the subclass definitions to be autoloaded when an object is serialized, as discussed below.
An alternative way to create a DB_Table object is to create an instance of DB_Table itself, rather than of a subclass of DB_Table. In this method, one first instantiates a generic DB_Table object, which initially contains no information about the table schema, and then sets the values of the public $col and $idx properties needed to define a table schema. As an example, the following code constructs an instance of DB_Table that represents the Person table:
<?php
$Person = new DB_Table($conn, 'Person');
$Person->col['PersonID'] = array('type' => 'integer', 'require' => true);
$Person->col['FirstName'] = array('type' => 'char', 'size' => 32, 'require' => true);
$Person->col['MiddleName'] = array('type' => 'char', 'size' => 32);
$Person->col['LastName'] = array('type' => 'char', 'size' => 64, 'require' => true);
$Person->col['NameSuffix'] = array('type' => 'char', 'size' => 16);
$Person->col['AddressID'] = array('type' => 'integer');
$Person->idx['PersonID'] = array('cols' => 'PersonID', 'type' => 'primary');
$Person->idx['PersonID'] = array('cols' => 'PersonID', 'type' => 'normal');
$Person->auto_inc_col = 'PersonID';
?>
This method is valid only in recent versions of the DB_Table package (1.5.0RC1 and greater) that contain the DB_Table_Database class. Earlier versions of DB_Table required that DB_Table always be extended. The only real disadvantage of using such generic DB_Table objects is that it makes it impossible to override the methods of DB_Table to, for example, customize the insert or update method so as to implement business rules for a table. Generic DB_Table objects are used by the fromXML() method, which takes an XML description of a database schema as a parameter, and returns a DB_Table_Database object in which each of the child tables is represented by an instance of DB_Table.
A DB_Table_Database object is instantiated as an empty shell, to which tables, foreign key references, and links are then added. The constructor interface is
The parameter $conn must be either a DB or MDB2 object, which establishes a connection to a RDBMS. The $name parameter is the name of the database. To instantiate an object that represents a database named TestDB with a DB connection to a MySQL database, we might thus use (with no error checking):
<?php
require_once 'DB/Table/Database.php'
$conn = DB::connect("mysqli://$user:$password@$host");
$db = new DB_Table_Database($conn, 'TestDB');
?>
where the values of $user, $password, and $host are the user name, database password, and host machine, respectively.
To construct a model of a relational database, after instantiating a set of DB_Table objects and a DB_Table_Database object, we must add the table objects to the database object, add declarations of foreign key references, and declare many-to-many relationships that involve linking tables, in that order.
After a DB_Table object is instantiated, it can be added to the parent database with the DB_Table_Database::addTable() method. The interface for this method is
where $Table is a DB_Table object that is passed by reference. The method returns boolean true on normal completion, and a PEAR_Error object on failure.
The following code adds the four tables of our example database to the $db DB_Table_Database object:
<?php
$db->addTable($Person);
$db->addTable($Address);
$db->addTable($Phone);
$db->addTable($PersonPhone);
?>
In this and all subseqent examples, we omit the error handling code that should be added to production code.
After tables have been added to a database, we can use the addRef() method to add references between pairs of tables.
Synopsis (simplified):
Here $ftable is the name of a referencing (or foreign key) table, $fkey is the foreign key, $rtable is the name of the referenced table, and $rkey is the (optional) referenced key. If the optional $rkey parameter is absent or null, the referenced key is taken by default to be the primary key of the referenced table. The foreign and referenced key values are specified using the same syntax as that used to define indices in DB_Table: Each key may be either a column name string, for a single-column key, or a sequential array of column names, for a multi-column key. The method returns true on normal completion, and a PEAR_Error on failure. The simplified synopsis shown here does not include two more optional parameters (parameters 5 and 6) that can be used to specify 'on delete' and 'on update' actions. The full interface is presented below. For example, the command:
<?php
$db->addRef('Person', 'AddressID', 'Address', 'AddressID');
?>
adds a reference from foreign key Person.AddressID of referencing table Person to the primary key Address.AddressID of referenced table Address. Because the referenced key 'AddressID' is also the primary key of table 'Address' this could also be written as:
<?php
$db->addRef('Person', 'AddressID', 'Address');
?>
When the referenced key is explicitly specified, as in the first example, it should always be either a primary key or a key for which a unique index is defined, as required by standard SQL.
A reference between two tables can only be added after both the referencing and referenced DB_Table objects have been instantiated and added to the parent DB_Table_Datbase instance.
A table may be declared to be a "linking" table that establishes a many-to-may relationship between two others. The only effect of such a declaration is to change the action of the autoJoin method: If a table named $link is declared to be a linking table that creates a many-to-many relationship between tables named $table1 and $table2, then the autoJoin method may use the linking table to join $table1 and $table2, if necessary.
Method addLink() declares a table to be a linking or association table for two others.
Synopsis:
Here, $link is the name of a linking table that links tables named $table1 and $table2. All three parameters are required, and all must be valid table name strings. It does not matter which of the two linked tables, $table1 and $table2, is listed first and which second in the function call. A table that links $table1 and $table2 must have foreign keys that references to both of the linked tables. A link can only be added to the model after the references from the linking table to both of the linked tables have been added. The method returns true on normal completion and a PEAR_Error() if an error is detected.
For example, the command:
<?php
$db->addLink('Person', 'Phone, 'PersonPhone')
?>
declares PersonPhone to be a linking table that links tables Person and Phone.
The addLink() method will not prevent one from declarating more than one linking table for the same two linked tables. Doing so would make the link declaration useless, however, because the autoJoin() method will fail if it needs to use a linking table to join a pair of tables, but finds that more than one of linking table is declared for those two tables.
The command
<?php
$db->addAllLinks()
?>
adds all possible linking tables to the database. In this method, any table that has foreign keys that reference tables $table1 and $table2 is declared to be a link between $table1 and $table2. In some databases, the easiest way to declare links may be by using the addAllLinks method to create all possible links and then using the deleteLink() method to delete those that are not desired.
The database model is complete when all of the tables have been added, all of the references have been added, and all the links have been added.
For our example, let us create a directory in which to put all of the code required as an interface to a database. We will put each DB_Table subclass definition in a separate file in this directory, in which each file name is simply the class name with a '.php' extension. In addition, it is convenient to create a single file, which we will call 'Database.php', in which we create a DB or MDB2 connection, create one object per table, and construct a parent DB_Table_Database object. This file structure is used by the DB_Table_Generator class for code that is auto-generated for an existing database. Below is a listing of the minimal 'Database.php' file required for our example database:
Database.php File
<?php
require_once 'MDB2.php';
require_once 'DB/Table/Database.php';
require_once 'Person_Table.php';
require_once 'Address_Table.php';
require_once 'Phone_Table.php';
require_once 'PersonPhoneAssoc_Table.php';
// NOTE: User must uncomment & edit code to create $dsn
$phptype = 'mysqli';
$username = 'root';
$password = 'password';
$hostname = 'localhost';
$dsn = "$phptype://$username:$password@$hostname";
// Instantiate DB/MDB2 connection object $conn
$conn =& MDB2::connect($dsn);
if (PEAR::isError($conn)) {
print "Error connecting to database server\n";
print $conn->getMessage();
die;
}
// Create one instance of each DB_Table subclass
$Person = new Person_Table($conn, 'Person');
$Address = new Address_Table($conn, 'Address');
$Phone = new Phone_Table($conn, 'Phone');
$PersonPhoneAssoc = new PersonPhoneAssoc_Table($conn, 'PersonPhoneAssoc');
// Instantiate a parent DB_Table_Database object $db
$db = new DB_Table_Database($conn, '42A');
// Add DB_Table objects to parent DB_Table_Database object
$db->addTable($Person);
$db->addTable($Address);
$db->addTable($Phone);
$db->addTable($PersonPhoneAssoc);
// Add foreign references
$db->addRef('PersonPhoneAssoc', 'PersonID', 'Person');
$db->addRef('PersonPhoneAssoc', 'PhoneID', 'Phone');
$db->addRef('Person', 'AddressID', 'Address');
// Add all possible linking tables
$db->addAllLinks();
?>
This example file is very similar to the skeleton file that would be created by DB_Table_Generator for an existing database with this structure. The main differences are that some lines in the auto-generated file would have to be uncommented or edited to produce the above (e.g., the lines that define the database DSN). In this example, the call to addAllLinks() method would correctly identify 'PersonPhoneAssoc' as a table that links 'Person' and 'Phone'. This example does not include any referentially triggered 'ON DELETE' or 'ON UPDATE' actions, discussed below, which could be added to the end of the same file.
The deleteTable(), deleteRef(), and deleteLinks() methods can be used to delete tables, and foreign key references, and linking table declarations, respectively, from the DB_Table_Database model.
deleteTable() - deletes a table from the database model
Synopsis:
Parameter $table is the name of the table to be deleted. Deletion of a table causes deletion of the table and all other entities of the model that depend on the existence of that table, including foreign key references to or from that table, and linking relationships that depend upon the existence of those foreign key references.
deleteRef() - deletes a reference from the database model
Synopsis:
where $ftable and $rtable are the names of the referencing and referenced tables, respectively. Deletion of a foreign key reference causes deletion of any links that rely on the existence of that reference, i.e., links in which $ftable is the linking table and $rtable is one of the linked tables.
deleteLink() - deletes a linking table declaration
Synopsis:
Here $table1 and $table2 are names of the linked tables, and the optional parameter $link is the name of the linking table. If $link is null or absent, all declarations of linking tables between $table1 and $table2 are deleted. If $link is present, only the declaration of $link as a linking table between $table1 and $table2 is deleted (if one exists). The deleteLinks() method may be used after the addAllLinks() to prune the resulting set of linking table declarations.
DB_Table_Database optionally provides actions designed to enforce referential integrity that are provided by ANSI SQL, but that are not provided by some popular databases (e.g., SQLite and the default MySQL engine). DB_Table_Database offers optional PHP emulation of referentially triggered ON DELETE and ON UPDATE actions, such as cascading deletes (discussed here), and also optionally checks the validity of foreign key values before insertion or updating (discussed below)
The ON DELETE and ON UPDATE actions associated with a reference (if any) may be declared either as additional parameters to addRef(), or by using setOnDelete() and setOnUpdate().
Actions to be taken on deletion or updating of a referenced row may may be declared when a reference is added to the model using two optional parameters of the addRef() method. The following example shows the extended form of addRef() needed to add a reference from PersonPhone to Person (as above), while also declaring a cascade action on delete of a referenced row of Person, and a restrict action on update of such a row:
<?php
$db->addRef('PersonPhone', 'PersonID', 'Person', null, 'cascade', 'restrict');
?>
Here, a null value of the fourth parameter is used to indicate that the referenced key should be taken, by default, to be primary key of referenced table Person. The values of the fifth and sixth parameters represent actions to be taken upon delete ('cascade') and upon update ('restrict'), respectively. A null or absent value for either of these parameters indicates that no referentially triggered action should be taken on delete or on update.
The effect of the 'cascade' values of the fifth parameter in the above example is to declare that that all referencing rows of PersonPhone should be deleted upon deletion of a corresponding referenced row of Person (a cascading delete). The 'restrict' value of the sixth parameter declares that updating of the primary key PersonID of Person should be prevented (the 'restrict' on update action), and an error should be thrown by the update method, in rows of Person that are referenced by rows of PersonPhone.
The full interface of the addRef() method is:
Here, $ftable is the referencing table, $fkey is the foreign key, $rtable is referenced table, and $rkey is the referenced key. The $fkey and $rkey parameters may be column name strings or arrays of column names, or $rkey may be null. An absent or null value of $rkey indicates a reference to the primary key of the referenced table. The $on_delete and $on_update parameters indicate actions to be taken on deletion or updating of a referenced row. The only allowed values of $on_delete and $on_update are the string literals
'cascade' | 'restrict' | 'set null' | 'set default'
or PHP null, which is the default value for both parameters. Each of the allowed action strings is the lower case form of a standard SQL action, and turns on PHP emulation of the corresponding action. An absent or null value for either action indicates that no action should be taken at the PHP layer upon delete or update of a referenced row. (Note that a PHP null value is different from the 'set null' action string.)
The following example declares all of the foreign key references needed in our example database, with appropriate referentially triggered actions:
<?php
$db->addRef('Person', 'AddressID', 'Address', null, 'set null', 'cascade');
$db->addRef('PersonPhone', 'PersonID', 'Person', null, 'cascade', 'cascade');
$db->addRef('PersonPhone', 'PhoneID', 'Phone', null, 'cascade', 'cascade');
?>
As a result of these declarations, rows in the linking table PersonPhone will be deleted when corresponding rows of either Person or Phone are deleted, and updated if the primary keys of reference rows Person or Phone are modified. The foreign key AddressID of a row in table Person will be set to null if the corresponding referenced row of Address is deleted (to indicate that no address is known), and updated if the primary key of that row in Address is modified.
The referentially triggered actions associated with a foreign key reference may also be changed, or turned off, with the setOnDelete() and setOnUpdate() methods.
Synopses:
Here, $ftable and $rtable are the names of referencing (foreign key) and referenced table, respectively, for an existing reference. The $action parameter is the value for the on_delete or on_update action for that reference, i.e., either an action strings or null. A null parameter is used to indicate that no action on delete or update of rows of table $rtable.
For example, the following code would change the 'on_update' action associated with the reference from 'PersonPhone' to 'Person' to a 'restrict' action:
<?php
$db->setOnUpdate('PersonPhone', 'Person', 'restrict');
?>
The effect this is to prohibit updates of the primary key value in rows of Person that are referenced by rows of PersonPhone.
PHP emulation of referentially triggered actions may be turned on or off for the entire database by the setActOnDelete() and setActOnUpdate() methods.
Synopses:
Passing a true value to either method activates PHP emulation of all of the declared ON DELETE or ON UPDATE actions, respectively, while a false value turns off PHP emulation of the corresponding action. By default, PHP emulation of both ON DELETE and ON UPDATE actions is on. Calling either of these methods with a false value does not modify the values of the instance property (the $_ref property) that records the on delete or on update actions associated with each reference: It merely prevents PHP emulation of these actions by the DB_Table_Database::delete() and DB_Table_Database::update() methods.
By default, DB_Table_Database checks the validity of foreign key values before inserting or updating data in a table with foreign keys. That is, before inserting a row, or updating any foreign key column values, the insert() and update() methods of DB_Table_Database actually submit a query to confirm that the inserted or updated foreign key column values correspond to values of the referenced columns of an existing row in the referenced table. By default, both methods throw an error, and do not modify the data, if this check fails.
This checking of foreign key validity by the PHP layer may be turned on or off, for insertion or updating of any table in database, with the setCheckFKey() method. The interface of this method is:
Passing a true value of $flag turns on checking of foreign keys (the default), while a false value turns checking off.
DB_Table_Database provides an object-oriented interface for SQL select statements that is almost identical to that of DB_Table.
As in DB_Table, queries are represented in DB_Table_Database as arrays, in which array elements represents clauses of a corresponding SQL select statement. For example, a query for names of all people that live on Oak Street in Anytown in our example database might be
The buildSQL() method accepts such a query array as a parameter and returns the corresponding SQL command string. For example
<?php
echo $db->buildSQL($oak);
?>
yields the output
SELECT Person.FirstName, Person.LastName, Address.Building FROM Person, Address WHERE Person.AddressID = Address.AddressID AND Address.Street = 'Oak Street' AND Address.City = 'AnyTown' ORDER BY Address.Building
The string values of most values in this array are passed to the RDBMS unmodified, prefixed by the keywords 'SELECT', 'FROM', etc. Column names that appear in only one table often do not need to be qualified by table names, as they are in the above example.
As in DB_Table, such query arrays can be stored in the public $sql property array:
<?php
$db->sql['oak'] = $oak
?>
Representing queries as arrays, rather than strings, makes it easier for baseline queries to be modified by, for example, adding additional limitations to the end of the 'where' clause string.
The select*() methods are inherited by both the DB_Table_Database and DB_Table classes from the DB_Table_Base class, and thus share the same interface and behavior. The interface is also the same for all three methods. The required first parameter can be either the key for a previously stored query array, as in
<?php
$result = $db->select('oak')
?>
or the corresponding array value as a parameter, as in
<?php
$result = $db->select($oak)
?>
The select method returns a result set as a numerically indexed array of rows. Each row can represented as be either an associative or numerical array, or an object, depending on the value of the $fetchmode property of the DB_Table_Database object or (if this is null) the fetchmode of the underlying DB or MDB2 object.
The common interface of three select* methods select(), selectCount(), and selectResult() is:
As discussed above, $sql_key is either a query array, or the key of a baseline query array that has been stored in the $sql property. The $filter parameter is an SQL logical expression string that limits the result set. This condition is added (i.e., ANDed) to the end of the 'where' element of the $sql_key query array. The $order parameter is an ORDER BY clause (without the ORDER BY prefix) that can be used to override any 'order' element of the $sql_key array. Integer parameters $start is which the position within the full result set of the first row that should be included in the return value, while $count is the maximum number of rows desired within the return value. If present, $params is an array in which the values are parameters for placeholder substitution in a prepared query.
autoJoin() - accepts an array parameter containing the names of desired columns and/or an array of tables names, and returns a query array containing a WHERE clause with automatically generated join conditions.
Synopsis:
<?php
array|PEAR_Error autoJoin([array $cols], [array $tables], [string $filter])
?>
Here, $cols is a sequential array of the names of the desired columns, $tables is a sequential array of names of tables to be joined, and $filter is an SQL logical statement that may be used to limit the results. The $filter clause is added (i.e., ANDed) to the end of the the 'where' element, after the automatically generated join conditions. Both the $col and $tables parameter are optional, but at least one of them must be supplied. The query returned by autoJoin is an inner join of a set of tables containing all of those listed in the $tables parameter, all of the tables containing the columns listed in the $cols parameter, and any linking tables required to join these tables.
The following example generates and submits query that selects a result set in which each row contains a person's name, home phone number and address, based on knowledge of the names of the desired columns. In our example database, this requires that all four tables be joined.
<?php
$cols = array('FirstName', 'LastName', 'PhoneNumber', 'Building', 'Street', 'City')
$report = $db->autoJoin($cols, "Phone.PhoneType = 'HOME'");
$result = $db->select($report);
?>
Note that the column names in the $cols parameter do not need to be qualified by table names if they are unambiguous -- the autoJoin method internally uses the validCol() method to validate and disambiguate all qualified and unqualified column names.
The SQL command corresponding to such a query array may be obtained using buildSQL(). In this example,the command
<?php
echo $db->buildSQL($report);
?>
yields
SELECT Person.FirstName, Person.LastName, Phone.PhoneNumber, Address.Building, Address.Street, Address.City FROM Person, Phone, Address, PersonPhone WHERE PersonPhone.PhoneID = Phone.PhoneID AND PersonPhone.PersonID = Person.PersonID AND Person.AddressID = Address.AddressID
If autoJoin() is passed only a set of column names, as in the above example, it identifies the set of tables that contain those columns, and joins those tables, plus any linking tables needed to create many-to-many relationships.
In the following example, the $cols parameter is null, but the names of the tables to be joined are specified in the $tables parameter:
<?php
$tables = array('Person', 'Address', 'Phone');
$report = $db->autoJoin(null,$tables);
$result = $db->select($report);
?>
The corresponding SQL command is
SELECT * FROM Person, Phone, Address, PersonPhone WHERE PersonPhone.PhoneID = Phone.PhoneID AND PersonPhone.PersonID = Person.PersonID AND Person.AddressID = Address.AddressID
When the first argument of autoJoin is null, as in this example, the SELECT clause is taken to be 'SELECT * by default. Note that the FROM and WHERE clauses join a linking table PersonPhone that was not explicitly specified, because this table was necessary to join two of the tables containing the desired data.
Algorithm: The algorithm used by autoJoin() is designed to find appropriate join conditions if these exist and are unambiguous, and to return a PEAR Error if the structure of references and linking tables either yields a multiply connected network of joins, or if it cannot construct an appropriate set of join conditions. The method first examines the $col and $table property to identify the list of tables that must be joined. It then creates a network of joined tables (the joined set) by starting with one table and sequentially adding tables to the joined set from the set of tables have not yet been joined (the unjoined set). The process starts by taking the first required table as a nucleus of the joined set, and then iterating through the unjoined set in search of a table that can be joined to the first. During this and each subsequent stage of addition, the method iterates through the unjoined set in search of a table that can be joined to any table in the joined set (i.e., that either references or is referenced by one of the tables in the joined set.) If it finds an unjoined table that can be joined to exactly one table in the joined set, that table is added to the joined set, and the search for another table to join begins. If the search encounters a table in the unjoined set that can be joined to two or more tables in the joined set, the method returns an error indicating that the join conditions are ambiguous -- the method will only return a set of joins that correspond to a tree graph (where tables are nodes and joins are bonds) and will reject any multiply connected set of joins. If it is found that none of the tables in the unjoined set can be directly joined to any table in the joined set, the method then cycles through the unjoined set again in search of a table that can be joined to exactly one table in the joined set through a linking tables. If it finds a table that is connected via linking tables to two or more tables in the joined set, it will also return an error. If the search does not identify any unjoined table that can be joined to a table in the joined set either through a direct reference or a linking table, the method returns an error indicating that the required set of tables can not be joined.
The quote method returns an SQL literal string representation of the parameter $value.
Synposis:
The DB_Table_Database::quote() method calls either the DB::quoteSmart() or MDB2::quote() method internally. The return value is always a string, with a return value whose format depends upon the PHP type of $value: If $value is a string, the method returns a string that is properly quoted and escaped for the underlying RDBMS. If $value is an integer or float, it returns an unquoted string representation of the number. If $value is boolean, it returns '1' for true, or '0' for false (consistent with the representation of booleans as integers used by the DB_Table abstract data type). If $value is null, it returns the unquoted string NULL.
buildFilter() returns a SQL logical expression that is true if the values of a specified set of database columns are equal to a corresponding set of SQL literal values. It must be passed an array parameter in which the array keys are column names and the array values are the required values.
The following example uses the buildFilter method to construct a filter for addresses on Pine St. in Peoria:
<?php
$data = array('Street' => 'Pine St', 'City' => 'Peoria');
$filter = $db->buildFilter($data);
?>
Printing $filter then yields the SQL snippet:
Street = 'Pine St' AND City = 'Peoria'
In this example, the resulting SQL string is admittedly longer than the code required to create it. The function becomes more useful when the column names and/or values are variables representing data of various types, rather than string literals, or strings that may require escaping. The buildFilter method uses the quote method internally to construct SQL literal string representations of values.
buildSQL() - takes a query array of the form used by the select* methods, and returns a corresponding SQL command string. It is called internally by the select*() methods. Both buildSQL() and the select*() methods are inherited from DB_Table_Base.
Synopsis: The interface is similar to that of the select*() methods:
As in the select*() methods, $query is a query array or a key for a query array stored in the $sql property, $filter is an SQL logical condition that is added to the 'where' element of the query array, $order is an ORDER BY clause that overrides the 'order' element of the query array when it is present, and $start and $count are the first row in the result set that should be returned, and the maximum number of rows that should be returned. Only the $query argument is required.
validCol() - validates and (if necessary) disambiguates column names.
Synopsis:
The required parameter $col is a column name. The optional $from parameter is a sequential array of table names.
The $col parameter may either be a column name qualified by a table name, using the SQL syntax table.column, or a column name that is not qualified by a table name, if the identification of the column with a table is unambiguous. The return value of validCol, upon success, is a sequential array in which the second element is the unqualified column name string, and the first element is either a table name (if $col is qualified by a table name or a unique table can be identified) or a sequential array of possible column names (if $col is an unqualified column name that could refer to columns in two or more different tables). If no column with the specified name exists in the database, a PEAR error is returned.
The optional $from parameter is used only when $col is not explicitly qualified by a table name. When it is present, $from is a sequential list of tables that should be searched for a column of the specified name (as in the from clause of an SQL select statement). In this case, validCol first searches the tables in $from, and returns a table name if this yields a unique result. If a set of or more tables in $from are found to contain a column with the specified name, the return value is that set, or a subset thereof. If none of the table in $from contain a columns with the specified name, the search is instead broadened to all tables in the database. If two or more choices still remain at this point (either more than one tables in from, or more than one tables in the rest of the database) the method tries excluding tables in which the specified column is a foreign key column, if this still leaves one or more tables in which the column is not a foreign key column.
The insert(), delete(), and update() methods of DB_Table_Database have interfaces and behaviors similiar to those of the corresponding methods of DB_Table. The only differences in the interfaces are that each of these DB_Table_Database method requires an additional first parameter whose value is the name of the table to which the SQL insert, update, or delete command should be applied.
Synopses:
In all three functions $table_name is the name of the table to which the operation should be applied. In the insert and update methods, $data is an associative array of data to be inserted or updated, in which the keys are column name strings and the values are the value to be inserted or updated in the database. In the delete and update methods, the optional $where parameter is a string containing an SQL logical condition that is used to select the rows that should be deleted or updated, respectively. That is, the $where parameter should contain the contents of the WHERE clause of the corresponding SQL command, without the 'WHERE ' prefix. Each method returns true on normal completion, and a PEAR Error if an error is encountered.
These DB_Table_Database methods are simple wrappers that call the corresponding methods DB_Table methods internally. As one result, overriding any of these methods in a subclass of DB_Table in order to customize the behavior of a specific table will automatically modify the behavior of the DB_Table_Database method.
The DB_Table_Database data insert() and update() methods can validate foreign key values before actually modifying data in the database, and can emulate referentially triggered actions such cascading deletes, if foreign key validation and these referentially triggered actions are enabled. The corresponding methods of DB_Table will take identical actions if the DB_Table has been added to a parent DB_Table_Database object (i.e., if it contains a reference to a parent object), and if these actions are enabled in the parent DB_Table_Database object. Foreign key validation is disabled by default. Referentially triggered actions are enabled by default, for any such action that is declared in the database model.
The insert() and update() methods return a PEAR_Error object if foreign key validation fails, or if an error occurs during any database command. A PEAR_Error is also returned if a 'restrict' ON DELETE or ON UPDATE action is declared, when such actions are enabled, if an attempt is made to delete or update any row that is referenced by a foreign key of one or more rows of another table.
One way to maintain the state of DB_Table_Database between web pages is to serialize the entire database as one string, and save it in a session variable, a file, or a database. A serialized DB_Table_Database object contains serialized versions of all of its tables, and thus contains the information necessary to reconstruct the database. Serialization is accomplished by the PHP serialize function:
<?php
$db_serial = serialize($db);
?>
The following two commands are necessary to unserialize and restore the state of a DB_Table_Database object:
<?php
$db = unserialize($db_serial);
$db->setDBconnection($DB_object);
?>
where $DB_object is a DB or MDB2 connection object. The setDBconnection method sets the same database connection for the parent DB_Table_Database object and all of the child DB_Table objects.
When a DB_Table_Database object is unserialized, each child DB_Table object is unserialized in turn by the DB_Table_Database::__wakeup() method. If the DB_Table objects are instances of subclasses of DB_Table, this requires that the definitions of these subclasses exist in memory prior to unserialization of the table. This can be accomplished by explicitly including the file or files containing the required class definitions in the file containing the unserialize command, or by taking advantage of an auto-load mechanism that is built into the wake-up method.
In order for autoloading of subclass definitions to work, each of the subclasses must be defined in a separate file in a default directory, with a filename that is given by the class name with an added '.php' extension. If the definition of a required subclass of DB_Table named "classname" is found to not exist in memory when needed during unserialization, the __wakeup() method tries to include a file named "classname.php" in this directory.
For autoloading to work, the base of each such filename must be the class name obtained by applying the built-in get_class function to the object. This yields a lower case class name PHP 4 and preserves the capitalization used in the class definition in PHP 5.
setTableSubclassPath() - sets the path to the default directory for DB_Table subclass definitions.
Synopsis:
Parameter $path is the path to the desired directory, without a trailing directory separator. The path must be specified in the form required by a 'require_once" statement, with the current PHP settings.
The toXML() and fromXML() methods may be used to serialize a database schema to, and unserialize it from, an XML string, respectively. The fromXML() method uses simpleXML to parse the XML string, and so requires PHP 5. (This is the only method in the class that is not compatible with PHP 4).
The XML schema used by these methods is an extension of the current MDB2_Schema DTD, extended so as to allow specification of foreign key references. This extension for foreign keys has been agreed upon for adoption in a future release of MDB2_Schema.
The toXML() method returns an XML string for the entire database, including all of its tables and foreign key references, like so:
<?php
$xml_string = $db->toXML();
?>
The DB_Table_Database::fromXML() method is a static method that takes an MDB2 XML database schema string as its only parameter and returns a DB_Table_Database object containing all the tables and references in the database. The following pair of commands is necessary to create a DB_Table_Database object and connect it to a RDBMS
<?php
$db = DB_Table_Database::fromXML($xml_string);
$db->setDBconnection($DB_object);
?>
Here, as for unserialization, the setDBconnection() method is used to establish a connection be the new object and a database server, where $DB_object is a DB or MDB2 connection object. The tables of the DB_Table_Database object that is returned by fromXML() are all instances of DB_Table itself, rather than of custom subclasses of DB_Table. fromXML() returns a PEAR_Error if the XML string cannot be parsed, if an error is thrown during instantiation of either the DB_Table_Database object or any of the child DB_Table objects, or if called with a PHP 4 interpreter (it requires PHP 5).
Several methods of DB_Table_Database are used to set a common value of a DB_Table property for every child table in the database. These methods, which have the same names and interfaces as the corresponding DB_Table methods, are:
Each has a boolean argument that turns on (true) or off (false) one of the features of DB_Table. All of the relevant features affect data insertion and updating, and are implemented within the DB_Table insert() and update() methods. The DB_Table_Database insert() and update() methods simply call the corresponding DB_Table methods, so changes in these properties also change the behavior of the DB_Table_Database methods.
The autoValidInsert and autoValidUpdate methods turn on or off the automatic validation that data is of the expected type prior to insertion or updating of the data. The autoRecast method turns on or off the attempted recasting of data to the expected data type, if necessary, prior to insertion or updating. autoInc turns on or off the PHP implementation of auto-incrementation of the value of the $auto_inc_col column (if any) upon insertion. Note that, when the feature is on, this column is still auto-incremented only if its value is left null in the data to be inserted.
Most of the properties of DB_Table_Database are private. A get* method is defined for each private property. Please see the API documentation for a discussion of all of properties and associated get* methods.
The DB_Table_Generator class can generate the PHP code necessary to use the DB_Table package to interact with an existing database. It generates skeleton DB_Table subclass definitions for every table in the database, using table schemas that are obtained by querying the database. It can also generate a file containing the code required to connect to the database, and to create a parent DB_Table_Database object.
All code generated by a DB_Table_Generator object is written to a directory whose path is given by the $class_write_path property of that object. By default, this is the current directory. By default, the name of the class constructed for a table named 'thing' is Thing_Table. That is, the class name is the table name, with the first letter upper case, with an added suffix '_Table'. This suffix can be changed by setting the $class_suffix property. The name of the file containing a subclass definition is the subclass name with a PHP extension, e.g., 'Thing_Table.php'. The name of the object instantiated from that subclass is the same as the table name, with no suffix, e.g., 'thing'.
To generate the code for all of the tables in a database named $database, instantiate a DB or MDB2 object named $conn that connects to the database of interest, and execute the following code:
Code Generation for an Entire Database
<?php
require_once 'DB/Table/Generator.php';
// [snip] Instantiate DB or MDB2 object $conn
// Instantiate a Generator object
$generator = new DB_Table_Generator($conn, $database);
// Choose a directory for the generated code
$generator->class_write_path = '/var/www/html/app1/db_table' ;
// Generate DB_Table subclass definition files
$generator->generateTableClassFiles();
// Generate the 'Database.php' file
$generator->generateDatabaseFile();
?>
In the above example, '/var/www/html/app1/db_table' is the path (without a trailing directory separator) to a directory in which all of the code should be written. If this directory does not exist, it will be created (if possible). If the directory does already exist, existing files will not be overwritten. If $class_write_path is not set (i.e., if this line from the example is ommitted) all the code will be written to the current directory.
The generateTableClassFiles() method generates skeleton subclass definitions for a set of tables, with different subclass definitions in different files. If it is called with no argument, as above, it generates subclass definitions for all of the tables in the current database.
The generateDatabaseFile() method generates a file, named 'Database.php' by default, that contains all of the additional code necessary to connect to a database and to create a parent DB_Table_Database object. If the generateDatabaseFile() method is called, as in the above example, it must be called after the generateTableClassFiles(). The code in the 'Database.php' file includes (using the "require_once" command) each of the table subclass definition files, instantiates one object of each DB_Table subclass (creating one object per table), instantiates a parent DB_Table_Database object, adds all the tables to that parent, and attempts to guess foreign key relationships between tables based on the column names. This file will generally need to be edited so as to include information about the data source name (DSN), and any foreign key relationships that could not be guessed by the generator.
By default, generateTableClassFiles() and generateDatabaseFiles() generate code for all of the tables in the current database. To generate code for a specified list of tables, set the value of the public $tables property to a sequential list of table names before calling either of these methods. For example, code can be generated for three tables named 'table1', 'table2', and 'table3' as follows:
Code Generation for a Specified Set of Tables
<?php
require_once 'DB/Table/Generator.php'
// [snip] Instantiate DB or MDB2 object $conn
// Instantiate a Generator object
$generator = new DB_Table_Generator($conn, $database);
// Choose a directory for the generated code
$generator->class_write_path = '/var/www/html/app1/db_table' ;
// Define the set of tables
$generator->tables = array('table1', 'table2', 'table3');
// Generate DB_Table subclass definition files
$generator->generateTableClassFiles();
// Generate the 'Database.php' file
$generator->generateDatabaseFile();
?>
If the $tables property of the DB_Table_Generator object is not set to an array value before generateTableClassFiles() is called, then, by default, the generateTableClassFiles() method queries the database for an array of all table names (by calling the getTableNames() method internally), and the $table property is set equal to the resulting array of table names.
These are the supported data types in DB_Table:
DB_Table abstracts data types for you, so your data is always stored the same way, regardless of the database backend. In some cases, particularly with date, time, and timestamp, the native database format is ignored completely and data is stored as a fixed-length character string.
DB_Table does no support binary large objects (BLOBs), but character large objects (CLOBS) are available.
There are three sizes of integer columns:
The $col definition, you need only specify the column type; no size or scope is needed.
Integer column declaration
<?php
var $col = array(
// unique row ID
'fieldname' => array(
'type' => 'integer'
)
);
?>
To define a fixed-point column in DB_Table, use the 'decimal' datatype, and indicate both the 'size' (width) and 'scope' (number of decimal places). For example, to define a 5-digit number that has 2 decimal places:
Decimal column declaration
<?php
var $col = array(
// unique row ID
'fieldname' => array(
'type' => 'decimal',
'size' => 5,
'scope' => 2
),
);
?>
For the above example, standard SQL requires that the column be able to store any value with 5 digits and 2 decimals. In this case, therefore, the range of values that can be stored in the column is from -999.99 to 999.99. DB_Table attempts to enforce this behavior regardless of the RDBMS backend behavior.
To define a floating-point column in DB_Table, use the 'single' or 'double' datatype.
You need only specify the column type; no size or scope is needed.
Floating-point column declaration
<?php
var $col = array(
// unique row ID
'fieldname' => array(
'type' => 'double'
),
);
?>
A boolean value is a true/false (1 or 0) value. You need only specify the column type; no size or scope is needed.
Boolean column declaration
<?php
var $col = array(
// unique row ID
'fieldname' => array(
'type' => 'boolean'
)
);
?>
Boolean values are stored as fixed-point decimals of size 1, scope 0.
If the column is not required, a NULL value stored therein may be treated as a third value, which allows the boolean column to be treated as a ternary value instead of a binary value (i.e., NULL|0|1 instead of 0|1).
To define a fixed-length character string column, use the 'char' datatype, and indicate the exact size of the string.
To define a variable-length character string column, use the 'varchar' datatype, and indicate the maximum size of the string.
For example, to define a 64-character variable-length string:
Variable length string column declaration
<?php
var $col = array(
// unique row ID
'fieldname' => array(
'type' => 'varchar',
'size' => 64
)
);
?>
You must specify a 'size' element, but no scope is needed. The maximum size is 255 characters.
To define an ISO-standard date column, use the 'date' datatype.
Date column declaration
<?php
var $col = array(
// unique row ID
'fieldname' => array(
'type' => 'date'
)
);
?>
You need only specify the column type; no size or scope is needed.
Values for 'date' are always stored as 10-character strings, in the format "yyyy-mm-dd".
To define an ISO standard time with hour, minutes, and seconds, use the 'time' data type.
Time column declaration
<?php
var $col = array(
// unique row ID
'fieldname' => array(
'type' => 'time'
)
);
?>
You need only specify the column type; no size or scope is needed.
A 'time' value is always stored as an 8-character string, in the format "hh:ii:ss".
To define an ISO standard date-and-time column, use the 'timestamp' data type.
Timestamp column declaration
<?php
var $col = array(
// unique row ID
'fieldname' => array(
'type' => 'timestamp'
)
);
?>
You need only specify the column type; no size or scope is needed.
Values for 'timestamp' are always stored as an 19-character strings, in the format "yyyy-mm-dd hh:ii:ss" (24-hour clock).
Note: If you want to store a Unix timestamp, use the 'integer' datatype; Unix timestampes are 4-byte integers, which maps perfectly to the DB_Table 'integer' datatype.
CLOB Column Declaration
<?php
var $col = array(
// unique row ID
'fieldname' => array(
'type' => 'clob'
)
);
?>
You need only specify the column type; no size or scope is needed.
Values for 'clob' are always stored as the large possible native text type (e.g., LONGTEXT) or native CLOB type.
A unified API for accessing databases, based on user provided meta data.
To connect to a database through PEAR::MDB, you have to create a valid DSN - data source name. This DSN consists in the following parts:
phptype
:
Database backend used in PHP (i.e. mysql
, odbc
etc.)
dbsyntax
:
Database used with regards to SQL syntax etc.
protocol
:
Communication protocol to use ( i.e. tcp
,
unix
etc.)
hostspec
:
Host specification (hostname[:port]
)
database
:
Database to use on the DBMS server
username
:
User name for login
password
:
Password for login
proto_opts
:
Maybe used with protocol
The format of the supplied DSN is in its fullest form:
phptype(dbsyntax)://username:password@protocol+hostspec/database
Most variations are allowed:
phptype://username:password@protocol+hostspec:110//usr/db_file.mdb phptype://username:password@hostspec/database_name phptype://username:password@hostspec phptype://username@hostspec phptype://hostspec/database phptype://hostspec phptype(dbsyntax) phptype
The currently supported database backends are:
mysql -> MySQL pgsql -> PostgreSQL ibase -> InterBase mssql -> Microsoft SQL Server oci8 -> Oracle 7/8/8i fbsql -> FrontBase
With an up-to-date version of MDB, you can use a second DSN format
phptype(syntax)://user:pass@protocol(proto_opts)/database
Connect to database through a socket
mysql://user@unix(/path/to/socket)/pear
Connect to database on a non standard port
pgsql://user:pass@word@tcp(localhost:5555)/pear
Please note, that some features may be not supported by all database backends. Please refer to the PEAR MDB extensions status document located at:
<pear base dir>/MDB/STATUS
to get a detailed list about what features are supported by which backend.
To connect to a database you have to use the function MDB::connect() , which requires a valid DSN as parameter and optional a boolean value, which determines wether to use a persistent connection or not. In case of success you get a new instance of the database class. It is strongly recommended to check this return value with MDB::isError() . To disconnect use the method disconnect() from your database class instance.
Connect and disconnect
<?php
require_once 'MDB.php';
$user = 'foo';
$pass = 'bar';
$host = 'localhost';
$db_name = 'clients_db';
// Data Source Name: This is the universal connection string
$dsn = "mysql://$user:$pass@$host/$db_name";
// MDB::connect will return a PEAR MDB object on success
// or an PEAR MDB Error object on error
$db = MDB::connect($dsn);
// With MDB::isError you can differentiate between an error or
// a valid connection.
if (MDB::isError($db)) {
die ($db->getMessage());
}
....
// close conection
$db->disconnect();
?>
To perform a query against a database, you have to use the
function
query(), that takes the query string
as an argument. On failure you get a
MDB_Error object. Be sure to check it with
MDB::isError(). On success, you get
MDB_OK or when you set a
SELECT
-statement a result resource handle
A simple MDB query
<?php
// Once you have a valid MDB object...
$sql = "select * from clients";
$result = $db->query($sql);
// Always check that $result is not an error
if (MDB::isError($result)) {
die ($result->getMessage());
}
// Continue on with your script
?>
In order to fetch data from a result resource you can use one if the following methods: fetchInto() , fetchOne() . , fetchRow() . , fetchCol() . and fetchAll() . All above mentioned methods except fetchOne() return the requested data encapsuled into a (multi-dimensional-)array, NULL on no more data or a MDB_Error , when an error occurs. All method prefixed with fetch() automatically free the result set.
Fetching a result set
<?php
...
$db = MDB::connect($dsn);
$res = $db->query("SELECT * FROM mytable");
// Get each row of data on each iteration until
// there are no more rows
while ($row = $db->fetchInto($res)) {
$id = $row[0];
}
// If we are just interested in the first column of the first row
$id = $db->fetchOne($res);
// Since the fetch methods always free the result set
// we cannot loop across the result set but instead
// need to choose the proper fetch method
$data = $db->getAll($res);
foreach($data as $row)
{
$id = $row[0];
}
?>
The fetch modes supported are:
MDB_FETCHMODE_ORDERED (default)
The fetch*() returns an ordered array. The order is taken from the select statement.
Fetch a ordered array
<?php
$res = $db->query('SELECT id, name, email FROM users');
$row = $db->fetchRow($res, MDB_FETCHMODE_ORDERED);
/*
$row will contain:
array (
0 => <column "id" data>,
1 => <column "name" data>,
2 => <column "email" data>
)
*/
// Access the data with:
$id = $row[0];
$name = $row[1];
$email = $row[2];
?>
MDB_FETCHMODE_ASSOC
Returns an associative array with the column names as the array keys
Fetch a assoc. array
<?php
$res = $db->query('SELECT id, name, email FROM users');
$row = $db->fetchRow($res, MDB_FETCHMODE_ASSOC);
/*
$row will contain:
array (
'id' => <column "id" data>,
'name' => <column "name" data>,
'email' => <column "email" data>
)
*/
// Access the data with:
$id = $row['id'];
$name = $row['name'];
$email = $row['email'];
?>
You can set the fetch mode per result call or for your whole MDB instance.
Per call
<?php
while ($row = $db->fetchInto($res, MDB_FETCHMODE_ASSOC)) {
$id = $row['id'];
}
?>
Once per instance
<?php
$db = MDB::connect($dsn);
// this will set a default fetchmode for this Pear MDB instance
// (for all queries)
$db->setFetchMode(MDB_FETCHMODE_ASSOC);
$result = $db->query(...);
while ($row = $db->fetchRow($res)) {
$id = $row['id'];
}
?>
The PEAR MDB fetch system also supports an extra parameter to the fetch statement. So you can fetch rows from a result by number. This is especially helpful if you only want to show sets of an entire result (for example in building paginated HTML lists), fetch rows in an special order, etc.
Fetching by number
<?php
...
// the row to start fetching
$from = 50;
// how many results per page
$resPage = 10;
// the last row to fetch for this page
$to = $from + $resPage;
foreach (range($from, $to) as $rowNum) {
if (!$row = $db->fetchInto($res, $fetchmode, $rowNum)) {
break;
}
$id = $row[0];
....
}
?>
It is recommended to finish the result set after processing in order to to save memory. Use freeResult() to do this.
Freeing
<?php
...
$res = $db->query('SELECT * FROM clients');
while ($row = $res->fetchInto($res)) {
...
}
$db->freeResult($res);
?>
MDB provides some special ways to retrieve information from a query without the need of using fetch*() and loop throw results.
queryOne() retrieves the first result of the first column from a query
<?php
$numrows = $db->queryOne('select count(id) from clients');
?>
queryRow() returns the first row and returns it as an array.
<?php
$sql = 'select name, address, phone from clients where id=1';
if (is_array($row = $db->queryRow($sql))) {
list($name, $address, $phone) = $row;
}
?>
queryCol() returns an array with the data of the selected column. It accepts the column number to retrieve as the second parameter.
<?php
$all_client_names = $db->queryCol('SELECT name FROM clients');
?>
The above sentence could return for example:
<?php
$all_client_names = array('Stig', 'Jon', 'Colin');
?>
getAll() fetches all the rows returned from a query. This method also has some advanced parameters still will also enable you to return the data as an associative array using the first column as the key.
<?php
$data = getAll('SELECT id, text, date FROM mytable');
/*
Will return:
array(
1 => array('4', 'four', '2004'),
2 => array('5', 'five', '2005'),
3 => array('6', 'six', '2006')
)
*/
?>
The query*() family methods will do all the dirty job for you, this is: launch the query, fetch the data and free the result. Please note that as all PEAR MDB functions they will return a MDB_Error object on errors.
With MDB you have many ways to retrieve useful information from query results. These are:
numRows() : Returns the total number of rows returned from a "SELECT" query.
<?php
// Number of rows
echo $db->numRows($res);
?>
numCols() : Returns the total number of columns returned from a "SELECT" query.
<?php
// Number of cols
echo $db->numCols($res);
?>
affectedRows() : Returns the number of rows affected by a data manipulation query ("INSERT", "UPDATE" or "DELETE").
<?php
// remember that this statement won't return a result object
$db->query('DELETE * FROM clients');
echo 'I have deleted ' . $db->affectedRows() . ' clients';
?>
tableInfo() : Returns an associative array with information about the returned fields from a "SELECT" query.
<?php
// Table Info
print_r($db->tableInfo($res));
?>
Don't forget to check if the returned result from your action is a MDB_Error object. If you get a error message like "MDB_Error: database not capable", means that your database backend doesn't support this action.
Sequences are a way of offering unique IDs for data rows. If you do most of your work with e.g. MySQL, think of sequences as another way of doing AUTO_INCREMENT. It's quite simple, first you request an ID, and then you insert that value in the ID field of the new row you're creating. You can have more than one sequence for all your tables, just be sure that you always use the same sequence for any particular table. To get the value of this unique ID use nextId() , if a sequence doesn't exists, it will be created automaticlly.
Using a sequence
<?php
...
$id = $db->nextId('mySequence');
// Use the ID in your INSERT query
$res = $db->query("INSERT INTO myTable (id,text) VALUES ($id,'foo')");
...
?>
prepareQuery() and executeQuery*() give you more power and flexibilty for query execution. You can use them, if you have to do more than one equal query (i.e. adding a list of adresses to a database) or if you want to support different databases, which have different implementations of the SQL standard.
Imagine you want to support two databases with different INSERT syntax:
Corresponding to create multi-lingual scripts you can create a array with queries like this:
<?php
$statement['db1']['INSERT_PERSON'] = "INSERT INTO person ( surname, name, age ) VALUES ( ?, ?, ? )" ;
$statement['db2']['INSERT_PERSON'] = "INSERT INTO person SET surname=?, name=?, age=?" ;
?>
To use the features above, you have to do two steps. Step one is to prepareQuery the statement and the second is to executeQuery it.
Prepare() has to be called with the generic statement at least once. It returns a handle for the statement.
To create a generic statement is simple. Write the SQL query as usual, i.e.
Now check which parameters should be replaced while script runtime. Substitute this parameters with a placeholder.
So, thats all! Now you have a generic statement, required by prepareQuery() .
prepareQuery() can handle different types of placeholders or wildcards.
?
- (recommended) stands for a scalar
value like strings or numbers, the value will be quoted
depending of the database
!
- stands for a scalar value and
will inserted into the statement "as is".
&
- requires an existing filename,
the content of this file will be included into the statement
(i.e. for saving binary data of a graphic file in a database)
After preparing the statement, you can execute the query. This means to assign the variables to the prepared statement. To do this, executeQuery() requires two arguments, the statement handle of prepareQuery() and an array with the values to assign. The array has to be numerically ordered. The first entry of the array represents the first wildcard, the second the second wildcard etc. The order is independent from the used wildcard char.
Inserting data into a datebase
<?php
$alldata = array( array(1, 'one', 'en'),
array(2, 'two', 'to'),
array(3, 'three', 'tre'),
array(4, 'four', 'fire'));
$sth = $dbh->prepareQuery("INSERT INTO numbers VALUES(?,?,?)");
foreach ($alldata as $row) {
$dbh->executeQuery($sth, $row);
}
?>
In the example the query is done four times:
executeMultiple() works in the same way, but requires a two dimensional array. So you can avoid the explicit foreach in the eample above.
Using executeMultiple() instead of executeQuery()
<?php
...
$alldata = array( array(1, 'one', 'en'),
array(2, 'two', 'to'),
array(3, 'three', 'tre'),
array(4, 'four', 'fire'));
$sth = $dbh->prepareQuery("INSERT INTO numbers VALUES(?,?,?)");
$dbh->executeMultiple($sth, $alldata);
}
?>
The result is the same. If one of the records failed, the unfinished records will not be executed.
If executeQuery*() fails a MDB_Error, else MDB_OK will returned.
The main MDB class is simply a container class with some static methods for creating MDB objects.
object MDB::connect
(
string $dsn
, boolean
$options
= false
)
Creates a new MDB connection object and connect to the specified database
$dsn
Data Source Name. See the "DSN" section for further information.
$options
If $options
is TRUE the
connection will be persistent
(requires support by database driver).
Default is FALSE. In future releases, this parameter
will be an array
and take different options depending on the database.
object
- the created MDB connection object,
or a MDB_Error object on error.
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NOT_FOUND | NULL | The database specific class was not found. |
Check the $dsn and make sure
to have an complete installation of the
MDB-package and that you database
is supported by MDB.
|
This function should be called statically.
boolean MDB::isError
(
mixed $value
)
Checks whether a result code from a MDB method is a MDB_Error object or not.
$value
Variable to check
boolean
- TRUE, if
$value
is a MDB_Error object
This function should be called statically.
MDB_Common is an interface class; that provides all methods to query a specific database. An instance of a database specific class will be returned by the MDB::connect() method.
integer affectedRows
(
)
Number of affected rows by a query
integer
- number of rows or
MDB_Error when fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NOT_CAPABLE | NULL | Function is not supported by the database backend | Switch to another database system, if you really need this feature. |
This function can not be called statically.
integer createSequence
(
string $seq_name
,
integer $start
)
$seq_name
name of the new sequence to create
$start
starting value of the sequence
Error code | Error message | Reason | Solution |
---|---|---|---|
every error code | Database specific error | Check the name of the sequence. If correct, probably a bug in the sequence implementation |
This function can not be called statically.
resource currId
(
string $seq_name
)
$seq_name
name of the sequence
resource
- a free id or a
MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NOT_CAPABLE | NULL | Function is not supported by the database backend | Switch to another database system, if you really need this feature. |
MDB_ERROR_NOT_LOCKED | NULL | Locking of sequence table fails | Database specific, check documention of your database, |
MDB_ERROR_NOSUCHTABLE | NULL | Sequence table was not found | Try to create a new sequence or if you are sure, a sequence was already create, check database integrity |
This function can not be called statically.
boolean MDB::disconnect
(
)
Disconnects from a database
boolean
- Returns TRUE on success, FALSE on failure.
This function can not be called statically.
integer dropSequence
(
string $seqName
)
$seqName
name of the sequence to delete
Error code | Error message | Reason | Solution |
---|---|---|---|
every error code | Database specific error | Check the name of the sequence. If correct, probably a bug in the sequence implementation. |
This function can not be called statically.
mixed execute
(
resource $stmt
,
array $types
= null
,
array $params = array()
,
array $param_types
= null
)
execute() joins the prepared SQL statement from prepareQuery() with the given data and executes the SQL query.
$stmt
query handle from prepareQuery()
$types
if supplied, the types of the columns in the result set will be set for fetching
$params
a numeric array containing the data to insert into the query
$param_types
if supplied, the values in $param will automatically be set to the passed datatypes
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_INVALID | NULL | SQL statement handle is not valid. | Check correct processing of the SQL statement with prepareQuery() . Note that execute() requires a handle to the statement returned by prepareQuery() , not the statement itself. |
MDB_ERROR_NEED_MORE_DATA | NULL | To less data for filling the prepared SQL statement. |
Check the number of wild cards given in the SQL statement for
prepareQuery()
. Check the count of
entries in the array for $data . The count
of entries have to be equal to the number of wild cards.
|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
mixed executeQuery
(
resource $stmt
,
array $types
= null
)
executeQuery() joins the prepared SQL statement from prepareQuery() with the data that was set using one of the setParam() methods and executes the SQL query.
$stmt
query handle from prepareQuery()
$types
if supplied, the types of the columns in the result set will be set for fetching
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_INVALID | NULL | SQL statement handle is not valid. | Check correct processing of the SQL statement with prepareQuery() . Note that executeQuery() requires a handle to the statement returned by prepareQuery() , not the statement itself. |
MDB_ERROR_NEED_MORE_DATA | NULL | To less data for filling the prepared SQL statement. |
Check the number of wild cards given in the SQL statement for
prepareQuery()
. Check the count of
entries in the array for $data . The count
of entries have to be equal to the number of wild cards.
|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
See Introduction - Execute for general using and an example.
mixed executeMultiple
(
resource $stmt
,
array $types
= null
,
array $params = array()
,
array $param_types
= null
, array $data
)
executeMultiple() joins the prepared SQL statement
from
prepareQuery()
with the given data and does the
SQL query for every "row" in the
$data
array.
$stmt
query handle from prepareQuery()
$types
if supplied, the types of the columns in the result set will be set for fetching
$params
if supplied, prepareQuery()/ executeQuery() will be used with this array as execute parameters
$param_types
if supplied, the values in $param will automatically set to the passed datatypes
array $data
a numeric array containing the data to insert into the query
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_INVALID | NULL | SQL statement handle is not valid. | Check correct processing of the SQL statement with prepareQuery() . Note that executeMultiple() requires a handle to the statement returned by prepareQuery() , not the statement itself. |
MDB_ERROR_NEED_MORE_DATA | NULL | To less data for filling the prepared SQL statement. |
Check the number of wild cards given in the SQL statement for
prepareQuery()
. Check the count of
entries in the array for $data . The count
of entries have to be equal to the number of wild cards.
|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
If an error occurs during execution, the function will be stopped. Possible remaining data will be unprocessed.
See Introduction - Execute for general using and an example.
array &fetchAll
(
resource $result
, integer
$fetchmode
= MDB_FETCHMODE_DEFAULT
, boolean
$rekey
= false
, boolean
$force_array
= false
, boolean
$group
= false
)
Fetch the entire result set of a result set and return it into a nested array and free the result set.
$result
a valid resource returned by query() or executeQuery()
$fetchmode
the fetch mode to use
$rekey
if set to TRUE the array result be modified as follows: If the result set contains more than two columns, the value will be an array of the values from column 2 to n. If the result set contains only two columns, the returned value will be a scalar with the value of the second column (unless forced to an array with the $force_array parameter).
$force_array
used only if the query returns exactly two columns. If TRUE, the values of the returned array will be one-element arrays instead of scalars.
boolean $group
if TRUE, the values of the returned array is wrapped in another array. If the same key value (in the first column) repeats itself, the values will be appended to this array instead of overwriting the existing values.
array
- an nested array or a
MDB_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_TRUNCATED | NULL | The result set contains fewer then two columns | Check the SQL fetch or choose another fetch*() function |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() , executeQuery() , fetchRow() , fetchOne() , fetchCol()
array &fetchCol
(
resource $result
,
mixed
$colnum = 0
)
Fetch a single column from a result set of a result set and free the result set.
$result
a valid resource returned by query() or executeQuery()
$params
if supplied, prepareQuery()/ executeQuery() will be used with this array as execute parameters
$colnum
which column to return (integer [column number, starting at 0] or string [column name])
array
- the first row of results as an array
indexed from 0 or a MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_TRUNCATED | NULL | The result set contains fewer then two columns | Check the SQL query or choose another query*() function |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() , executeQuery() , fetchRow() , fetchOne() , fetchAll()
mixed &fetchOne
(
resource $result
)
Fetch the first column of the first row of data returned from a result set and free the result set.
mixed
- the returned value or
a MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() , executeQuery() , fetchRow() , fetchCol() , fetchCol()
array &fetchRow
(
resource $result
,
integer $fetchmode
= MDB_FETCHMODE_DEFAULT
,
integer $rownum
= null
)
Fetch the first row of data returned from a result set and free the result set.
$result
a valid resource returned by query() or executeQuery()
$fetchmode
the fetch mode to use, default is MDB_FETCHMODE_DEFAULT
$rownum
the row number to fetch
array
- the first row of results as an array
indexed from 0 or a MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() executeQuery() , fetchCol() , fetchRow()
mixed fetchInto (
resource
$result
, integer
$fetchMode
= MDB_FETCHMODE_DEFAULT
, integer
$rownum
= null
)
Fetch a row and return data in an array.
$result
result identifier
$fetchMode
format of fetched row
$rownum
the row number to fetch
mixed
- an array or NULL, if no
more rows
boolean freeResult (
resource $result
)
Deletes the result set and frees the memory occupied by the result set.
boolean
- Returns TRUE on success, FALSE on failure.
This function can not be called statically.
array &getAll
(
string $query
,
array $types
= null
,
array $params = array()
,
array $param_types
= null
,
integer $fetchmode
= MDB_FETCHMODE_DEFAULT
)
Fetch the entire result set of a query and return it into a nested array. The function takes care of doing the query and freeing the results when finished.
$query
the SQL query
$types
if supplied, the types of the columns in the result set will be set for fetching
$params
if supplied, prepareQuery()/ executeQuery() will be used with this array as execute parameters
$param_types
if supplied, the values in $param will automatically set to the passed datatypes
$fetchmode
the fetch mode to use, default is MDB_FETCHMODE_DEFAULT
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_INVALID | NULL | SQL statement for preparing is not valid. | See the prepareQuery() documentation, if you want to use a SQL statemt using wildcards. |
MDB_ERROR_NEED_MORE_DATA | NULL | To less data for filling the prepared SQL statement. |
Check the number of wild cards given in the SQL statement
prepareQuery()
. Check the count of
entries in the array for $params . The count
of entries have to be equal to the number of wild cards.
|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() executeQuery() , getCol() , getRow() , getAssoc() , getOne()
array &getAssoc
(
string $query
,
array $types
= null
,
array $params = array()
,
array $param_types
= null
, integer
$fetchmode
= MDB_FETCHMODE_DEFAULT
,
boolean $force_array
= false
, boolean
$group
= false
)
Fetch the entire result set of a query and return it as an associative array using the first column as the key. The function takes care of doing the query and freeing the results when finished. If the result set contains more than two columns, the value will be an array of the values from column 2 to n. If the result set contains only two columns, the returned value will be a scalar with the value of the second column (unless forced to an array with the $force_array parameter).
$query
the SQL query
$types
if supplied, the types of the columns in the result set will be set for fetching
$params
if supplied, prepareQuery()/ executeQuery() will be used with this array as execute parameters
$param_types
if supplied, the values in $param will automatically set to the passed datatypes
$fetchmode
the fetch mode to use
$force_array
used only if the query returns exactly two columns. If TRUE, the values of the returned array will be one-element arrays instead of scalars.
boolean $group
if TRUE, the values of the returned array is wrapped in another array. If the same key value (in the first column) repeats itself, the values will be appended to this array instead of overwriting the existing values.
array
- associative array with results from the
query.
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_INVALID | NULL | SQL statement for preparing is not valid. | See the prepareQuery() documentation, if you want to use a SQL statemt using wildcards. |
MDB_ERROR_NEED_MORE_DATA | NULL | To less data for filling the prepared SQL statement. |
Check the number of wild cards given in the SQL statement
prepareQuery()
. Check the count of
entries in the array for $params . The count
of entries have to be equal to the number of wild cards.
|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
MDB_ERROR_TRUNCATED | NULL | The result set contains fewer then two columns | Check the SQL query or choose another get*() function |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() , executeQuery() , getRow() , getOne() , getCol()
array &getCol
(
string $query
,
string $type
= null
,
array
$params = array()
,
array $param_types
= null
,
mixed
$colnum = 0
)
Fetch a single column from a result set of a query. The function takes care of doing the query and freeing the results when finished.
$query
the SQL query
$type
if supplied, the type of the column in the result set will be set for fetching
$params
if supplied, prepareQuery()/ executeQuery() will be used with this array as execute parameters
$param_types
if supplied, the values in $param will automatically set to the passed datatypes
$colnum
which column to return (integer [column number, starting at 0] or string [column name])
array
- the first row of results as an array
indexed from 0 or a MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_INVALID | NULL | SQL statement for preparing is not valid. | See the prepareQuery() documentation, if you want to use a SQL statemt using wildcards. |
MDB_ERROR_NEED_MORE_DATA | NULL | To less data for filling the prepared SQL statement. |
Check the number of wild cards given in the SQL statement
prepareQuery()
. Check the count of
entries in the array for $params . The count
of entries have to be equal to the number of wild cards.
|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
MDB_ERROR_TRUNCATED | NULL | The result set contains fewer then two columns | Check the SQL query or choose another get*() function |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() , executeQuery() , getRow() , getOne() , getAssoc()
mixed &getOne
(
string $query
,
string $type
= null
,
array
$params = array()
,
array $param_types
= null
)
Fetch the first column of the first row of data returned from a query. The function takes care of doing the query and freeing the results when finished.
$query
the SQL query or the statement to prepare
$type
if supplied, the type of the column in the result set will be set for fetching
$params
if supplied, prepareQuery()/ executeQuery() will be used with this array as execute parameters
$param_types
if supplied, the values in $param will automatically set to the passed datatypes
mixed
- the returned value or
a MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_INVALID | NULL | SQL statement for preparing is not valid. | See the prepareQuery() documentation, if you want to use a SQL statemt using wildcards. |
MDB_ERROR_NEED_MORE_DATA | NULL | To less data for filling the prepared SQL statement. |
Check the number of wild cards given in the SQL statement
prepareQuery()
. Check the count of
entries in the array for $params . The count
of entries have to be equal to the number of wild cards.
|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() , executeQuery() , getRow() , getCol() , getCol() , getCol()
array &getRow
(
string $query
,
array $types
= null
,
array $params = array()
,
array $param_types
= null
, integer
$fetchmode
= MDB_FETCHMODE_DEFAULT
)
Fetch the first row of data returned from a query. The function takes care of doing the query and freeing the results when finished.
$query
the SQL query
$types
if supplied, the types of the columns in the result set will be set for fetching
$params
if supplied, prepareQuery()/ executeQuery() will be used with this array as execute parameters
$param_types
if supplied, the values in $param will automatically set to the passed datatypes
$fetchmode
the fetch mode to use, default is MDB_FETCHMODE_DEFAULT
array
- the first row of results as an array
indexed from 0 or a MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_INVALID | NULL | SQL statement for preparing is not valid. | See the prepareQuery() documentation, if you want to use a SQL statemt using wildcards. |
MDB_ERROR_NEED_MORE_DATA | NULL | To less data for filling the prepared SQL statement. |
Check the number of wild cards given in the SQL statement
prepareQuery()
. Check the count of
entries in the array for $params . The count
of entries have to be equal to the number of wild cards.
|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() executeQuery() , getCol() , getRow() , getAssoc()
string MDB::getTextValue
(
string $string
)
Quotes a string database-dependent, so it can be safely used in a query
$string
the input string to quote
string
- the quoted string.
This function can not be called statically.
mixed &limitQuery
(
string $query
,
array
$types
, integer $from
, integer $count
)
Executes a SQL query, but fetches only the specificed count of rows. It is an emulation of the MySQL LIMIT option.
$query
the SQL query
$types
if supplied, the types of the columns in the result set will be set for fetching
$from
the row to start to fetch
$count
the numbers of rows to fetch
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
This module is EXPERIMENTAL. That means that the behaviour of these functions, these function names, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this module at your own risk.
Depending on the database you will not really get more speed compared to query() . The advantage of limitQuery() is the deleting of unneeded rows in the resultset, as early as possible. So this can decrease memory usage.
resource nextId
(
string $seq_name
, boolean
$onDemand
= true
)
$seq_name
name of the sequence
$onDemand
when TRUE the sequence is automatic created, if it not exists.
resource
- a free id or a
MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NOT_CAPABLE | NULL | Function is not supported by the database backend | Switch to another database system, if you really need this feature. |
MDB_ERROR_NOT_LOCKED | NULL | Locking of sequence table fails | Database specific, check documention of your database, |
MDB_ERROR_NOSUCHTABLE | NULL | Sequence table was not found | Try to create a new sequence or if you are sure, a sequence was already create, check database integrity |
This function can not be called statically.
integer numCols
(
resource $result
)
Get the number of columns of the rows in a result set.
integer
- number of columns
This function can not be called statically.
integer numRows (
resource $result
)
Get the number of rows in a result set.
integer
- number of rows
This function can not be called statically.
resource prepareQuery
(
string $query
)
Prepares a query for execution with executeQuery() .
$query
the query to prepareQuery
resource
- the query handle
This function can not be called statically.
See Introduction - Execute for general using and an example.
mixed &query
(
string $query
,
array
$types
)
Executes a SQL query
$query
the SQL query
$types
if supplied, the types of the columns in the result set will be set for fetching
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
array &queryAll
(
string $query
,
array $types
= null
, integer
$fetchmode
= MDB_FETCHMODE_DEFAULT
, boolean
$rekey
= false
, boolean
$force_array
= false
, boolean
$group
= false
)
Fetch the entire result set of a query and return it into a nested array. The function takes care of doing the query and freeing the results when finished.
$query
the SQL query
$types
if supplied, the types of the columns in the result set will be set for fetching
$fetchmode
the fetch mode to use
$rekey
if set to TRUE the array result be modified as follows: If the result set contains more than two columns, the value will be an array of the values from column 2 to n. If the result set contains only two columns, the returned value will be a scalar with the value of the second column (unless forced to an array with the $force_array parameter).
$force_array
used only if the query returns exactly two columns. If TRUE, the values of the returned array will be one-element arrays instead of scalars.
boolean $group
if TRUE, the values of the returned array is wrapped in another array. If the same key value (in the first column) repeats itself, the values will be appended to this array instead of overwriting the existing values.
array
- an nested array or a
MDB_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
MDB_ERROR_TRUNCATED | NULL | The result set contains fewer then two columns | Check the SQL query or choose another query*() function |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() , executeQuery() , queryRow() , queryOne() , queryCol()
array &queryCol
(
string $query
,
string $type
= null
,
mixed
$colnum = 0
)
Fetch a single column from a result set of a query. The function takes care of doing the query and freeing the results when finished.
$query
the SQL query
$type
if supplied, the type of the column in the result set will be set for fetching
$params
if supplied, prepareQuery()/ executeQuery() will be used with this array as execute parameters
$param_types
if supplied, the values in $param will automatically set to the passed datatypes
$colnum
which column to return (integer [column number, starting at 0] or string [column name])
array
- the first row of results as an array
indexed from 0 or a MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
MDB_ERROR_TRUNCATED | NULL | The result set contains fewer then two columns | Check the SQL query or choose another query*() function |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() , executeQuery() , queryRow() , queryOne() , queryAll()
mixed &queryOne
(
string $query
,
string $type
= null
)
Fetch the first column of the first row of data returned from a query. The function takes care of doing the query and freeing the results when finished.
$query
the SQL query or the statement to prepare
$type
if supplied, the type of the cell in the result set will be set for fetching
mixed
- the returned value or
a MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() , executeQuery() , queryRow() , queryCol() , queryCol()
array &queryRow
(
string $query
,
array $types
= null
,
integer $fetchmode
= MDB_FETCHMODE_DEFAULT
,
integer $rownum
= null
)
Fetch the first row of data returned from a query. The function takes care of doing the query and freeing the results when finished.
$query
the SQL query
$types
if supplied, the types of the columns in the result set will be set for fetching
$fetchmode
the fetch mode to use, default is MDB_FETCHMODE_DEFAULT
$rownum
the row number to fetch
array
- the first row of results as an array
indexed from 0 or a MDB_Error, if fail
Error code | Error message | Reason | Solution |
---|---|---|---|
MDB_ERROR_NO_DB_SELECTED | NULL | No database was chosen. | Check the DSN in connect() . |
every other error code | Database specific error | Check the database related section of PHP-Manual to detect the reason for this error. In the most cases a misformed SQL statement. Ie. using LIMIT in a SQL-Statement for an Oracle database. |
This function can not be called statically.
query() , limitQuery() , prepareQuery() executeQuery() , queryCol() , queryRow()
void setFetchMode
(
integer $fetchmode
, string
$object_class
= null
)
Sets the fetch mode used by default on queries for the connection.
$fetchmode
MDB_FETCHMODE_ORDEREDor MDB_FETCHMODE_ASSOC, all possibly bit-wise OR'ed with MDB_FETCHMODE_FLIPPED. See "Introduction - Fetch" for further information.
void
- NULL, if ok(!)
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
invalid fetchmode mode
|
The given fetch mode does not exists or is not implement in your MDB_Error version. | Check writing of the argument and your used version of MDB. |
This function can not be called statically.
boolean tableInfo (
resource $result
, int $mode
= null
)
Fetch table information from result
query.
Returns an associative array of table and field information.
This function is currently not documented.
This function can not be called statically.
In case of failure, most of the MDB functions return a MDB_Error object which contains information about the error. MDB_Error offers the same functions as PEAR_Error.
A unified API for accessing databases and constructing SQL in a portable way.
PEAR MDB2 is a merge of the PEAR DB and Metabase php database abstraction layers.
It provides a common API for all support RDBMS. The main difference to most other database abstraction packages is that MDB2 goes much further to ensure portability. Among other things MDB2 features:
Currently supported RDBMS:
When you install PEAR MDB2, you only get the base classes, but you also need to install the driver of the appropriate DBMS to have a working installation. For instance, to install the MySQL and the PostgreSQL drivers, you have to run these commands:
Still having problems? Check the F.A.Q.
To connect to a database through PEAR::MDB2, you have to create a valid DSN - data source name. This DSN consists in the following parts:
phptype
:
Database backend used in PHP (i.e. mysql
, pgsql
etc.)
dbsyntax
:
Database used with regards to SQL syntax etc.
protocol
:
Communication protocol to use ( i.e. tcp
,
unix
etc.)
hostspec
:
Host specification (hostname[:port]
)
database
:
Database to use on the DBMS server
username
:
User name for login
password
:
Password for login
proto_opts
:
Maybe used with protocol
option
:
Additional connection options in URI query string format.
options get separated by &.
The Following table shows a non complete list of options:
Name | Description | Type |
---|---|---|
charset |
Some backends support setting the client charset. (Invokes setCharset(string $charset, [resource $connection = null]) | string |
new_link [boolean] |
Some RDBMS do not create new connections when connecting to the same host multiple times. If this option is set to TRUE it will attempt to force a new connection. | boolean |
The DSN can either be provided as an associative array or as a string. The array format is preferred, since it doesn't require a further parsing step (see the Connecting chapter for an example). The string format of the supplied DSN is in its fullest form:
phptype(dbsyntax)://username:password@protocol+hostspec/database?option=value
Most variations are allowed:
phptype://username:password@protocol+hostspec:110//usr/db_file.db phptype://username:password@hostspec/database phptype://username:password@hostspec phptype://username@hostspec phptype://hostspec/database phptype://hostspec phptype:///database phptype:///database?option=value&anotheroption=anothervalue phptype(dbsyntax) phptype
The currently supported database backends are:
fbsql -> FrontBase ibase -> InterBase / Firebird (requires PHP 5) mssql -> Microsoft SQL Server (NOT for Sybase. Compile PHP --with-mssql) mysql -> MySQL mysqli -> MySQL (supports new authentication protocol) (requires PHP 5) oci8 -> Oracle 7/8/9/10 pgsql -> PostgreSQL querysim -> QuerySim sqlite -> SQLite 2
A second DSN format is supported
phptype(syntax)://user:pass@protocol(proto_opts)/database
If your database
, option
values,
username
or password
contain characters used to delineate DSN parts, you
can escape them via URI hex encodings:
: = %3a / = %2f @ = %40 + = %2b ( = %28 ) = %29 ? = %3f = = %3d & = %26
Please note, that some features may be not supported by all database backends.
Connect to database through a socket
mysql://user@unix(/path/to/socket)/pear
Connect to database on a non standard port
pgsql://user:pass@tcp(localhost:5555)/pear
Connect to SQLite on a Unix machine using options
sqlite:////full/unix/path/to/file.db?mode=0666
Connect to SQLite on a Windows machine using options
sqlite:///c:/full/windows/path/to/file.db?mode=0666
Connect to MySQLi using SSL
mysqli://user:pass@localhost/pear?key=client-key.pem&cert=client-cert.pem
Connect to Oracle using Service name
oci8://username:password@foo.example.com[:port]/?service=service
Connect to Oracle using "Easy Connect" syntax
username/password@[//]host[:port][/service_name]
To instantiate a database object you have several methods available using MDB2.
Function | Summary | Description |
---|---|---|
factory() | Efficient | Will instantiate a new MDB2_Driver_Common instance, but will not connect to the database until required. This will delay making the actual connection. This is called lazy connecting. Using this makes sense if it is possible that due to caching inside the application no connection will ever need to be established. |
connect() | Eager | Will instantiate a new MDB2_Driver_Common instance, and will establish a database connection immediately. This way any connection issues will immediately raise an error. |
singleton() | Available | Returns a MDB2_Driver_Common instance. A new MDB2_Driver_Common object is only created once using factory(), subsequent calls to singleton will return a reference to the existing object. This method is preferred over declaring your database object as a global. |
To connect to a database you have to use the function factory(), connect() or singleton(), which require a valid DSN as the first parameter. This parameter can either be a string or an array. If using an array, the array used gets merged with the default information:
$dsn = array( 'phptype' => false, 'dbsyntax' => false, 'username' => false, 'password' => false, 'protocol' => false, 'hostspec' => false, 'port' => false, 'socket' => false, 'database' => false, 'new_link' => false, 'service' => false, // only in oci8 );
Any elements you set override the defaults and the remainder stay at their defaults.
The second parameter is the optional $options
array that can contain runtime configuration settings for this package.
Name | Type | Description |
---|---|---|
ssl |
boolean | determines if ssl should be used for connections |
field_case |
integer | CASE_LOWER|CASE_UPPER: determines what case to force on field/table names |
disable_query |
boolean | determines if queries should be executed |
result_class |
string | class used for result sets |
buffered_result_class |
string | class used for buffered result sets, default is MDB2_Result_Common |
result_wrap_class |
string | class used to wrap result sets into, default is MDB2_Result_Common |
result_buffering |
boolean | should results be buffered or not? |
fetch_class |
string | class to use when fetch mode object is used |
persistent |
boolean | persistent connection? |
debug |
integer | numeric debug level |
debug_handler |
string | function/method that captures debug messages |
debug_expanded_output |
boolean | BC option to determine if more context information should be send to the debug handler |
default_text_field_length |
integer | default text field length to use |
lob_buffer_length |
integer | LOB buffer length |
log_line_break |
string | line-break format |
idxname_format |
string | pattern with '%s' for index name |
seqname_format |
string | pattern with '%s' for sequence name |
savepoint_format |
string | pattern with '%s' for auto generated savepoint names |
seqcol_name |
string | sequence column name |
quote_identifier |
boolean | if identifier quoting should be done when check_option is used |
use_transactions |
boolean | if transaction use should be enabled |
decimal_places |
integer | number of decimal places to handle |
portability |
integer | portability constant |
modules |
array | short to long module name mapping for __call() |
emulate_prepared |
boolean | force prepared statements to be emulated |
datatype_map |
array | map user defined datatypes to other primitive datatypes |
datatype_map_callback |
array | callback function/method that should be called |
In case of success you get a new instance of the database class. It is strongly recommended to check this return value with PEAR::isError() (will detect PEAR_Error or any subclass) or the MDB2_Driver_Common specific isError().
To disconnect use the method disconnect() from your database class instance.
Connect and disconnect
<?php
require_once 'MDB2.php';
$dsn = 'pgsql://someuser:apasswd@localhost/thedb';
$options = array(
'debug' => 2,
'result_buffering' => false,
);
$mdb2 =& MDB2::factory($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// ...
$mdb2->disconnect();
?>
Connect using an array for the DSN information
<?php
require_once 'MDB2.php';
$dsn = array(
'phptype' => 'pgsql',
'username' => 'someuser',
'password' => 'apasswd',
'hostspec' => 'localhost',
'database' => 'thedb',
);
$options = array(
'debug' => 2,
'portability' => MDB2_PORTABILITY_ALL,
);
// uses MDB2::factory() to create the instance
// and also attempts to connect to the host
$mdb2 =& MDB2::connect($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
?>
When connecting to SQLite using a DSN array, the value
of the mode
element must be a string:
<?php
$dsn = array(
'phptype' => 'sqlite',
'database' => 'thedb',
'mode' => '0644',
);
?>
Connect to MySQLi via SSL using an array for the DSN information
The ssl
element of the
$options
array must be set to
TRUE in order for SSL to work. Each of the extra
elements in the $dsn
array
(key
through cipher
in the example below) are optional.
<?php
require_once 'MDB2.php';
$dsn = array(
'phptype' => 'mysqli',
'username' => 'someuser',
'password' => 'apasswd',
'hostspec' => 'localhost',
'database' => 'thedb',
'key' => 'client-key.pem',
'cert' => 'client-cert.pem',
'ca' => 'cacert.pem',
'capath' => '/path/to/ca/dir',
'cipher' => 'AES',
);
$options = array(
'ssl' => true,
);
// gets an existing instance with the same DSN
// otherwise create a new instance using MDB2::factory()
$mdb2 =& MDB2::singleton($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
?>
Connect to a PostgreSQL database via a socket
<?php
require_once 'MDB2.php';
$dsn = 'pgsql://someuser:apasswd@unix(/tmp)/thedb';
$options = array(
'debug' => 2,
'portability' => MDB2_PORTABILITY_ALL,
);
$mdb2 =& MDB2::factory($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
?>
Connect using singleton
<?php
require_once 'MDB2.php';
$dsn = 'pgsql://someuser:apasswd@localhost/thedb';
$mdb2 =& MDB2::singleton($dsn);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
$blah =& new blah();
// is able to use the existing database connection
$blah->foo();
$mdb2->disconnect();
class blah
{
function foo() {
// get a reference to the existing database object
$mdb2 =& MDB2::singleton();
// ...
}
}
?>
PEAR MDB2 provides several methods for querying databases. The most direct method is query(). It takes a SQL query string as an argument. There are two possible returns: A new MDB2_Result object for queries that return results (such as SELECT queries), or a MDB2_Error object on failure. It should not be used with statements that manipulate data (such as INSERT queries)
Doing a query
<?php
// Create a valid MDB2 object named $mdb2
// at the beginning of your program...
require_once 'MDB2.php';
$mdb2 =& MDB2::connect('pgsql://usr:pw@localhost/dbnam');
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// Proceed with a query...
$res =& $mdb2->query('SELECT * FROM clients');
// Always check that result is not an error
if (PEAR::isError($res)) {
die($res->getMessage());
}
// Disconnect
$mdb2->disconnect();
?>
exec() should be used for manipulation queries. There are two possible returns: An integer denoting the number of affected rows for statements that manipulate data (such as INSERT queries), or a MDB2_Error object on failure. It should not be used with statements that return results (such as SELECT queries)
Using exec to manipulate data
<?php
// Once you have a valid MDB2 object named $mdb2...
$sql = "INSERT INTO clients (name, address) VALUES ($name, $address)";
$affected =& $mdb2->exec($sql);
// Always check that result is not an error
if (PEAR::isError($affected)) {
die($affected->getMessage());
}
?>
MDB2 supports a number of data types across all drivers. These can be set for result sets at query or prepare time or using the setResultTypes() method. You can find an overview of the supported data types and their format here.
In order to read/write to only a limited number of rows from a result set and/or to start reading from a specific offset the setLimit() can be called prior to issueing the query. The limit and offset will only affected the next method call that will issue a query or prepare a statement and will automatically be reset after issueing the query. This also applies to any internal queries issues inside MDB2. Note that limit may not work with DML statements on RDBMS that emulate limit support and no error will be raised.
Using setLimit with query and exec
<?php
// Once you have a valid MDB2 object named $mdb2...
$sql = "SELECT * FROM clients";
// read 20 rows with an offset of 10
$mdb2->setLimit(20, 10);
$affected =& $mdb2->exec($sql);
$sql = "DELETE FROM clients";
if ($mdb2->supports('limit_queries') === 'emulated') {
echo 'offset will likely be ignored'
}
// only delete 10 rows
$mdb2->setLimit(10);
$affected =& $mdb2->exec($sql);
?>
MDB2 provides a quote() method to quote a value into a DBMS specific format that is suitable to compose query statements. It has four parameters (only the first one is required): the value to be quoted, its datatype, whether or not to quote the value, and whether or not to escape the wildcards in the value. If you don't provide the datatype, it will be guessed from the value.
Doing a query with quoted values
<?php
// Create a valid MDB2 object named $mdb2
// at the beginning of your program...
require_once 'MDB2.php';
$mdb2 =& MDB2::connect('pgsql://usr:pw@localhost/dbnam');
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// some sample values to be inserted
$id = 1;
$name = 'sample item';
$time = date('Y-m-d H:i:s');
// Proceed with a query...
$query = 'INSERT INTO tablename (id, itemname, saved_time) VALUES ('
. $mdb2->quote($id, 'integer') .', '
. $mdb2->quote($name, 'text') .', '
. $mdb2->quote($time, 'timestamp') .')';
$res =& $mdb2->exec($query);
?>
With the third parameter of the quote() you can specify whether or not the above fields should be individually quoted:
Individually choose the values to be quoted
<?php
$query = 'INSERT INTO sometable (textfield1, boolfield2, datefield3) VALUES ('
.$mdb2->quote($val1, "text", true).', '
.$mdb2->quote($val2, "boolean", false).', '
.$mdb2->quote($val3, "date", true).')';
?>
The above example will quote the fields and the resulting SQL will look as such:
INSERT INTO sometable FIELDS (textfield1, boolfield2, datefield3) VALUES ('blah', 1, '2006-02-21')
where the values defined were the values inserted accordingly. You will notice that the "boolfield2" is unquoted as we specified FALSE in the quote() method.
NB: If you use prepared statements, then quoting will be done automatically, you don't need to do it yourself.
You can quote the db identifiers (table and field names) with quoteIdentifier(). The delimiting style depends on which database driver is being used. NOTE: just because you CAN use delimited identifiers, it doesn't mean you SHOULD use them. In general, they end up causing way more problems than they solve. Anyway, it may be necessary when you have a reserved word as a field name (in this case, we suggest you to change it, if you can). Also, don't use quoteIdentifier() if you have a period in the table name itself (which, BTW, is a really bad idea), since it will consider it as a schema.table pair.
Some of the internal MDB2 methods generate queries. Enabling the quote_identifier
option of MDB2 you can tell MDB2 to quote the identifiers in these generated
queries. For all user supplied queries this option is irrelevant.
Portability is broken by using the following characters inside delimited identifiers:
backtick (`
) -- due to MySQL
double quote (") -- due to Oracle
brackets ([ or ]) -- due to Access
Delimited identifiers are known to generally work correctly under the following drivers:
mssql
mysql
mysqli
oci8
pgsql
sqlite
Firebird/InterBase doesn't seem to be able to use delimited identifiers via PHP 4. They work fine under PHP 5.
Within the MDB2 API there are a number of options to set the quoting options, one of which simply quotes the identifiers within the abstraction, the other quotes the field values on insert/update etc. when using the prepared statements methods.
When using the quote_identifier
option, all of the
field identifiers will be automatically quoted in the resulting SQL statements:
<?php
$mdb2->setOption('quote_identifier', true);
?>
will result in a SQL statement that all the field names are quoted with the
backtick '`
' operator (in MySQL).
SELECT * FROM `sometable` WHERE `id` = '123';
as opposed to:
SELECT * FROM sometable WHERE id='123';
If you want to escape a value, without surrounding it with quotes, you can use the escape() method. If you also want to escape the wildcards (_ and %), set the second parameter to TRUE
If you just want to escape the wildcards in a value, you can use the escapePattern() method.
All DBMS provide multiple choice of data types for the information that can be stored in their database table fields. However, the set of data types made available varies from DBMS to DBMS.
To simplify the interface with the DBMS supported by MDB2, it was defined a base set of data types that applications may access independently of the underlying DBMS.
The MDB2 applications programming interface takes care of mapping data types when managing database options. It is also able to convert that is sent to and received from the underlying DBMS using the respective driver.
The following data type examples should be used with MDB2's createTable() method. The example array at the end of the data types section may be used with createTable() to create a portable table on the DBMS of choice (please refer to the main MDB2 documentation to find out what DBMS back ends are properly supported). It should also be noted that the following examples do not cover the creation and maintenance of indices, this chapter is only concerned with data types and the proper usage thereof.
Within the MDB2 API there are a few modifiers that have been designed to aid in optimal table design. These are:
The notnull
modifiers
The length
modifiers
The default
modifiers
unsigned
modifiers for some field definitions,
although not all DBMS's support this modifier for integer field types.
fixed
length modifiers for some field definitions.
Building upon the above, we can say that the modifiers alter the field definition
to create more specific field types for specific usage scenarios.
The notnull
modifier will be used in the following way
to set the default DBMS NOT NULL Flag on the field to true or false, depending
on the DBMS's definition of the field value:
In PostgreSQL the "NOT NULL" definition will be set to "NOT NULL", whilst in
MySQL (for example) the "NULL" option will be set to "NO".
In order to define a "NOT NULL" field type, we simply add an extra parameter
to our definition array (See the examples in the following section)
<?php
'sometime' = array(
'type' = 'time',
'default' = '12:34:05',
'notnull' = true,
),
?>
Using the above example, we can also explore the default
field operator. Default is set in the same way as the notnull
operator to set a default value for the field. This value may be set in any
character set that the DBMS supports for text fields, and any other valid
data for the field's data type. In the above example, we have specified a
valid time for the "Time" data type, '12:34:05'.
Remember that when setting default dates and times, as well as datetimes,
you should research and stay within the epoch of your chosen DBMS, otherwise
you will encounter difficult to diagnose errors!
Example of the length
modifier
<?php
'sometext' = array(
'type' = 'text',
'length' = 12,
),
?>
The above example will create a character varying field of length 12 characters in the database table. If the length definition is left out, MDB2 will create a length of the maximum allowable length for the data type specified, which may create a problem with some field types and indexing. Best practice is to define lengths for all or most of your fields.
The text data type is available with two options for the length: one that is explicitly length limited and another of undefined length that should be as large as the database allows.
The length limited option is the most recommended for efficiency reasons. The undefined length option allows very large fields but may prevent the use of indexes, nullability and may not allow sorting on fields of its type.
The fields of this type should be able to handle 8 bit characters. Drivers take care of DBMS specific escaping of characters of special meaning with the values of the strings to be converted to this type.
By default MDB2 will use variable length character types. If fixed length types should be used can be controlled via the fixed modifier.
Example of text data type with length
and fixed
option
<?php
'sometext' = array(
'type' => 'text',
'length' => 12,
'fixed' => 'fixed',
),
?>
The boolean data type represents only two values that can be either 1 or 0. Do not assume that these data types are stored as integers because some DBMS drivers may implement this type with single character text fields for a matter of efficiency. Ternary logic is possible by using null as the third possible value that may be assigned to fields of this type.
Example of boolean data type
<?php
'someboolean' = array(
'type' => 'boolean',
),
?>
The integer data type may store integer values as large as each DBMS may handle. Fields of this type may be created optionally as unsigned integers but not all DBMS support it. Therefore, such option may be ignored. Truly portable applications should not rely on the availability of this option.
Example of integer data type
<?php
'someint' = array(
'type' => 'integer',
'length' => 10,
'unsigned' => true,
),
?>
The decimal data type may store decimal numbers accurately with a fixed number of decimal places. This data type is suitable for representing accurate values like currency amounts.
Some DBMS drivers may emulate the decimal data type using integers. Such drivers need to know in advance how many decimal places that should be used to perform eventual scale conversion when storing and retrieving values from a database. Despite this, applications may use arithmetic expressions and functions with the values stored on decimal type fields as long as any constant values that are used in the expressions are also converted with the respective MDB2 conversion functions.
The number of places that are used to the left and the right of the decimal point is pre-determined and fixed for all decimal values stored in the same database. By default, MDB2 uses 2 places to the right of the decimal point, but this may be changed when setting the database connection. The number of places available to the right of the decimal point depend on the DBMS.
It is not recommended to change the number places used to represent decimal values in database after it is installed. MDB2 does not keep track of changes in the number of decimal places. The number of decimal places can be set using the setOption() method.
Example of decimal data type
<?php
'somedecimal' = array(
'type' => 'decimal',
),
?>
The float data type may store floating point decimal numbers. This data type is suitable for representing numbers within a large scale range that do not require high accuracy. The scale and the precision limits of the values that may be stored in a database depends on the DBMS that it is used.
Example of float data type
<?php
'somefloat' = array(
'type' => 'float',
),
?>
The date data type may represent dates with year, month and day. DBMS independent representation of dates is accomplished by using text strings formatted according to the IS0-8601 standard.
The format defined by the ISO-8601 standard for dates is
YYYY-MM-DD
where YYYY
is the number of
the year (Gregorian calendar), MM
is the number of the
month from 01 to 12 and DD
is the number of the day from
01 to 31. Months or days numbered below 10 should be padded on the left with 0.
Some DBMS have native support for date formats, but for others the DBMS driver may have to represent them as integers or text values. In any case, it is always possible to make comparisons between date values as well sort query results by fields of this type.
Example of date data type
<?php
'somedate' = array(
'type' => 'date',
),
?>
The time data type may represent the time of a given moment of the day. DBMS independent representation of the time of the day is also accomplished by using text strings formatted according to the ISO-8601 standard.
The format defined by the ISO-8601 standard for the time
of the day is HH:MI:SS
where HH
is the
number of hour the day from 00 to 23 and MI
and
SS
are respectively the number of the minute and of the
second from 00 to 59. Hours, minutes and seconds numbered below 10 should be
padded on the left with 0.
Some DBMS have native support for time of the day formats, but for others the DBMS driver may have to represent them as integers or text values. In any case, it is always possible to make comparisons between time values as well sort query results by fields of this type.
Example of time data type
<?php
'sometime' = array(
'type' => 'time',
'default' => '12:34:05',
'notnull' => true,
),
?>
The timestamp data type is a mere combination of the date and the time of
the day data types. The representation of values of the time stamp type is
accomplished by joining the date and time string values in a single string
joined by a space. Therefore, the format template is YYYY-MM-DD HH:MI:SS
.
The represented values obey the same rules and ranges described for the
date and time data types
Example of timestamp data type
<?php
'sometimestamp' = array(
'type' => 'timestamp',
),
?>
The large object data types are meant to store data of undefined length that may be too large to store in text fields, like data that is usually stored in files.
MDB2 supports two types of large object fields: Character Large OBjects (CLOBs) and Binary Large OBjects (BLOBs). CLOB fields are meant to store only data made of printable ASCII characters. BLOB fields are meant to store all types of data.
Large object fields are usually not meant to be used as parameters of query
search clause (WHERE
) unless the underlying DBMS supports
a feature usually known as "full text search"
Example of large object data types
<?php
'someblob' = array(
'type' => 'blob',
),
// or
'someclob' = array(
'type' => 'clob',
),
?>
Example of field definition
<?php
$fields = array(
'id' => array(
'type' => 'text',
'length' => 32,
'fixed' => true,
),
'someint' => array(
'type' => 'integer',
'length' => 10,
'unsigned' => true,
),
'sometext' => array(
'type' => 'text',
'length' => 12,
),
'somedate' => array(
'type' => 'date',
),
'sometimestamp' => array(
'type' => 'timestamp',
),
'someboolean' => array(
'type' => 'boolean',
),
'somedecimal' => array(
'type' => 'decimal',
),
'somefloat' => array(
'type' => 'float',
),
'sometime' => array(
'type' => 'time',
'default' => '12:34:05',
'notnull' => true,
),
'somedate' => array(
'type' => 'date',
),
'someclob' => array(
'type' => 'clob',
),
'someblob' => array(
'type' => 'blob',
),
);
?>
The above example will create a database table as such:
Column | Type | Not Null | Default | comment |
---|---|---|---|---|
id | character(32) | |||
someint | bigint | |||
sometext | character varying(12) | |||
somedate | date | |||
sometimestamp | timestamp without time zone | |||
someboolean | boolean | |||
somedecimal | numeric(18,2) | |||
somefloat | double precision | |||
sometime | time without time zone | NOT NULL | '12:34:05'::time without time zone | |
someclob | text | |||
someblob | bytea |
Field | Type | Collation | Attributes | Null | Default | comment |
---|---|---|---|---|---|---|
id | char(32) | YES | ||||
someint | bigint(20) unsigned | |||||
sometext | varchar(12) | latin1_swedish_ci | YES | |||
somedate | date | YES | ||||
sometimestamp | timestamp without time zone | YES | ||||
someboolean | tinyint(1) | YES | ||||
somedecimal | decimal(18,2) | YES | ||||
somefloat | double | YES | ||||
sometime | time | NO | 12:34:05 | |||
someclob | longtext | latin1_swedish_ci | YES | |||
someblob | longblob | binary | YES |
Custom data types can be defined. This will be explored soon. For now you can refer to these blog posts:
Further reading should be done at the following URL's: getTypeDeclaration, getDeclaration.
The MDB2_Result_Common object provides two methods for fetching data from rows of a result set: fetchOne(), fetchRow(), fetchCol() and fetchAll().
fetchRow() and fetchOne() read an entire row or a single field from a column respectively. The result pointer gets moved to the next row each time these methods are called. NULL is returned when the end of the result set is reached.
fetchAll() and fetchCol() read all rows in the result set and therefore move the result pointer to the end. While fetchAll() reads the entire row data, fetchCol() only reads a single column.
MDB2_Error is returned if an error is encountered.
Fetching a result set
<?php
// Create a valid MDB2 object named $mdb2
// at the beginning of your program...
require_once 'MDB2.php';
$mdb2 =& MDB2::connect('pgsql://usr:pw@localhost/dbnam');
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// Proceed with getting some data...
$res =& $mdb2->query('SELECT * FROM mytable');
// Get each row of data on each iteration until
// there are no more rows
while (($row = $res->fetchRow())) {
// Assuming MDB2's default fetchmode is MDB2_FETCHMODE_ORDERED
echo $row[0] . "\n";
}
// while (($one = $res->fetchOne())) {
// echo $one . "\n";
// }
?>
The data from the row of a query result can be placed into one of three constructs: an ordered array (with column numbers as keys), an associative array (with column names as keys) or an object (with column names as properties).
MDB2_FETCHMODE_ORDERED (default)
Array
(
[0] => 28
[1] => hi
)
MDB2_FETCHMODE_ASSOC
Array
(
[a] => 28
[b] => hi
)
MDB2_FETCHMODE_OBJECT
stdClass Object
(
[a] => 28
[b] => hi
)
NOTE: When a query contains the same column name more than once (such as when joining tables which have duplicate column names) and the fetch mode is MDB2_FETCHMODE_ASSOC or MDB2_FETCHMODE_OBJECT, the data from the last column with a given name will be the one returned. There are two immediate options to work around this issue:
People.Name AS PersonName
TIP: If you are running into this issue, it likely indicates poor planning of the database schema. Either data is needlessly being duplicated or the same names are being used for different kinds of data.
You can set the fetch mode each time you call a fetch method and/or you can set the default fetch mode for the whole MDB2 instance by using the setFetchMode() method.
Determining fetch mode per call
<?php
// Once you have a valid MDB2 object named $mdb2...
$res =& $mdb2->query('SELECT * FROM users');
while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) {
echo $row['id'] . "\n";
}
?>
Changing default fetch mode
<?php
// Once you have a valid MDB2 object named $mdb2...
$mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);
$res =& $mdb2->query('SELECT * FROM users');
while ($row = $res->fetchRow()) {
echo $row['id'] . "\n";
}
?>
The PEAR MDB2 fetch system also supports an extra parameter to the fetch statement. So you can fetch rows from a result by number. This is especially helpful if you only want to show sets of an entire result (for example in building paginated HTML lists), fetch rows in an special order, etc.
Fetching by number
<?php
// Once you have a valid MDB2_Result object named $res...
// the row to start fetching
$from = 50;
// how many results per page
$resPage = 10;
// the last row to fetch for this page
$to = $from + $resPage;
foreach (range($from, $to) as $rowNum) {
if (!($row = $res->fetchRow(MDB2_FETCHMODE_ORDERED, $rowNum))) {
break;
}
echo $row[0] . "\n";
}
?>
The MDB2_Result_Common object provides several methods to read entire results sets: fetchCol() and fetchAll().
Once you finish using a result set, if your script continues for a while, it's a good idea to save memory by freeing the result set via Use free().
Freeing
<?php
// Once you have a valid MDB2 object named $mdb2...
$res =& $mdb2->query('SELECT name, address FROM clients');
while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) {
echo $row['name'] . ', ' . $row['address'] . "\n";
}
$res->free();
?>
If whatever data you need to read from a result set is not yet implemented in MDB2 you can get the native result resource using the getResource() method and then call the underlying PHP extension directly (though this would of course require that it is now up to you to make this sufficiently portable).
Native result resource
<?php
// Once you have a valid MDB2 object named $mdb2...
$res =& $mdb2->query('SELECT name, address FROM clients');
$native_result = $res->getResource();
?>
With MDB2 there are four ways to retrieve useful information about the query result sets themselves:
numRows() tells how many rows are in a SELECT query result
<?php
// Once you have a valid MDB2 object named $mdb2...
$res =& $mdb2->query('SELECT * FROM phptest');
if ($mdb2->getOption('result_buffering')) {
echo $res->numRows();
} else {
echo 'cannot get number of rows in the result set when "result_buffering" is disabled';
}
?>
numCols() tells how many columns are in a SELECT query result
<?php
// Once you have a valid MDB2 object named $mdb2...
$res =& $mdb2->query('SELECT * FROM phptest');
echo $res->numCols();
?>
rowCount() tells which row number the internal result row pointer currently points to
<?php
// Once you have a valid MDB2 object named $mdb2...
$res =& $mdb2->query('SELECT * FROM phptest');
$res->fetchRow();
// returns 2 if the result set contains at least 2 rows
echo $res->rowCount();
?>
getColumnNames() returns an associative array with the names of the column of the result set as keys and the position inside the result set as the values
<?php
// Once you have a valid MDB2 object named $mdb2...
$res =& $mdb2->query('SELECT * FROM phptest');
print_r($res->getColumnNames());
?>
seek() allows to seek to a specific row inside a result set. Note that seeking to previously read rows is only possible if the 'result_buffering' option is left enabled, otherwise only forward seeking is supported.
<?php
// Once you have a valid MDB2 object named $mdb2...
$res =& $mdb2->query('SELECT * FROM phptest');
// Seek to the 10th row in the result set
$res->seek(10);
?>
nextResult() allows iterate over multiple results returned by a multi query.
<?php
// Once you have a valid MDB2 object named $mdb2...
$multi_query = $this->db->setOption('multi_query', true);
// check if multi_query can be enabled
if (!PEAR::isError($multi_query)) {
$res =& $mdb2->query('SELECT * FROM phptest; SELECT * FROM phptest2;');
$data1 = $res->fetchAll();
// move result pointer to the next result
$res->nextResult();
$data2 = $res->fetchAll();
} else {
echo 'multi_query option is not supported';
}
?>
Another nextResult() example with a Stored Procedure returning multiple result sets.
<?php
$result = $db->executeStoredProcedure('procedureName', array('argument1'));
do {
while ($row = $result->fetchRow(MDB2_FETCHMODE_OBJECT)) {
var_dump($row);
}
} while ($result->nextResult());
$result->free();
?>
bindColumn() allows to bind a reference to a user variable to a specific field inside the result set. This means that when fetching the next row, this variable is automatically updated.
<?php
// Once you have a valid MDB2 object named $mdb2...
$name = $address = null;
$res =& $mdb2->query('SELECT id, name, address FROM clients', array('id' => 'integer'));
$res->bindColumn('id', $id);
// provide a type for the column not included in the query() call
$res->bindColumn('name', $name, 'text');
// but specifying a type is as always optional in MDB2
$res->bindColumn('address', $address);
while ($res->fetchRow()) {
echo "The address of '$name' (user id '$id') is '$address'\n";
}
?>
All of the fetch methods are also available in a variant that executes a query directly: queryOne(), queryRow(), queryCol() and queryAll().
<?php
// Once you have a valid MDB2 object named $mdb2...
$data = $mdb2->queryAll('SELECT * FROM phptest');
print_r($data);
?>
Users that prefer to use prepared statements can make use of the following methods from the Extended module: getOne(), getRow(), getCol(), getAll() and getAssoc().
<?php
// Once you have a valid MDB2 object named $mdb2...
$mdb2->loadModule('Extended');
$query = 'SELECT * FROM phptest WHERE id = ?';
$data = $mdb2->extended->getRow($query, null, array(1), array('integer'));
print_r($data);
?>
MDB2 supports a number of data types across all drivers. These can be set for result sets at query or prepare time or using the setResultTypes() method. You can find an overview of the supported data types and their format here.
To retrieve a Large Object (BLOB or CLOB), you can use streams, as you were reading a file
Fetching LOBs with streams.
<?php
$result =& $mdb2->query('SELECT document, picture FROM files WHERE id = 1', array('clob', 'blob'));
if (PEAR::isError($result) || !$result->valid()) {
//uh-oh
}
$row = $result->fetchRow();
//fetch the Character LOB into the $clob_value variable
$clob = $row[0];
if (!PEAR::isError($clob) && is_resource($clob)) {
$clob_value = '';
//use streams
while (!feof($clob)) {
$clob_value .= fread($clob, 8192);
}
$mdb2->datatype->destroyLOB($clob);
}
//fetch the Binary LOB into the $blob_value variable
$blob = $row[1];
if (!PEAR::isError($blob) && is_resource($blob)) {
$blob_value = '';
while (!feof($blob)) {
$blob_value.= fread($blob, 8192);
}
$mdb2->datatype->destroyLOB($blob);
}
//free the result
$result->free();
?>
Don't forget to use isError() to check if your actions return a MDB2_Error object.
prepare() and execute() give you more power and flexibilty for query execution. Prepare/execute mode is helpful when you have to run the same query several times but with different values in it, such as adding a list of addresses into a database.
Another place prepare/execute is useful is supporting databases which have different SQL syntaxes. Imagine you want to support two databases with different INSERT syntax:
Corresponding to create multi-lingual scripts you can create a array with queries like this:
<?php
$statement['db1']['INSERT_PERSON'] = 'INSERT INTO person
(surname, name, age) VALUES (?, ?, ?)';
$statement['db2']['INSERT_PERSON'] = 'INSERT INTO person
SET surname=?, name=?, age=?';
?>
Furthermore it is also possible to use named placeholders as inspired by Oracle. Using named placeholders also allows using the same placeholder name multiple times inside a single statement:
<?php
$statement['db1']['INSERT_PERSON'] = 'INSERT INTO person
(surname, name, age) VALUES (:surname, :lastname, :age)';
$statement['db2']['INSERT_PERSON'] = 'INSERT INTO person
SET surname=:surname, name=:lastname, age=:age';
?>
NB: we don't recommend using non-standard SQL syntax. The example above is just meant to show what you can do with prepared statements, but if you use MDB2 because you heart portability, then be sure you're using a standard SQL syntax (hint: the db1 INSERT is correct).
To use the features above, you have to do two steps. Step one is to prepare the statement which returns an instance of the MDB2_Statement_Common class. The second step is to execute it.
To start out, you need to prepare() a generic SQL statement. Create a generic statement by writing the SQL query as usual:
SELECT surname, name, age FROM person WHERE name = 'name_to_find' AND age < age_limit
Then substitute "placeholders" for the literal values which will be provided at run time:
SELECT surname, name, age FROM person WHERE name = ? AND age < ?
Then pass this SQL statement to prepare(), which returns a statement class instance to be used when calling execute().
prepare() can handle different types of placeholders (a.k.a. wildcards). By default all placeholders are handled as strings. However passing an array of data types as the second parameter makes it possible to set a specific type for each placeholder.
Since DML (data manipulation language - INSERT, UPDATE, DELETE) statements have different return values than data fetches the prepare() accepts a third parameter. This parameter should be set to MDB2_PREPARE_MANIP for DML statements (this way the number of affected rows will be returned). For data reads it should either be set to MDB2_PREPARE_RESULT, an array of data types for each of the columns in the result set or NULL in order to automatically detect the data types in the result set.
After preparing the statement, you can execute the query. This means to assign the variables to the prepared statement. To do this, execute() requires one argument a scalar or array with the values to assign.
Passing scalars to execute()
<?php
// Once you have a valid MDB2 object named $mdb2...
$sth = $mdb2->prepare('INSERT INTO numbers (number) VALUES (?)', array('integer'), MDB2_PREPARE_MANIP);
$sth->execute(1);
$sth->execute(8);
?>
When a prepared statement has multiple placeholders, you must use an array to pass the values to execute(). The first entry of the array represents the first placeholder, the second the second placeholder, etc. The order is independent of the type of placeholder used.
Passing an array to execute()
<?php
// Once you have a valid MDB2 object named $mdb2...
$types = array('integer', 'text', 'text');
$sth = $mdb2->prepare('INSERT INTO numbers VALUES (?, ?, ?)', $types, MDB2_PREPARE_MANIP);
$data = array(1, 'one', 'en');
$affectedRows = $sth->execute($data);
?>
When using named placeholders the data array needs to be an associative array with the keys matching the placeholder names.
Passing an array to execute()
<?php
// Once you have a valid MDB2 object named $mdb2...
$types = array('integer', 'text', 'text');
$sth = $mdb2->prepare('INSERT INTO numbers VALUES (:id, :name, :lang)', $types);
$data = array('id' => 1, 'name' => 'one', 'lang' => 'en');
$affectedRows = $sth->execute($data);
?>
When using named placeholders the data array needs to be an associative array with the keys matching the placeholder names.
Passing an array to execute()
<?php
// Once you have a valid MDB2 object named $mdb2...
$sth = $mdb2->prepare('SELECT name, lang FROM numbers WHERE id = ?', array('integer'), array('text', 'text'));
$result = $sth->execute(1);
?>
The values passed in
$data
must be literals. Do not submit SQL functions (for exampleCURDATE()
). SQL functions that should be performed at execution time need to be put in the prepared statement. Similarly, identifiers (i.e. table names and column names) can not be used because the names get validated during the prepare phase.
MDB2 contains a process for executing several queries at once. So, rather than having to execute them manually, like this:
Passing arrays to execute()
<?php
// Once you have a valid MDB2 object named $mdb2...
$alldata = array(array(1, 'one', 'en'),
array(2, 'two', 'to'),
array(3, 'three', 'tre'),
array(4, 'four', 'fire'));
$sth = $mdb2->prepare('INSERT INTO numbers VALUES (?, ?, ?)');
foreach ($alldata as $row) {
$sth->execute($row);
}
?>
which would issue four queries:
INSERT INTO numbers VALUES ('1', 'one', 'en') INSERT INTO numbers VALUES ('2', 'two', 'to') INSERT INTO numbers VALUES ('3', 'three', 'tre') INSERT INTO numbers VALUES ('4', 'four', 'fire')
you can use executeMultiple() to avoid the explicit foreach in the example above:
Using executeMultiple() from the Extended Module instead of execute()
<?php
// Once you have a valid MDB2 object named $mdb2...
$mdb2->loadModule('Extended', null, false);
$alldata = array(array(1, 'one', 'en'),
array(2, 'two', 'to'),
array(3, 'three', 'tre'),
array(4, 'four', 'fire'));
$sth = $mdb2->prepare('INSERT INTO numbers VALUES (?, ?, ?)');
$mdb2->extended->executeMultiple($sth, $alldata);
?>
The result is the same. If one of the records failed, the unfinished records will not be executed.
execute() has three possible returns: a new MDB2_Result_Common object for queries that return results (such as SELECT queries), integer for queries that manipulate data (such as INSERT queries) or a MDB2_Error object on failure
MDB2 supports a number of data types across all drivers. These can be set for result sets at query or prepare time or using the setResultTypes() method. You can find an overview of the supported data types and their format here.
Once you finish using prepared statements, if your script continues for a while, it's a good idea to save memory by freeing the prepared statement set via Use free().
Freeing
<?php
// Once you have a valid MDB2 object named $mdb2...
$sth = $mdb2->prepare('SELECT name, lang FROM numbers WHERE id = ?', array('integer'), array('text', 'text'));
$result = $sth->execute(1);
$sth->free();
?>
In order to read/write to only a limited number of rows from a result set and/or to start reading from a specific offset, the setLimit() can be called prior to calling prepare(). The limit and offset will only affect the next method call that will issue a query or prepare a statement and will automatically be reset after issuing the query. This also applies to any internal queries issued inside MDB2. Note that limit may not work with DML statements on RDBMS that emulate limit support and no error will be raised.
Using setLimit with prepare
<?php
// Once you have a valid MDB2 object named $mdb2...
// read 20 rows with an offset of 10
$mdb2->setLimit(20, 10);
$sth = $mdb2->prepare('SELECT name, lang FROM numbers WHERE group_id = ?', array('integer'), array('text', 'text'));
?>
PEAR MDB2 defaults to auto-committing all queries. However using the beginTransaction() method one can open a new transaction and with the commit() and rollback() methods, a transaction is finished. These three methods optionally accept a string name of a savepoint to set, release or rollback to respectively. The method inTransaction() may be used to check if a transaction is currently open.
Doing a transaction
<?php
// Create a valid MDB2 object named $mdb2
// at the beginning of your program...
require_once 'MDB2.php';
$mdb2 =& MDB2::connect('pgsql://usr:pw@localhost/dbnam');
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// check if transaction are supported by this driver
if (!$mdb2->supports('transactions')) {
exit();
}
// Open a transaction
$res = $mdb2->beginTransaction();
..
// check if we are inside a transaction and if savepoints are supported
if ($mdb2->inTransaction() && $mdb2->supports('savepoints')) {
// Set a savepoint
$savepoint = 'MYSAVEPOINT';
$res = $mdb2->beginTransaction($savepoint);
..
// determine if the savepoint should be released or to rollback to the savepoint
if ($error_condition) {
$res = $mdb2->rollback($savepoint);
} else {
$res = $mdb2->commit($savepoint);
}
}
..
// determine if the commit or rollback
if ($error_condition) {
$res = $mdb2->rollback();
} else {
$res = $mdb2->commit();
}
?>
PEAR MDB2 does not emulate transactions or savepoints. This means that it depends on the underlying RDBMS (and in the case of MySQL the storage engines used) if transactions will be available in MDB2. Also note that some RDBMS implicitly commit transactions when executing DDL statements - notable exceptions are Oracle and PostgreSQL.
MDB2 also supports "nested" transactions using the beginNestedTransaction() method. Actually these are not true nested transactions as they are natively supported in Interbase for example. MDB2 maintains a counter of opened nested transactions. The transaction is finished once that counter is decremented back to 1 with completeNestedTransaction() calls. If the RDBMS supports savepoints then MDB2 will automatically set a savepoint on every call of the beginNestedTransaction() method after the initial call and will return the name. These savepoints are automatically released by the completeNestedTransaction() method. The name of these automatic savepoints are determined by the "savepoint_format" option and the nested transaction counter. The "savepoint_format" defaults to 'MDB2_SAVEPOINT_%s'.
If, after initially opening a nested transaction, an unexpected PEAR error is raised on the MDB2 instance the transaction is rolled back, otherwise it is commited at this point. Using the getNestedTransactionError() method it is possible to check if there has been an error inside the transaction. Alternatively a rollback can be forced using the failNestedTransaction(). This method optionally accepts a mixed parameter which will set the error to return if the getNestedTransactionError() method is called, as well as a second boolean parameter that optionally forces an immidiate rollback.
Using emulated nested transactions
<?php
$mdb2->beginNestedTransaction(); # open transaction
$query = "INSERT INTO autoinc (id) VALUES (?)";
$stmt = $mdb2->prepare($query);
$stmt->execute(array(1));
$savepoint = $mdb2->beginNestedTransaction(); # ignored / sets savepoint
..
// never true for this example
if (false) {
// raise an error
$error = $mdb2->raiseError(MDB2_ERROR, null, null, 'kaboom');
$mdb2->failNestedTransaction();
}
if(($error = $mdb2->getNestedTransactionError())) {
die($error->getUserinfo());
}
$mdb2->completeNestedTransaction(); # ignored / releases savepoint
$stmt->execute(array(2));
$mdb2->completeNestedTransaction(); # commit
?>
Finally MDB2 supports setting the transaction isolation level as per the SQL 92 standard using the setTransactionIsolation() method. If a given RDBMS does not support a given isolation level but supports a higher more strict isolation level, then MDB2 silently uses that higher transaction level. Some RDBMS systems support additional options which are silently ignored if they are not supported.
Setting the transaction isolation level
<?php
$options = array('wait' => 'WAIT', 'rw' => 'READ WRITE');
$options = array('wait' => 'NO WAIT', 'rw' => 'READ ONLY');
$isolation_level = READ UNCOMMITTED # (allows dirty reads)
$isolation_level = READ COMMITTED # (prevents dirty reads)
$isolation_level = REPEATABLE READ # (prevents nonrepeatable reads)
$isolation_level = SERIALIZABLE # (prevents phantom reads)
$mdb2->setTransactionIsolation($isolation_level, $options);
?>
MDB2 follows a modular concept to provide functionality beyond the basic ability to send queries to the database and fetch result sets. Currently the following modules are available:
A module is loaded using the loadModule() method. This method returns the module instance, but also stores the instance in a property. The name of the property is either the lowercased name of the module passed in as the first parameter, or optionally the non null value of the second parameter. The optional third parameter is used to differentiate modules that depend on a specific RDBMS (like the Datatype module) and those that do not (like the Extended module). The method can also be used to load custom modules that are installed.
The third parameter is automatically detected if it is not set. On hosts that have 'safe_mode' enabled automatic detection does however require silenced falls to fopen(). Error handling and error handlers should be configured accordingly.
Loading a module
<?php
require_once 'MDB2.php';
$dsn = 'pgsql://someuser:apasswd@localhost/thedb';
$options = array(
'debug' => 2,
'result_buffering' => false,
);
$mdb2 =& MDB2::connect($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// ...
$mdb2->loadModule('Manager');
// specifically stating that the module that is being loaded is RDBMS independent
// this works around some needless internal calls
$mdb2->loadModule('Extended', null, false);
?>
Loading a custom module that is RDBMS independent
<?php
// ...
// file must reside in [peardir]/MDB2/MyModule.php
class MDB2_MyModule extends MDB2_Module_Common
{
function myMethod()
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
...
}
}
?>
<?php
// ...
// file must reside in [peardir]/MDB2/MyModule.php
$mdb2->loadModule('MyModule');
?>
Loading a custom module that is RDBMS dependent
<?php
// ...
// file must reside in [peardir]/MDB2/Driver/MyRDBMSModule/pgsql.php
// this is the class that would get loaded for an MDB2 PostgreSQL instance
// equivalent classes for other backends would need to implemented,
// potentially making use of a common base class
class MDB2_Driver_MyRDBMSModule_pgsql extends MDB2_Module_Common
{
function myRDBMSMethod()
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
...
}
}
?>
<?php
// ...
// file must reside in [peardir]/MDB2/Driver/MyRDBMSModule/[phptype].php
$mdb2->loadModule('MyRDBMSModule');
?>
Using a loaded module
<?php
// ...
// loading into default name
$mdb2->loadModule('Manager');
$tables = $mdb2->manager->listTables();
// loading into non standard property $foo
$mdb2->loadModule('Function', 'foo');
$tables = $mdb2->foo->concat($str1, $str2);
?>
On PHP5 users can also rely on overloading to load and call modules.
Using the 'modules' option with PHP5 overloading
<?php
require_once 'MDB2.php';
$dsn = 'pgsql://someuser:apasswd@localhost/thedb';
$options = array(
'debug' => 2,
'result_buffering' => false,
);
$mdb2 =& MDB2::connect($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// ...
$module_shorthands = $mdb2->getOptions('modules');
// use the shorthand key for the given module as a prefix for the method name
// where the first letter of the original method name is uppercased
$tables = $mdb2->mgListTables();
?>
Calling a method on a loaded module with PHP5 overloading
<?php
require_once 'MDB2.php';
$dsn = 'pgsql://someuser:apasswd@localhost/thedb';
$options = array(
'debug' => 2,
'result_buffering' => false,
);
$mdb2 =& MDB2::connect($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// ...
$mdb2->loadModule('Manager');
// since the manager module is already loaded we can call the listTable() method
$tables = $mdb2->manager->listTables();
// NB: on PHP5, where __autoload() is available,
// the above line can be rewritten as:
$tables = $mdb2->listTables();
?>
The Function module provides methods for executing non-standard SQL database functions in a consistent way. The following document lists the available methods, providing examples of their use. To include the Function module functionality, you need to load it first.
Loading the Function module
<?php
require_once 'MDB2.php';
$dsn = 'pgsql://someuser:apasswd@somehost';
$mdb2 =& MDB2::factory($dsn);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// loading the Function module
$mdb2->loadModule('Function');
?>
After including the module, you can access its methods like this:
Get the length of a string expression
<?php
// PHP5
$mdb2->length('expression');
// PHP4 and PHP5
$mdb2->function->length('expression');
?>
Further in the document the PHP5-compatible way will be used.
<?php
$mdb2->concat('string1', 'string2');
?>
Supposing we have the following Stored Procedure (MySQL syntax):
DELIMITER // CREATE PROCEDURE procedure1 (IN parameter1 INTEGER) BEGIN DECLARE variable1 CHAR(10); IF parameter1 = 17 THEN SET variable1 = 'birds'; ELSE SET variable1 = 'beasts'; END IF; INSERT INTO table1 VALUES (variable1); END // DELIMITER ;
we can call it this way:
Execute a Stored Procedure
<?php
$params = array(17);
$mdb2->executeStoredProc('procedure1', $params);
?>
The Manager module provides methods for managing database structure. The methods can be grouped based on their responsibility: create, edit (alter or update), list or delete (drop) database elements. The following document lists the available methods, providing examples of their use. The following tables will be created, altered and finally dropped, in a database named "events_db":
To include the Manager module functionality, you need to load it first.
Loading the Manager module
<?php
require_once 'MDB2.php';
$dsn = 'pgsql://someuser:apasswd@somehost';
$mdb2 =& MDB2::factory($dsn);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// loading the Manager module
$mdb2->loadModule('Manager');
?>
After including the module, you can access its methods like this:
Creating a database
<?php
// PHP5
$mdb2->createDatabase('events_db');
// PHP4 and PHP5
$mdb2->manager->createDatabase('events_db');
?>
Further in the document the PHP5-compatible way will be used.
These are methods to create new databases, tables, indices, constraints and sequences.
Creating and setting an events_db database
<?php
// MDB2 setup
require_once 'MDB2.php';
$dsn = 'mysql://root:test@localhost'; // note that DB name is omitted
$mdb2 =& MDB2::factory($dsn);
// loading the Manager module
$mdb2->loadModule('Manager');
// create the database
$mdb2->createDatabase('events_db');
// set the new database to work with it
$mdb2->setDatabase('events_db');
// the next time the DSN could be like
// mysql://root:test@localhost/events_db
?>
Now that the database is created, we can proceed with adding some tables. The method createTable() takes three parameters: the table name, an array of field definition and some extra options (optional and RDBMS-specific).
Creating the events table
<?php
$definition = array (
'id' => array (
'type' => 'integer',
'unsigned' => 1,
'notnull' => 1,
'default' => 0,
),
'name' => array (
'type' => 'text',
'length' => 255
),
'datetime' => array (
'type' => 'timestamp'
)
);
$mdb2->createTable('events', $definition);
?>
The keys of the definition array are the names of the fields in the table. The values are arrays containing the required key 'type' as well as other keys, depending on the value of 'type'. The values for the 'type' key are the same as the possible MDB2 datatypes. Depending on the datatype, the other options may vary.
Datatype | length | default | not null | unsigned |
---|---|---|---|---|
text | x | x | x | |
boolean | x | x | ||
integer | x | x | x | |
decimal | x | x | ||
float | x | x | ||
timestamp | x | x | ||
time | x | x | ||
date | x | x | ||
clob | x | x | ||
blob | x | x |
The third parameter to createTable() contains extra options for the table, such as the charset, collation, and other DBMS-specific properties, like MySQL's table engine. Here's an example for MySQL.
Creating the people table
<?php
$table_options = array(
'comment' => 'Repository of people',
'charset' => 'utf8',
'collate' => 'utf8_unicode_ci',
'type' => 'innodb',
);
$definition = array (
'id' => array (
'type' => 'integer',
'unsigned' => 1,
'notnull' => 1,
'default' => 0,
),
'name' => array (
'type' => 'text',
'length' => 255
)
);
$mdb2->createTable('people', $definition, $table_options);
?>
To round up the example database, here's the event_participants table creation code.
Creating the event_participants table
<?php
$definition = array (
'event_id' => array (
'type' => 'integer',
'unsigned' => 1,
'notnull' => 1,
'default' => 0,
),
'person_id' => array (
'type' => 'integer',
'unsigned' => 1,
'notnull' => 1,
'default' => 0,
),
);
$mdb2->createTable('event_participants', $definition, $table_options);
?>
In the example events table, the id should be a primary key. Creating a primary key is a task done by the createConstraint() method. It takes three parameters: the table name, the constraint name and the definition array.
The full structure of the definition
array looks like this
(in this case, it's representing a FOREIGN KEY constraint):
<?php
$definition = array (
'primary' => false,
'unique' => false,
'foreign' => true,
'check' => false,
'fields' => array (
'field1name' => array(), // one entry per each field covered
'field2name' => array(), // by the index
'field3name' => array(
'sorting' => ascending|descending,
'position' => 3,
),
)
'references' => array(
'table' => name,
'fields' => array(
'field1name' => array( //one entry per each referenced field
'position' => 1,
),
)
'deferrable' => false,
'initiallydeferred' => false,
'onupdate' => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION,
'ondelete' => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION,
'match' => SIMPLE|PARTIAL|FULL,
);
?>
Creating primary keys in the events and people tables
<?php
$definition = array (
'primary' => true,
'fields' => array (
'id' => array()
)
);
$mdb2->createConstraint('events', 'keyname', $definition);
$mdb2->createConstraint('people', 'keyname', $definition);
?>
Note: Some RDBMS may choose to ignore the name of the constraint, for example
MySQL will not use the value keyname
provided in the
method call, but will use PRIMARY
when a primary key is
created, or [tablename]_ibfk_[n]
when a foreign key is
created.
In the definition array, you specify which fields will be included in the
constraint, using the fields
key. The other possible
keys in the definition array are primary
and
unique
, which have boolean values.
Let's create another key in the event_participants, where each row has to be
unique, meaning that one person can appear only once for a specific event.
The definitions array will have both fields in the fields
key and the unique
key will be set to true.
Creating a unique constraint
<?php
$definition = array (
'unique' => true,
'fields' => array (
'event_id' => array(),
'person_id' => array(),
)
);
$mdb2->createConstraint('event_participants', 'unique_participant', $definition);
?>
To create an index, the method createIndex() is used,
which has similar signature as createConstraint(), so it
takes table name, index name and a definition array. The definition array has
one key fields
with a value which is another associative
array containing fields that will be a part of the index. The fields are
defined as arrays with possible keys:
sorting
, with values ascending
and descending
length
, integer value
Not all RDBMS will support index sorting or length, in these cases the drivers will ignore them. In the test events database, we can assume that our application will show events occuring in a specific timeframe, so the selects will use the datetime field in WHERE conditions. It will help if there is an index on this field.
Creating an index
<?php
$definition = array(
'fields' => array(
'datetime' => array()
)
);
$mdb2->createIndex('events', 'event_timestamp', $definition);
?>
The way MDB2 handles sequences is described here. For the events table in our example database, we'll need to have the 'id' auto-incrementing. For this purpose the method nextId() is used to give the next value. nextId() will create the sequence table if it doesn't exist, but we can create if beforehand with createSequence(). It takes a sequence name and an optional start value for the sequence.
Creating sequences
<?php
$mdb2->createSequence('events');
$mdb2->createSequence('people', 1);
?>
In the default MDB2 setup the example above will create
two tables: events_seq and people_seq, each with one field called 'sequence',
but the field name and the '_seq' postfix are configurable via the MDB2 options
seqname_format
and seqcol_name
.
Once a database table is created you can rename it or add, remove, rename and alter fields, using the alterTable() method. alterTable() takes three parameters: the table name, the definition of changes and a boolean "check-only" flag. If true, no changes will be made, but only a check if the proposed changes are feasible for the specific table and RDBMS. The second parameter (definition of changes) is an array with these keys:
name
: new name for the table. This is the only key
related to the table itself, the other keys contain field definitions
add
: fields to be added
remove
: fields to be removed
change
: fields to be changed
rename
: fields to be renamed
The values for add/remove/change/rename keys have slightly different structures, but they all contain field definitions. You can check the API docs for more information and an examples.
To see what's in the database, you can use the list*() family of functions, namely:
Listing database elements
<?php
$dbs = $mdb2->listDatabases();
print_r($dbs);
/*
prints:
Array
(
[0] => information_schema
[1] => events_db
[2] => mysql
[3] => test
[4] => test_db
[5] => test_db_explain
[6] => test_mdb2
)
*/
$seqs = $mdb2->listSequences('events_db');
print_r($seqs);
/*
prints:
Array
(
[0] => events
[1] => people
)
*/
$cons = $mdb2->listTableConstraints('event_participants');
print_r($cons);
/*
prints:
Array
(
[0] => unique_participant
)
*/
$fields = $mdb2->listTableFields('events');
print_r($fields);
/*
prints:
Array
(
[0] => id
[1] => name
[2] => datetime
)
*/
$idx = $mdb2->listTableIndexes('events');
print_r($idx);
/*
prints:
Array
(
[0] => event_timestamp
)
*/
$tables = $mdb2->listTables();
print_r($tables);
/*
prints:
Array
(
[0] => event_participants
[1] => events
[2] => people
)
*/
// currently there is no method to create a view,
// so let's do it "manually"
$sql = "CREATE VIEW names_only AS SELECT name FROM people";
$mdb2->exec($sql);
$sql = "CREATE VIEW last_ten_events AS SELECT * FROM events ORDER BY id DESC LIMIT 0,10";
$mdb2->exec($sql);
// list views
$views = $mdb2->listViews();
print_r($views);
/*
prints:
Array
(
[0] => last_ten_events
[1] => names_only
)
*/
/*
Not implemented in the MySQL driver
listTableTriggers()
listTableViews()
listFunctions()
*/
?>
For every create*() method as shown above, there is a corresponding drop*() method to delete a database, a table, field, index or constraint. The drop*() methods do not check if the item to be deleted exists, so it's the developer's responsibility to check for PEAR errors.
Drop database elements
<?php
// let's say our normal setup is to die on PEAR errors
PEAR::setErrorHandling(PEAR_ERROR_DIE);
// for the next statements we'll temporarily
// change the error handling
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
// drop a sequence
$result = $mdb2->dropSequence('nonexisting');
if (PEAR::isError($result)) {
echo 'The sequence does not exist';
} else {
echo 'Sequence dropped';
}
// another sequence
$result = $mdb2->dropSequence('people');
// drop a constraint
$mdb2->dropConstraint('events', 'PRIMARY', true);
// note: the third parameter gives a hint
// that this is a primary key constraint
$mdb2->dropConstraint('event_participants', 'unique_participant');
// drop an index
$mdb2->dropIndex('events', 'event_timestamp');
// drop a table
$mdb2->dropTable('events');
// drop the database already!
$mdb2->dropDatabase('events_db');
// revert to the usual error handling
PEAR::popErrorHandling();
?>
The Reverse module is an extension of the Common Module driver, and is made up in the following structure:
In order to use the MDB2 Reverse drivers, it is necessary to first load the Reverse driver into your MDB2 instance. Let's start a MDB2 instance and set up our connection:
Loading the Reverse module
<?php
// Load up the MDB2 Base class
require 'MDB2.php';
// define a DSN to connect to our database
$dsn = array(
'phptype' => 'mysql',
'username' => 'someuser',
'password' => 'somepass',
'hostspec' => 'somehost',
'database' => 'somedb',
);
// create MDB2 instance
// MDB2 has a number of connection options - singleton(), factory() and connect()
$mdb2 = &MDB2::connect($dsn);
if(PEAR::isError($mdb2)) {
// Die with the user info message on failure for any reason
die($mdb2->getuserinfo());
}
// We have a valid connection, so let's do some magic!
// Load the Reverse Module using MDB2's loadModule method
$mdb2->loadModule('Reverse', null, true);
?>
In the above example, we have created a valid connection to a database that does exist, using a valid user and password for our database server. We now have access to all of the MDB2 Reverse functionality within our example application. For the purposes of this document, we will set up an example table as such:
Create a table
<?php
// Define a table name that we will be working with
$fields = array(
'id' => array(
'type' => 'integer',
'unsigned' => true,
'autoincrement' => true,
),
'somename' => array(
'type' => 'text',
'length' => 12,
),
'somedate' => array(
'type' => 'date',
),
);
$table = 'sometable';
// Create the table
//Load the Manager Module
$mdb2->loadModule('Manager', null, true);
//create the table with the manager module
$mdb2->manager->createTable($table, $fields);
?>
The getTableFieldDefinition() Method exists primarily to get an array that defines a table field. This array can then be used to re-create the table elsewhere, or for any other purpose that may be necessary. Using the MDB2 instance defined above, we will connect to a database, define a table that we want to work with, and reverse engineer a specific field that we are interested in. First, we need to define the table and field that we want to work with; then, it is as easy as a single line of code to get the table definition back as an array of mixed values, then using var_dump to view the results:
Get the table definition
<?php
// Set the field that we would like to reverse engineer (get definition of)
$field = 'somedate';
// Here we get the definition of the table field and return it to a variable
// The return will either be a mixed array on success, or an MDB2 error on failure,
//so we don't need additional checks
$def = $mdb2->getTableFieldDefinition($table, $field);
// finally dumping the variable to screen
var_dump($def);
?>
The return will look something like the following, depending on your field definition:
<?php
array(1) {
[0] => array(5) {
['notnull'] => bool(false)
['nativetype'] => string(4) "date"
['default'] => NULL
['type'] => string(4) "date"
['mdb2type'] => string(4) "date"
}
}
?>
A number of other methods exist to gain information about a selected table. You may use any of the following methods according to the information needed:
This method will return a lot of information regarding a table, and can be used in a number of different ways. The information that it returns will differ slightly across different RDBM's and may give some variance in results. This method can be used to query either a table definition, or a resultset, which makes it great for creating optimized tables. The tableInfo() method allows you to pass a parameter for the mode that you would like to see the results presented as. In order to demonstrate the results more effectively, we will use a series of examples to do some queries and return the results in different modes. NOTE: Either a table OR a resultset can be passed as the first parameter to get information on the table. In these examples, we will be using the table that we defined above.
tableinfo() usage 1
<?php
// Default Mode - NULL
$tableInfo = $mdb2->tableInfo($table, NULL);
var_dump($tableInfo);
//will produce:
array(3) {
[0] => array(11) {
['notnull'] => bool(true)
['nativetype'] => string(3) "int"
['length'] => int(4)
['unsigned'] => int(1)
['default'] => string(0) ""
['autoincrement'] => bool(true)
['type'] => string(3) "int"
['mdb2type'] => string(7) "integer"
['name'] => string(2) "id"
['table'] => string(9) "sometable"
['flags'] => string(29) "primary_key not_null unsigned"
}
[1] => array(10) {
['notnull'] => bool(false)
['nativetype'] => string(7) "varchar"
['length'] => string(2) "12"
['fixed'] => bool(false)
['default'] => NULL
['type'] => string(7) "varchar"
['mdb2type'] => string(4) "text"
['name'] => string(8) "somename"
['table'] => string(9) "sometable"
['flags'] => string(0) ""
}
[2] => array(8) {
['notnull'] => bool(false)
['nativetype'] => string(4) "date"
['default'] => NULL
['type'] => string(4) "date"
['mdb2type'] => string(4) "date"
['name'] => string(8) "somedate"
['table'] => string(9) "sometable"
['flags'] => string(0) ""
}
}
?>
In the subsequent examples, we will only include the first table field definition,
as it effectively illustrates the differences in modes.
Now, let's change the mode to MDB2_TABLEINFO_ORDER
.
In addition to the information found in the default output, a notation of the
number of columns is provided by the num_fields
element
while the order element provides an array with the column names as the keys
and their location index number (corresponding to the keys in the default
output) as the values.
tableinfo() usage 2
<?php
$tableInfo = $mdb2->tableInfo($table, MDB2_TABLEINFO_ORDER);
var_dump($tableInfo);
array(5) {
['num_fields'] => int(3)
[0] => array(11) {
['notnull'] => bool(true)
['nativetype'] => string(3) "int"
['length'] => int(4)
['unsigned'] => int(1)
['default'] => string(0) ""
['autoincrement'] => bool(true)
['type'] => string(3) "int"
['mdb2type'] => string(7) "integer"
['name'] => string(2) "id"
['table'] => string(9) "sometable"
['flags'] => string(29) "primary_key not_null unsigned"
}
[1] => array(10) {
['notnull'] => bool(false)
['nativetype'] => string(7) "varchar"
['length'] => string(2) "12"
['fixed'] => bool(false)
['default'] => NULL
['type'] => string(7) "varchar"
['mdb2type'] => string(4) "text"
['name'] => string(8) "somename"
['table'] => string(9) "sometable"
['flags'] => string(0) ""
}
[2] => array(8) {
['notnull'] => bool(false)
['nativetype'] => string(4) "date"
['default'] => NULL
['type'] => string(4) "date"
['mdb2type'] => string(4) "date"
['name'] => string(8) "somedate"
['table'] => string(9) "sometable"
['flags'] => string(0) ""
}
['order'] => array(3) {
['id'] => int(0)
['somename'] => int(1)
['somedate'] => int(2)
}
}
?>
Changing the mode to MDB2_TABLEINFO_ORDERTABLE
will give
us some additional information by adding additional dimensions to the array
in which the table names are keys, and the field names are sub keys.
This type of query can be useful when querying complex joins, where some field
names may be the same.
NOTE: The flags
element will contain a space-separated
list of additional information about the field. This data is inconsistent
between RDBMS's due to the way each RDBMS works:
primary_key
unique_key
multiple_key
not_null
unsigned
Most RDBMS's only provide the table
and flags
elements if the result is a table name.
If the portability
option is set to MDB2_PORTABILITY_FIX_CASE
,
the names of tables and fields will be lower- or upper-cased.
If the case is set to CASE_UPPER
all tables and fields
will be uppercased, as is seen in Oracle and Firebird/Interbase, while
CASE_LOWER
will lower case all field and table names;
this is the default setting.
autoPrepare() and autoExecute() reduce the need to write boring INSERT, UPDATE, DELETE or SELECT SQL queries which are difficult to maintain when you add a field for instance. It requires the use of the Extended module
Imagine you have a 'user' table with 3 fields (id, name and country). You have to write sql queries like:
INSERT INTO table (id, name, country) VALUES (?, ?, ?) UPDATE table SET id=?, name=?, country=? WHERE ...
If you add a field ('birthYear' for example), you have to rewrite your queries which is boring and can lead to bugs (if you forget one query for instance).
With autoPrepare(), you don't have to write your insert, update, delete or select queries. For example:
<?php
// Once you have a valid MDB2 object named $mdb2...
$table_name = 'user';
$table_fields = array('id', 'name', 'country');
$types = array('integer', 'text', 'text');
$mdb2->loadModule('Extended');
$sth = $mdb2->extended->autoPrepare($table_name, $table_fields,
MDB2_AUTOQUERY_INSERT, null, $types);
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
?>
In this example, autoPrepare() will build the following SQL query:
INSERT INTO user (id, name, country) VALUES (?, ?, ?)
And then, it will call prepare() with it.
To add records, you have to use execute() or executeMultiple() like:
<?php
// ... continuing from the example above...
$table_values = array(1, 'Fabien', 'France');
$res =& $sth->execute($table_values);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
So, you don't have to write any SQL INSERT queries! And it works with UPDATE and DELETE queries too. For flexibility reasons, you have only to write the WHERE clause of the query. For instance:
<?php
// Once you have a valid MDB2 object named $mdb2...
$table_name = 'user';
$mdb2->loadModule('Extended');
$sth = $mdb2->extended->autoPrepare($table_name, null,
MDB2_AUTOQUERY_DELETE, 'id = '.$mdb2->quote(1, 'integer'));
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
$res =& $sth->execute($table_values);
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
autoPrepare() will build the following query:
UPDATE user SET name=?, country=? WHERE id=1
Then, it will call prepare() with it.
Be careful, if you don't specify any WHERE clause, all the records of the table will be updated.
Using autoExecute() is the easiest way to do insert, update, delete or select queries. It is a mix of autoPrepare() and execute().
You only need an associative array (key => value) where keys are fields names and values are corresponding values of these fields. This is only relevant for insert and update queries. For instance:
<?php
// Once you have a valid MDB2 object named $mdb2...
$table_name = 'user';
$fields_values = array(
'id' => 1,
'name' => 'Fabien',
'country' => 'France'
);
$types = array('integer', 'text', 'text');
$mdb2->loadModule('Extended');
$affectedRows = $mdb2->extended->autoExecute($table_name, $fields_values,
MDB2_AUTOQUERY_INSERT, null, $types);
if (PEAR::isError($affectedRows)) {
die($affectedRows->getMessage());
}
?>
And that's all! The following query is built and executed:
INSERT INTO user (id, name, country) VALUES (1, 'Fabien', 'France')
And it's the same thing for UPDATE queries:
<?php
// Once you have a valid MDB2 object named $mdb2...
$table_name = 'user';
$fields_values = array(
'name' => 'Fabien',
'country' => 'France'
);
$types = array('text', 'text');
$mdb2->loadModule('Extended');
$affectedRows = $mdb2->extended->autoExecute($table_name, $fields_values,
MDB2_AUTOQUERY_UPDATE, 'id = '.$mdb2->quote(1, 'integer'), $types);
if (PEAR::isError($affectedRows)) {
die($affectedRows->getMessage());
}
?>
which prepares and executes the following query:
UPDATE user SET name='Fabien', country='France' WHERE id = 1
Be careful, if you don't specify any WHERE statement, all the records of the table will be updated.
Here is an example for a DELETE queries:
<?php
// Once you have a valid MDB2 object named $mdb2...
$table_name = 'user';
$mdb2->loadModule('Extended');
$affectedRows = $mdb2->extended->autoExecute($table_name, null,
MDB2_AUTOQUERY_DELETE, 'id = '.$mdb2->quote(1, 'integer'));
if (PEAR::isError($affectedRows)) {
die($affectedRows->getMessage());
}
?>
which prepares and executes the following query:
DELETE FROM user WHERE id = 1
Finally an example for a SELECT queries:
<?php
// Once you have a valid MDB2 object named $mdb2...
$table_name = 'user';
// if left as a non array all fields of the table will be fetched using '*'
// in that case this variable can be set to true, to autodiscover the types
$result_types = array(
'name' => 'text',
'country' => 'text'
);
$mdb2->loadModule('Extended');
$res = $mdb2->extended->autoExecute($table_name, null,
MDB2_AUTOQUERY_SELECT, 'id = '.$mdb2->quote(1, 'integer'),
null, true, $result_types);
if (PEAR::isError($res)) {
die($res->getMessage());
}
$row = $res->fetchRow();
?>
which prepares and executes the following query:
SELECT name, country FROM user WHERE id = 1
You can also use placeholders in the WHERE clause and pass the values like this:
<?php
$id = 1;
$mdb2->autoExecute('table_name', array($id), MDB2_AUTOQUERY_DELETE, 'id = ?');
?>
which prepares the following query and passes the $id parameter on execute:
DELETE FROM table_name WHERE id = ?
The values passed in
$data
must be literals. Do not submit SQL functions (for exampleCURDATE()
). SQL functions that should be performed at execution time need to be put in the prepared statement.
Each database management system (DBMS) has its own behaviour. For example, some databases capitalize field names in their output, some lowercase them, while others leave them alone. These quirks make it difficult to port your scripts over to another server type. PEAR MDB2 strives to overcome these differences so your program can switch between DBMSs without any changes.
You control which portability modes are enabled by using
the portability
configuration option.
Configuration options are set via
factory() and
setOption().
The portability modes are bitwised, so they can be combined using
|
and removed using ^
.
See the examples section below on how to do this.
NB: MDB2 portability modes are meant to change the behaviour of the returned
values only, not that of the query itself. For instance, if you created your
tables quoting the identifiers, remember to use the quoteIdentifier()
method in all your queries or you'll get "not found" or "not exists" errors.
Also check for the quote_identifier
option,
if it's false then quoting won't be applied if the check_option is used.
MDB2_PORTABILITY_ALL (default)
turn on all portability features. this is the default setting.
MDB2_PORTABILITY_DELETE_COUNT
Force reporting the number of rows deleted.
Some DBMSs don't count the number of rows deleted when performing
simple DELETE FROM tablename
queries. This mode
tricks such DBMSs into telling the count by adding
WHERE 1=1
to the end of DELETE
queries.
MDB2_PORTABILITY_EMPTY_TO_NULL
Convert empty strings values to null in data in and output. Needed because Oracle considers empty strings to be null, while most other DBMSs know the difference between empty and null.
MDB2_PORTABILITY_ERRORS
Makes certain error messages in certain drivers compatible with those from other DBMSs
Driver | Description | Old Constant | New Constant |
---|---|---|---|
mysql, mysqli | unique and primary key constraints | MDB2_ERROR_ALREADY_EXISTS | MDB2_ERROR_CONSTRAINT |
mysql, mysqli | not-null constraints | MDB2_ERROR_CONSTRAINT | MDB2_ERROR_CONSTRAINT_NOT_NULL |
MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES
This removes any qualifiers from keys in associative fetches. Some RDBMS, for example SQLite, will default to use the fully qualified name for a column in assoc fetches if it is qualified in a query.
MDB2_PORTABILITY_FIX_CASE
Convert names of tables and fields to lower or upper case in all methods.
The case depends on the field_case
option that may
be set to either CASE_LOWER (default) or
CASE_UPPER.
NB: the case change is applied to the returned values only, not to
the field and table names in the query.
MDB2_PORTABILITY_NONE
Turn off all portability features
MDB2_PORTABILITY_NUMROWS
Enable hack that makes numRows() work in Oracle
MDB2_PORTABILITY_RTRIM
Right trim the data output for all data fetches. This does not apply to drivers for RDBMS that automatically right trim values of fixed length character values, even if they do not right trim value of variable length character values.
Disabling all portability options while connecting
<?php
require_once 'MDB2.php';
$dsn = 'mysql://user:password@host/database'
$options = array(
'debug' => 2,
'portability' => MDB2_PORTABILITY_NONE,
);
$mdb2 =& MDB2::connect($dsn, $options);
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
?>
Using setOption() to enable portability for lowercasing and trimming
<?php
// Once you have a valid MDB2 object named $mdb2...
$mdb2->setOption('field_case', CASE_LOWER);
$mdb2->setOption('portability',
MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_RTRIM);
?>
Using setOption() to enable all portability options except trimming
<?php
// Once you have a valid MDB2 object named $mdb2...
$mdb2->setOption('portability',
MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_RTRIM);
?>
Sequences are a way of offering unique IDs for data rows. If you do most of your work with e.g. MySQL, think of sequences as another way of doing AUTO_INCREMENT.
It's quite simple, first you request an ID, and then you insert that value in the ID field of the new row you're creating. You can have more than one sequence for all your tables, just be sure that you always use the same sequence for any particular table. To get the value of this unique ID use nextID(), if a sequence doesn't exists, it will be created automatically.
The sequence is automatically incremented each time nextID() is called.
Using a sequence
<?php
// Once you have a valid MDB2 object named $mdb2...
$id = $mdb2->nextID('mySequence');
if (PEAR::isError($id)) {
die($id->getMessage());
}
// Use the ID in your INSERT query
$res =& $mdb2->query("INSERT INTO myTable (id, text) VALUES ($id, 'foo')");
?>
When using PEAR MDB2's sequence methods, we strongly advise using these methods for all procedures, including the creation of the sequences. Do not use PEAR MDB2's methods to access sequences that were created directly in the DBMS.
If you have a compelling reason to ignore this advice, be aware that the
$seq_name
argument given to all of PEAR MDB2's sequence methods are modified before MDB2 calls the underlying DBMS.
$seq_name
is passed through PHP's sprintf() function using the value from theseqname_format
option as sprintf()'s format argument. The defaultseqname_format
is%s_seq
. So, for example, if you useperson_id_sequence
as the$seq_name
, PEAR MDB2 will change that name toperson_id_sequence_seq
when querying the DBMS about creating/accessing/updating the sequence.Also note that the default table layout for sequences emulated in PEAR DB is slightly different in PEAR MDB2. Where PEAR DB calls the column "id" PEAR MDB2 instead calls it "sequence" to make its purpose more clear. For backward compatibility this can be controlled via the
seqcol_name
option.The
seqname_format
andseqcol_name
can be modified when connecting or via setOption().
If you prefer using AUTO_INCREMENT you can alternatively use the lastInsertID() method to retrieve the last generated value. This method alternatively also supports getting the current ID from a sequence using the format defined in PostgreSQL's SERIAL data type. MDB2 can emulate this behaviour for RDBMS that do not support autoincrement at table creation time when using the createTable() method.
Using lastInsertID()
<?php
// Once you have a valid MDB2 object named $mdb2...
$res =& $mdb2->query("INSERT INTO myTable (id, text) VALUES (NULL, 'foo')");
// optionally pass in a table and fieldname in order to call nextID()
// when autoincrement is not supported
$id = $mdb2->lastInsertID('myTable', 'id');
if (PEAR::isError($id)) {
die($id->getMessage());
}
?>
If you can get the current global value of a sequence using the currID() method.
Using currID()
<?php
// getting the current value of a sequence
$id = $mdb2->currID('myseq');
if (PEAR::isError($id)) {
die($id->getMessage());
}
?>
Finally if you prefer using whatever native feature the RDBMS supports you can use the getBeforeID() and getAfterID() methods from the Extended module. This way MDB2 will automatically use AUTO_INCREMENT if it is natively supported. If not MDB2 will instead use a sequence to get the next id value.
Using getBeforeID()/getAfterID()
<?php
// Once you have a valid MDB2 object named $mdb2...
// $id may either be a quoted integer or php null
$id = $mdb2->getBeforeID('myTable', 'id', true, true);
if (PEAR::isError($id)) {
die($id->getMessage());
}
$res =& $mdb2->query("INSERT INTO myTable (id, text) VALUES ($id, 'foo')");
// $id is now equivalent to the value in the id field that was inserted
$id = $mdb2->getAfterID($id, 'myTable', 'id');
if (PEAR::isError($id)) {
die($id->getMessage());
}
?>
If you get a PEAR_Error (or a MDB2_Error object), try using getMessage() and getUserInfo(). They do often give you more information about the cause of the error.
If you get this error after creating the MDB2 instance it means that you don't have any MDB2 database driver installed. Since most people use only one database system, it is unnecessary to install 15 driver files.
If SQLite is the database of your choice, do a pear install MDB2_Driver_sqlite or simply pear install MDB2#sqlite and it should work.
Also search for MDB2 drivers in the PEAR package list.
If you get this error when trying to install a driver it means that the php.ini loaded in the given installer does not see the "XXX" extension. Either you forgot to install the extension all together or you need to make sure that the extension is actived in all relevant php.ini files. Note that there are usually separate php.ini files for the CLI and your other SAPIs.
If all else failes do pear install -nodeps MDB2_Driver_XXX and it should work.
If you work with more than one database at the same time, you may notice that
as soon you connect to the second db the connection to the first one is lost,
since the DBMS reuses the last open connection link.
To solve the problem, just set the
new_link
DSN option
to TRUE.
This package is an OO-abstraction to the SQL-Query language, it provides methods such as setWhere, setOrder, setGroup, setJoin, etc. to easily build queries.
This package is an OO-abstraction to the SQL-Query language, it provides methods such as setWhere(), setOrder(), setGroup(), setJoin(), etc. to easily build queries. It also provides an easy to learn interface that interacts nicely with HTML-forms using arrays that contain the column data, that shall be updated/added in a database. This package bases on an SQL-Builder which lets you easily build SQL-Statements and execute them. It supports all the db engines supported by MDB and MDB2.
Since it's a 1:1 port of DB_QueryTool, it has the same API, the only difference being the class name (and the constructor name, of course). Unfortunately, complete documentation is not available at the moment.
The best way to use MDB_QueryTool is creating a class that extends it. Here's a sample usage:
<?php
require_once 'MDB/QueryTool.php';
define('TABLE_CARS', 'cars');
$dsn = 'mysql://user:pass@host/dbname';
/**
* Let's suppose the "car" table has the following fields:
* (id, model, hp, color, clima, price)
*/
class Car extends MDB_QueryTool
{
var $table = TABLE_CARS;
var $sequenceName = TABLE_CARS;
// this is default, but to demonstrate it here ...
var $primaryCol = 'id';
/**
* This table spec assigns a short name to a table name
* this short name is needed in case the table name changes
* i.e. when u put the application on a provider's db, where you have to
* prefix each table, and you dont need to change the entire application to
* where you refer to joined table columns, for that joined results the
* short name is used instead of the table name
*/
var $tableSpec = array(
array('name' => TABLE_CARS, 'shortName' => 'cars'),
//array('name' => TABLE_TIME, 'shortName' => 'time'),
);
}
//instanciate an object of the Car class
$car = new Car($dsn);
//get the car #3
$car->reset(); // reset the query-builder, so no where, order, etc. are set
$res = $car->get(3);
var_dump($res);
//get all the cars
$car->reset(); // reset the query-builder, so no where, order, etc. are set
$res = $car->getAll();
var_dump($res);
// get the first 10 cars
$car->reset(); // reset the query-builder, so no where, order, etc. are set
$res = $car->getAll(0, 10);
var_dump($res);
//get all the red cars with clima, sorted by price
$car->reset();
$car->setWhere('color="red"');
$car->addWhere('clima=1');
$car->setOrder('price');
$res = $car->getAll();
var_dump($res);
//add a new car to the database
$data = array(
'model' => 'Super Trooper',
'hp' => 140,
'color' => 'black',
'clima' => 0,
'price' => 19000
);
$newCarId = $car->save($data);
var_dump($newCarId);
//update an existing car
$data = array(
'id' => $newCarId,
'clima' => 1,
'price' => 20000,
);
$res = $car->save($data); //equivalent to $car->update($data);
var_dump($res);
//remove the car from the database
$res = $car->remove($newCarId);
var_dump($res);
?>
MDB_QueryTool also offers working with classes. Here's a sample usage:
<?php
require_once 'MDB/QueryTool.php';
define('TABLE_CARS', 'cars');
$dsn = 'mysql://user:pass@host/dbname';
/**
* Let's suppose the "car" table has the following fields:
* (id, model, hp, color, clima, price)
*/
class Car extends MDB_QueryTool
{
var $table = TABLE_CARS;
var $sequenceName = TABLE_CARS;
// this is default, but to demonstrate it here ...
var $primaryCol = 'id';
/**
* This table spec assigns a short name to a table name
* this short name is needed in case the table name changes
* i.e. when u put the application on a provider's db, where you have to
* prefix each table, and you dont need to change the entire application to
* where you refer to joined table columns, for that joined results the
* short name is used instead of the table name
*/
var $tableSpec = array(
array('name' => TABLE_CARS, 'shortName' => 'cars'),
//array('name' => TABLE_TIME, 'shortName' => 'time'),
);
}
//instanciate an object of the Car class
$car = new Car($dsn);
$car->useResult('object');
//get the car #3
$car->reset(); // reset the query-builder, so no where, order, etc. are set
$res = $car->get(3)->fetchRow();
var_dump($res);
//get all the cars
$car->reset(); // reset the query-builder, so no where, order, etc. are set
$cars = $car->getAll();
while ($res = $cars->getNext()) {
var_dump($res);
}
// get the first 10 cars
$car->reset(); // reset the query-builder, so no where, order, etc. are set
$cars = $car->getAll(0, 10);
while ($res = $cars->getNext()) {
var_dump($res);
}
//get all the red cars with clima, sorted by price
$car->reset();
$car->setWhere('color="red"');
$car->addWhere('clima=1');
$car->setOrder('price');
$cars = $car->getAll();
while ($res = $cars->getNext()) {
var_dump($res);
}
//add a new car to the database
$newCar = $car->newEntity();
$newCar->model = 'Super Trooper';
$newCar->hp = 140;
$newCar->color = 'black';
$newCar->clima = 0;
$newCar->price = 19000;
$newCarId = $newCar->save();
var_dump($newCarId);
//update an existing car
$car->reset();
$res = $car->get($newCarId)->fetchRow();
$res->clima = 1;
$res->price = 20000;
$res->save();
var_dump($res);
//remove the car from the database
$car->reset();
$res = $car->get($newCarId)->fetchRow();
var_dump($res->remove());
unset($res);
?>
It is possible to use objects as result. A comprehensive example may be seen in the intro.
But using objects is not a simple alternative to using arrays as result. It is also possible to register a custom Class to be returned instead of the default MDB_QueryTool_Result_Row. The new resulting class has to be child of the MDB_QueryTool_Result_Row class.
To change the resulting class the method setReturnClass
is used.
<?php
require_once 'MDB/QueryTool.php';
require_once 'MDB/QueryTool/Result/Object.php';
define('TABLE_CARS', 'cars');
$dsn = 'mysql://user:pass@host/dbname';
/**
* Let's suppose the "cars" table has the following fields:
* (id, model, hp, color, clima, price)
*/
class Car extends MDB_QueryTool
{
public $table = TABLE_CARS;
}
class CarEntity extends MDB_QueryTool_Result_Row
{
}
//instanciate an object of the Car class
$car = new Car($dsn);
$car->useResult('object');
$car->setReturnClass('CarEntity');
?>
This can now be used to implement getter and setter and thus regulate the accessability to the values. In order to block the access to the class variables from outside they have to be declared as protected. Declaring them as private would result in also blocking the parent class, which gets the data, of accessing them. Of course when doing so corresponding methods have to be implemented to access the variables again.
To keep the example short only methods for dealing with model, hp and clima have been implemented.
<?php
require_once 'MDB/QueryTool.php';
require_once 'MDB/QueryTool/Result/Object.php';
define('TABLE_CARS', 'cars');
$dsn = 'mysql://user:pass@host/dbname';
/**
* Let's suppose the "cars" table has the following fields:
* (id, model, hp, color, clima, price)
*/
class Car extends MDB_QueryTool
{
public $table = TABLE_CARS;
}
class CarEntity extends MDB_QueryTool_Result_Row
{
protected $id;
protected $model;
protected $hp;
protected $color;
protected $clima;
protected $price;
public function getModel() {
return $this->model;
}
public function setModel($model) {
$this->model = $model;
}
public function getHp() {
return $this->hp;
}
public function setHp($hp) {
$this->hp = $hp;
}
public function getClima() {
if ($this->clima) {
return true;
} else {
return false;
}
}
public function setClima($clima) {
if ($clima) {
$this->clima = 1;
} else {
$this->clima = 0;
}
}
}
//instantiate an object of the Car class
$car = new Car($dsn);
$car->useResult('object');
$car->setReturnClass('CarEntity');
?>
This example only demonstrates a very basic feature. But it enables for example the implementation of Decorators. Every feature of modern OOP is now possible to implement.
MDB_QueryTool provides the following methods:
autoJoin()
Join the given tables, using the column names, to find out how to join the tables; i.e., if table1 has a column named "table2_id", this method will join "WHERE table1.table2_id=table2.id". All joins made here are only concatenated via AND.
getDbInstance()
Return a PEAR::DB object
setDbInstance()
Pass an existing PEAR::DB object to MDB_QueryTool
get($id, $column)
Get the data of a single entry. If the second parameter is only one column, the result will be returned directly, not as an array!
getMultiple($ids, $column)
Same as get(), but for all the elements in the $ids array.
getAll()
Get all the entries from the db.
getCol($column)
This method only returns one column, so the result will be a one dimensional array. This does also mean that using setSelect() should be set to *one* column, the one you want to have returned. A common use case for this could be:
<?php
$table->setSelect('id');
$ids = $table->getCol();
//OR
$ids = $table->getCol('id');
?>
so ids will be an array with all the id's.
getCount()
Get the number of entries.
getDefaultValues()
return an empty element where all the array elements do already exist corresponding to the columns in the DB
getQueryString()
Render the current query and return it as a string.
save($data)
Save data, calls either update() or add(). If the primaryCol is given in the data this method knows that the data passed to it are meant to be updated (call update()), otherwise it will call the method add(). If you don't like this behaviour simply stick with the methods add() and update() and ignore this one here. This method is very useful when you have validation checks that have to be done for both adding and updating, then you can simply overwrite this method and do the checks in here, and both cases will be validated first.
update($data)
Update the member data of a data set.
add($data)
Add a new member in the db.
addMultiple($data)
Adds multiple new members in the db.
remove($data, $whereCol)
Removes a member from the db.
data
is the value of the column that shall be removed
(integer/string); if an array is used, it must contain multiple columns
that shall be matched (in this case, the second parameter will be ignored);
$whereCol
: the column to match the data against,
only if data
is not an array
removeAll()
Empty a table.
removeMultiple($ids, $colName)
Remove the datasets with the given ids. If colName
is set, it is used as the primary key column name.
removePrimary($ids, $colName, $atLeastOneObject)
Removes a member from the db and calls the remove() methods of the given objects so all rows in another table that refer to this table are erased too.
setLimit($from=0, $count=0)
Set the limits for the following query.
getLimit()
Get the limits for the following query.
setWhere($whereCondition)
Sets the where condition which is used for the current instance.
getWhere()
Gets the where condition which is used for the current instance.
addWhere($whereCondition, $condition)
Adds a string to the where clause. The default condition
is AND.
addWhereSearch($column, $stringToSearch, $condition)
Add a where-like clause which works like a search for the given string; i.e. calling it like this:
<?php
$this->addWhereSearch('name', 'otto hans')
?>
produces a where clause like this one
UPPER(name) LIKE "%OTTO%HANS%"
so the search finds the given string.
setOrder($orderCondition, $desc=FALSE)
Sets the order condition which is used for the current instance.
getOrder()
Gets the order condition which is used for the current instance.
addOrder($orderCondition, $desc=FALSE)
Adds an order parameter to the query.
setHaving($havingCondition)
Sets the having condition which is used for the current instance.
getHaving()
Gets the having condition which is used for the current instance.
addHaving($what, $connectString)
Adds an having parameter to the query.
setJoin($table, $where, $joinType)
Sets the join condition which is used for the current instance.
setLeftJoin($table, $where)
Sets a left join on $this->table.
addLeftJoin($table, $where, $type)
Adds a left join to the query.
setRightJoin($table, $where)
Sets a right join on $this->table.
getJoin($what)
Gets the join-condition.
addJoin($table, $where, $type)
adds a table and a where clause that shall be used for the join instead of calling
<?php
setJoin(array(table1, table2), '<where clause1> AND <where clause2>');
?>
you can also call
<?php
setJoin(table1,'<where clause1>');
addJoin(table2,'<where clause2>');
?>
setTable($table)
Sets the table this class is currently working on.
getTable()
Gets the table this class is currently working on.
setGroup($group)
Sets the group-by condition.
getGroup()
Gets the group-by condition.
setSelect($what)
Limit the result to return only the columns given in what
.
addSelect($what, $connectString)
Add a string to the select-part of the query and connects it to an existing
string using the connectString
, which by default is a comma.
("SELECT xxx FROM..." xxx is the select-part of a query)
getSelect()
Gets the select-part of the query.
setDontSelect($what)
Exclude some columns from the resultset.
getDontSelect()
Gets the columns excluded from the resultset.
reset($what)
Reset all the set* settings, with no parameter given it resets them all.
setOption($option, $value)
Set mode the class shall work in. The 'raw' mode does not quote the data before building the query
getOption($option)
Get the given option.
debug($string)
override this method and i.e. print the queryString to see the final query.
getTableShortName($table)
Gets the short name for a table.
execute($query, $method)
Execute a query (the current query is executed when query
is null.
writeLog($text)
Write events to the logfile. It does some additional work, like time measuring etc. to see some additional info.
returnResult($result)
Return the chosen result type
setIndex($key)
Format the result to be indexed by key
.
NOTE: be careful, when using this you should be aware, that if you
use an index which's value appears multiple times you may loose data
since a key can't exist multiple times!
The result for a result to be indexed by a key(=columnName)
(i.e. 'relationtoMe') which's values are 'brother'
and 'sister' or alike normally returns this:
<?php
$res['brother'] = array('name' => 'xxx');
$res['sister'] = array('name' => 'xxx');
?>
but if the column 'relationtoMe' contains multiple entries for 'brother' then the returned dataset will only contain one brother, since the value from the column 'relationtoMe' is used and which 'brother' you get depends on a lot of things, like the sort order, how the db saves the data, and whatever else. You can also set indexes which depend on 2 columns, simply pass the parameters like 'table1.id,table2.id' it will be used as a string for indexing the result and the index will be built using the 2 values given, so a possible index might be '1,2' or '2108,29389' this way you can access data which have 2 primary keys. Be sure to remember that the index is a string!
getIndex()
Gets the index.
useResult($type)
Choose the type of the returned result ('array', 'object', 'none')
setErrorCallback($param)
Set both callbacks.
setErrorLogCallback($param)
Set the error log callback.
setErrorSetCallback($param)
Set the error set callback.
PEAR::MDB2_Schema enables users to maintain RDBMS independent schema files in XML that can be used to create, alter and drop database entities and insert data into a database. Reverse engineering database schemas from existing databases is also supported. The format is compatible with both PEAR::MDB and Metabase.
MDB2_Schema builds upon MDB2 to provide tools to manage your database schema using XML which is both platform- and database-independent.
The XML format is inherited from the Metabase package and was improved to be able to represent new entities such as Data Manipulation Instructions and Foreign Keys.
It enables users to maintain RDBMS independent schema files in XML that can be used to create, alter and drop database entities (also called as DDL: Data Definition Language) and insert data (also called as DML: Data Manipulation Language) into a database. Reverse engineering database schemas from existing databases is also supported. It also features the hability to parse database schemas and database data in separated files. However, in this document the term "schema file" will be used to designate any XML supported by MDB2_Schema, no matter its nature.
Reading of schema files is handled by MDB2_Schema_Parser and writing to them by MDB2_Schema_Writer. There is also the MDB2_Schema_Validate class which is called after something is parsed. It is supposed to check the logical integrity of the schema file, for instance whether you are trying to declare a index field that does not exist or even when you are trying to insert a float in a date field.
Currently two parsers are available, one based on the package XML_Parser (default) and another based on XML_Unserializer. As MDB2_Schema is still beta, both parsers have its own limitation. The default one can't parse more than 2 nested expressions. The later doesn't respect DML instructions in the order they are specified in the schema file.
Although MDB2_Schema API supports input from physical databases and schema files, internally it always operates using a database schema array that will be described later in the documentation.
First you need MDB2 installed:
You should install a driver for each database you are working with. For MySQL it would be:
For some hints refers to MDB2 documentation or try in a UNIX-like system:
MDB2_Schema is a separate package, and can also be installed using the PEAR installer:
now you should be ready :)
To create an instance of the MDB2_Schema class you can use the
factory()
, which accepts a
$dsn
or an array. The factory method also accepts an
existing MDB2 object. In the example below, we will just use a
$dsn
.
<?php
require_once 'MDB2/Schema.php';
$options = array(
'log_line_break' => '<br>',
'idxname_format' => '%s',
'debug' => true,
'quote_identifier' => true,
'force_defaults' => false,
'portability' => false
);
$dsn = 'mysql://root:@localhost/MDB2Example';
$schema =& MDB2_Schema::factory($dsn, $options);
if (PEAR::isError($schema)) {
$error = $schema->getMessage();
}
if (isset($error)) {
var_dump($error);
}
$schema->disconnect();
?>
All the internal work of MDB2_Schema is done over array structures, which
we will call "definition array". Many methods, such as
createDatabase
, requires a definition array as
input parameter, instead of a filename. Others take care of the conversion
automatically, accepting both inputs.
For the cases when you need a definition array, there are two avaliable methods to manually generate the definition array, one for each schema source - either a database or a schema file. Obviously you can also write it by hand, it is not intended to be done this way though.
You can use getDefinitionFromDatabase()
to get
the definition array from an existing database.
<?php
require_once 'MDB2/Schema.php';
$options = array(
'log_line_break' => '<br>',
'idxname_format' => '%s',
'debug' => true,
'quote_identifier' => true,
'force_defaults' => false,
'portability' => false
);
$dsn = 'mysql://root:@localhost/MDB2Example';
$schema =& MDB2_Schema::factory($dsn, $options);
if (PEAR::isError($schema)) {
$error = $schema->getMessage();
} else {
// this method _attempts_ to get the defintition from the database
// make sure you have tested it with your database to see if it
// returns what you expect
$definition = $schema->getDefinitionFromDatabase();
if (PEAR::isError($definition)) {
$error = $definition->getMessage();
}
}
if (isset($error)) {
var_dump($error);
}
$schema->disconnect();
?>
Though you have to use the method with caution, if you use the method on a
database created by hand. Some of the fields might be slightly different,
but once you create your database using MDB2_Schema it is reliable and will
return the same $definition
every time.
You can use parseDatabaseDefinitionFile()
to get
the definition array from a schema file.
<?php
require_once 'MDB2/Schema.php';
$options = array(
'log_line_break' => '<br>',
'idxname_format' => '%s',
'debug' => true,
'quote_identifier' => true,
'force_defaults' => false,
'portability' => false
);
$dsn = 'mysql://root:@localhost/MDB2Example';
$schema =& MDB2_Schema::factory($dsn, $options);
if (PEAR::isError($schema)) {
$error = $schema->getMessage();
} else {
$definition = $schema->parseDatabaseDefinitionFile('schema.xml');
if (PEAR::isError($definition)) {
$error = $definition->getMessage();
}
}
if (isset($error)) {
var_dump($error);
}
$schema->disconnect();
?>
Although the method accepts more parameters, only the first one is required.
You can use dumpDatabase()
to copy your database
to a schema file.
dumpDatabase()
accepts a database definition
array, for instance:
<?php
require_once 'MDB2/Schema.php';
$options = array(
'log_line_break' => '<br>',
'idxname_format' => '%s',
'debug' => true,
'quote_identifier' => true,
'force_defaults' => false,
'portability' => false
);
$dsn = 'mysql://root:@localhost/MDB2Example';
$schema =& MDB2_Schema::factory($dsn, $options);
if (PEAR::isError($schema)) {
$error = $schema->getMessage();
} else {
$dump_options = array(
'output_mode' => 'file',
'output' => 'schema.xml',
'end_of_line' => "\n"
);
$definition = $schema->getDefinitionFromDatabase();
if (PEAR::isError($definition)) {
$error = $definition->getMessage();
} else {
$op = $schema->dumpDatabase($definition, $dump_options, MDB2_SCHEMA_DUMP_ALL);
if (PEAR::isError($op)) {
$error = $op->getMessage();
}
}
}
if (isset($error)) {
var_dump($error);
}
$schema->disconnect();
?>
The first parameter is just the database definition array. The second
parameter is the options where we choose to output to a file. The third
option tells dumpDatabase()
what to be dumped -
either the structure, the data in the tables, or both. This is defined
using the constants MDB2_SCHEMA_DUMP_STRUCTURE,
MDB2_SCHEMA_DUMP_CONTENT and
MDB2_SCHEMA_DUMP_ALL.
Some databases don't accept a text field with a default value. Given that,
notice that $options['force_defaults']
has to be set to
false when you want to create a field with the type
text, as it is true by default.
When having a schema file, it is a breeze to create a database. Simply do the following:
<?php
require_once 'MDB2/Schema.php';
$options = array(
'log_line_break' => '<br>',
'idxname_format' => '%s',
'debug' => true,
'quote_identifier' => true,
'force_defaults' => false,
'portability' => false
);
$dsn = 'mysql://root:@localhost/MDB2Example';
$schema =& MDB2_Schema::factory($dsn, $options);
if (PEAR::isError($schema)) {
$error = $schema->getMessage();
} else {
// first run with queries disabled to make sure everything is allright
$disable_query = true;
$definition = $schema->parseDatabaseDefinitionFile('example.xml');
if (PEAR::isError($definition)) {
$error = $definition->getMessage();
} else {
$op = $schema->createDatabase($definition, array(), $disable_query);
if (PEAR::isError($op)) {
$error = $op->getMessage();
}
}
}
if (isset($error)) {
var_dump($error);
}
$schema->disconnect();
?>
Having MDB2_Schema to update your database,
when its schema changes is also really easy.
You can use the
getDefinitionFromDatabase()
method to determine the current database schema,
and then just use
updateDatabase()
to do the actual update.
However, you have to make sure, that
getDefinitionFromDatabase()
returns what you expect before you rely on it.
See the respective chapter for more info.
<?php
require_once 'MDB2/Schema.php';
$options = array(
'log_line_break' => '<br>',
'idxname_format' => '%s',
'debug' => true,
'quote_identifier' => true,
'force_defaults' => false,
'portability' => false
);
$dsn = 'mysql://root:@localhost/MDB2Example';
$schema =& MDB2_Schema::factory($dsn, $options);
if (PEAR::isError($schema)) {
$error = $schema->getMessage();
} else {
// first run with queries disabled to make sure everything is allright
$disable_query = true;
$previous_schema = $schema->getDefinitionFromDatabase();
$op = $schema->updateDatabase('schema.xml', $previous_schema, array(), $disable_query);
if (PEAR::isError($op)) {
$error = $op->getMessage();
}
}
if (isset($error)) {
var_dump($error);
}
$schema->disconnect();
?>
The method accepts both,
a filename or a definition array,
as the first two parameters.
Note how we mixed them in the example above.
You may want to backup the current schema using
dumpDatabase()
for the case something goes wrong.
When updating database schemas we can run into data persistence issues. This can be addressed with data manipulation ability, that will be documented later in this manual.
The MDB2 XML Schema documentaion is available online at the package repository and refers to the latest development version.
PEAR::MDB2_TableBrowser is a lightweight ORM(object relation mapping) library. Turn any database table(or table view) into an easy to use php object.
MDB2_TableBrowser is a lightweight ORM(object relation mapping) library. Turn any database table(or table view) into an easy to use php object.
Table browsing objects allow your code to handle any database table in an abstract way. By freeing your code from the database details it is possible for you to build generic data reporting or manipulation functions.
Put another way, if you really hate using sql in your code, having to piece together bits of sql to make queries...this library gives you an alternative.
Currently only the single table browser is implemented. If you need to work with data that spans multiple tables, you can build a table view as this library works with them as well.
First you need MDB2 installed:
You should install a driver for each database you are working with. For MySQL it would be:
Finally you install MDB2_TableBrowser using the PEAR installer. Since the package is in alpha you have to add the -d option as below:
Steps to creating a table object out of your database table or view. This example connects to a mysql database animal_db and constructs a table object from the tbl_animals table.
Create an MDB2 object:
<?php
require_once "MDB2.php";
$dsn = 'mysql://username:pass@localhost/animal_db';
$mdb2 = MDB2::singleton($dsn);
?>
Load the TableBrowser extention:
<?php
$mdb2->loadModule('TableBrowser');
?>
Create the table object Create a table browser for the tbl_animals table, and specify id as the primary key:
<?php
$browser = $mdb2->tableBrowserFactory('tbl_animals', 'id');
?>
A table object allows you several ways of retrieving data from the underlying table. The examples below continue from the animals_db database.
Retrieving a single row is done via the getRows
method. This call returns the row data as a hash array:
<?php
$browser->getRow(1);
?>
Retrieving multiple rows is done via the getRows
method. This call returns an MDB2_Results object. From the animals db
example...
Get data the 3 animals in the table sorted by name starting with the
5th animal
<?php
$browser->getRows('name', 3, 5);
?>
Retrieving the different values in a column has is done via the
getColumnValues
method. In our example, we can
get the different kinds of animals in tbl_animals eg: mammal, reptile,...
<?php
$browser->getColumnValues('type');
?>
Inserting a single row is done via insertRow
method. It takes a single hash array as input.
<?php
$rowData = array('id'=>13, 'name' => 'duck','type' => 'bird','lifespan' => 5);
$browser->insertRow($rowData);
?>
Inserting multiple rows at once is done via
insertRows
method.
<?php
$data = array(
array(1,'dog','mammal',12),
array(2,'cat','mammal',30),
array(3,'parrot','bird',60),
array(4,'shark','fish',30),
array(5,'dolphin','mammal',50),
array(6,'crocodile','reptile',50),
array(7,'snake','reptile',20),
array(8,'spider','arachnid',1),
array(9,'housefly','insect',1),
array(10,'ostrich','bird',35),
array(11,'bat','mammal',6),
array(12,'human','mammal',100)
);
$browser->insertRows(array('id', 'name','type','lifespan'), $data);
?>
Updating a single row is done via updateRow
method.
<?php
//Get the row data
$rowData = $browser->getRow(3);
//Modify the data
$rowData['lifespan'] = 65;
//Update the row
$browser->updateRow(3, $rowData);
?>
The example source below contains all the different methods available to tableBrowser object beyond the features explained in the basic usage documentation.
<?php
require_once "MDB2.php";
define('DSN', 'mysql://username:pass@localhost/animal_db');
/**
* The example below relies on the following data from the table tbl_animals
* ID NAME TYPE LIFESPAN
* 1 dog mammal 12
* 2 cat mammal 30
* 3 parrot bird 60
* 4 shark fish 30
* 5 dolphin mammal 50
* 6 crocodile reptile 50
* 7 snake reptile 20
* 8 spider arachnid 1
* 9 housefly insect 1
* 10 ostrich bird 35
* 11 bat mammal 6
* 12 human mammal 100
*/
$dsn = 'mysql://username:pass@localhost/animal_db';
$options = array(
'debug' => 2,
'result_buffering' => false,
);
$mdb2 = MDB2::singleton($dsn, $options);
//Create the table
setupDb($mdb2);
$mdb2->loadModule('TableBrowser');
//Create a table browser for the tbl_animals table, and specify id as the primary key
$browser = $mdb2->tableBrowserFactory('tbl_animals', 'id');
//The data browsing object is now ready
//First insert the needed data
$data = array(
array(1,'dog','mammal',12),
array(2,'cat','mammal',30),
array(3,'parrot','bird',60),
array(4,'shark','fish',30),
array(5,'dolphin','mammal',50),
array(6,'crocodile','reptile',50),
array(7,'snake','reptile',20),
array(8,'spider','arachnid',1),
array(9,'housefly','insect',1),
array(10,'ostrich','bird',35),
array(11,'bat','mammal',6),
array(12,'human','mammal',100)
);
$browser->insertRows(array('id', 'name','type','lifespan'), $data);
//Get info on a single animal, getRow returns a hash array and getRows returns
//an MDB2_Result object
$browser->getRow(1);
print "\n" . $browser->getLastSQL();
//Prints: SELECT `ID`,`NAME`,`TYPE`,`LIFESPAN` FROM tbl_animals WHERE (`id` = 1)
//Get info on 3 animals in the table sorted by name starting with the 5th animal
$browser->getRows('name', 3, 5);
print "\n" . $browser->getLastSQL();
//The limits/offsets are not shown below as they are set by mdb2 library
//Prints: SELECT `ID`,`NAME`,`TYPE`,`LIFESPAN` FROM tbl_animals
//Hide the ID column and rename the column "TYPE" to "Animal Type"
$browser->selectColumns(array('name','type','lifespan'));
$browser->setColumnAlias('TYPE', 'ANIMAL TYPE');
$browser->getRows('name', 3, 5);
print "\n" . $browser->getLastSQL();
//Prints:SELECT `NAME`,`TYPE` AS `ANIMAL TYPE`,`LIFESPAN` FROM tbl_animals
//Get the different kinds of animal types in the table eg: mammal, reptile,...
$browser->getColumnValues('type');
print "\n" . $browser->getLastSQL();
//Prints:SELECT DISTINCT `TYPE` FROM tbl_animals
//This also works with aliases you have set up
$browser->getColumnValues('ANIMAL TYPE');
print "\n" . $browser->getLastSQL();
//Prints:SELECT DISTINCT `TYPE` FROM tbl_animals
/*
* Example using filters, look for mammals with a lifespan <60 years Multiple
* filters can be added and removed. This functionality can be used to quickly
* build a browsing application that gives the user the freedom to traverse the
* table data in different ways.
*/
$browser->addFilter('MaxAge', 'lifespan', '<=', 60);
$browser->addFilter('AnimalType', 'type', '=', 'mammal');
//Once a filter has been set, it affects the browser's output
$browser->getRows();
print "\n" . $browser->getLastSQL();
//Prints: SELECT `NAME`,`TYPE` AS `ANIMAL TYPE`,`LIFESPAN` FROM tbl_animals WHERE (`lifespan` <= 60 AND `type` = 'mammal')
//A single filter can be removed by specifying the filter name
$browser->removeFilter('MaxAge');
$browser->getRows();
print "\n" . $browser->getLastSQL();
//Prints: SELECT `NAME`,`TYPE` AS `ANIMAL TYPE`,`LIFESPAN` FROM tbl_animals WHERE (`type` = 'mammal')
//All Filters set can be cleared using this method
$browser->resetFilters();
$browser->getRows();
print "\n" . $browser->getLastSQL();
//Prints: SELECT `NAME`,`TYPE` AS `ANIMAL TYPE`,`LIFESPAN` FROM tbl_animals
//Insert a new row
$rowData = array('id'=>13, 'name' => 'duck','type' => 'bird','lifespan' => 5);
$browser->insertRow($rowData);
print "\n" . $browser->getLastSQL();
//The whole statement is not shown as this sql is prepared and excectues by mdb2 library
//Prints: INSERT INTO tbl_animals VALUES (?,?,?,?)
//Update the parrot's data
$rowData = $browser->getRow(3);
$rowData['lifespan'] = 65;
$browser->updateRow(3, $rowData);
print "\n" . $browser->getLastSQL();
//Prints: UPDATE tbl_animals SET `id`= NULL,`name`= 'parrot',`type`= NULL,`lifespan`= 65 WHERE (`id` = 3)
$browser->addFilter('AnimalType', 'type', '=', 'bird');
//Clear all colum selections and aliases
$browser->resetSelectColumns();
$browser->resetColumAliases();
$browser->getRows();
print "\n" . $browser->getLastSQL();
//Prints: SELECT `ID`,`NAME`,`TYPE`,`LIFESPAN` FROM tbl_animals WHERE (`type` = 'bird')
/**
* You can also create multiple filter chains to query for different conditions
* in parallel. Say you were interested in mammals with a lifespan > 30 or birds
* with a lifespan < 10. This can be accomplished using 2 filter chains as
* follows.
*/
$browser->resetFilters();
//Call the first chain 'Mammal Group' and the second "Bird Group'. You can use any identifier that makes sense to you
$browser->createFilterChain('Mammal Group');
$browser->createFilterChain('Bird Group');
//Define the mammals filter chain
$browser->selectFilterChain('Mammal Group');
$browser->addFilter('AnimalType', 'type', '=', 'mammal', 'Mammal Group');
$browser->addFilter('Lifespan', 'lifespan', '>', 30, 'Mammal Group');
//Define the birds filter chain
$browser->selectFilterChain('Bird Group');
$browser->addFilter('AnimalType', 'type', '=', 'bird', 'Bird Group');
$browser->addFilter('Lifespan', 'lifespan', '<', 10, 'Bird Group');
$browser->getRows();
print "\n" . $browser->getLastSQL();
//Prints: SELECT `ID`,`NAME`,`TYPE`,`LIFESPAN` FROM tbl_animals WHERE ((`type` = 'mammal' AND `lifespan` > 30)) OR ((`type` = 'bird' AND `lifespan` < 10))
//This resets all the filters chains
$browser->resetAllFilters();
//Switch back to the default filter chain
$browser->selectFilterChain();
//You can delete a filterChain like this. Any user defined filter chain can be removed
//But the default filter chain is always there
$browser->deleteFilterChain('Mammal Group');
$browser->deleteFilterChain('Bird Group');
//You can add custom columns as well for columns like 'md5()' or your own custom functions
$browser->addCustomColumn('md5(TYPE)', 'Special Column');
$browser->getRows();
print "\n" . $browser->getLastSQL();
//Prints: SELECT `id`,`name`,`type`,`lifespan`,md5(TYPE) AS `Special Column` FROM tbl_animals
$browser->removeCustomColumn('md5(TYPE)');
//You can also add grouping and sorting methods
$browser->setOrderBy('lifespan');
$browser->setGroupBy('type');
$browser->selectColumns(array('type','lifespan'));
$browser->addCustomColumn('count(*)', 'Number of Species');
$browser->getRows();
print "\n" . $browser->getLastSQL();
//Prints: SELECT `type`,`lifespan`,count(*) AS `Number of Species` FROM tbl_animals GROUP BY `type` ORDER BY `lifespan`
//Delete sharks (id 4)
$browser->deleteRow(4);
print "\n" . $browser->getLastSQL() . "\n\n";
//Prints: DELETE FROM tbl_animals WHERE (`id` = 4)
/**
* Creates the tbl_animals table
*
* @param ref &$mdb2 An mdb2 object reference
*
* @return void
*/
function setupDb(&$mdb2)
{
// loading the Manager module
$mdb2->loadModule('Manager');
$tableDefinition = array (
'id' => array (
'type' => 'integer',
'unsigned' => 1,
'notnull' => 1,
'default' => 0,
),
'name' => array (
'type' => 'text',
'length' => 300,
'notnull' => 1
),
'type' => array (
'type' => 'text',
'length' => 300,
'notnull' => 1
),
'lifespan' => array (
'type' => 'integer',
'unsigned' => 1,
'notnull' => 1,
'default' => 0,
),
);
$tableConstraints = array (
'primary' => true,
'fields' => array (
'id' => array()
)
);
$mdb2->dropTable('tbl_animals');
$mdb2->createTable('tbl_animals', $tableDefinition);
$mdb2->createConstraint('tbl_animals', 'primary_key', $tableConstraints);
$mdb2->createSequence('primary_key');
}
?>
Provides Packages for working with date and time data
Calendar Data Structures
This chapter describes how to use PEAR::Calendar
Some classes in PEAR::Calendar require PEAR::Date be installed. The PEAR::Date 1.3.1beta is missing a file and will not work. Make sure you have PEAR::Date version 1.4 or newer installed.
PEAR::Calendar is a package for generating Calendars as data structures. It does not render content or rely on any underlying data store, allowing it to be applied to many problem domains. At the same time it provides an easy to use API that makes rendering, for example, an HTML calendar very easy, while making it possible to "connect" the calendar with your data store.
PEAR::Calendar was developed as a result of finding no alternatives out there in PHP. There are numerous public domain calendar utilities / classes out there but all are tied to a specific output format (typically HTML with limited ability to customize without re-writing code) and often dependent on pre-built data structures stored in a database (read MySQL). This poses significant restrictions on the problem domains they can be applied to. By contrast PEAR::Calendar focuses on eliminating the math from the task of generating a calendar, allowing the end user to simply iterate over a prepared data structure.
Some of the benefits / features of PEAR::Calendar include:
A Simple Example
<?php
require_once 'Calendar/Month.php';
$Month = new Calendar_Month(2003, 10); // October 2003
$Month->build(); // Build the days in the month
// Loop through the days...
while ($Day = $Month->fetch()) {
echo $Day->thisDay().'<br />';
}
?>
Note: there are numerous and extensive examples provided with the PEAR::Calendar package file.
If not already installed, setup the PEAR package manager.
Type pear install Calendar-beta (using the command line manager)
PEAR::Calendar is now installed.
<?php
require_once 'Calendar/Month/Weekdays.php';
$Month = new Calendar_Month_Weekdays(date('Y'), date('n'));
$Month->build();
echo "<table>\n";
while ($Day = $Month->fetch()) {
if ($Day->isFirst()) {
echo "<tr>\n";
}
if ($Day->isEmpty()) {
echo "<td> </td>\n";
} else {
echo '<td>'.$Day->thisDay()."</td>\n";
}
if ($Day->isLast()) {
echo "</tr>\n";
}
}
echo "</table>\n";
?>
When working with PEAR::Calendar, there are a total of 12 (public) classes available for you to use, depending on the particular problem you are trying to solve. They can be grouped in date classes, tabular date classes, validation classes and decorators.
Providing representations of all basic human date units. All are subclasses of Calendar so provide the methods defined there.
Calendar_Year - represents a year and can build Calendar_Month, Calendar_Month_Weekdays and Calendar_Month_Weeks objects.
Include with
<?php
require_once 'Calendar/Year.php';
?>
.
Calendar_Month - represents a month and can build Calendar_Day objects.
Include with
<?php
require_once 'Calendar/Month.php';
?>
.
Calendar_Day - represents a day and can build Calendar_Hour objects.
Include with
<?php
require_once 'Calendar/Day.php';
?>
.
Calendar_Hour - represents a hour and can build Calendar_Minute objects.
Include with
<?php
require_once 'Calendar/Hour.php';
?>
.
Calendar_Minute - represents a minute and can build Calendar_Second objects.
Include with
<?php
require_once 'Calendar/Minute.php';
?>
.
Calendar_Second - represents a second but cannot build anything.
Include with
<?php
require_once 'Calendar/Second.php';
?>
.
Designed for building calendars in tabular format. All are subclasses of Calendar so provide the methods defined there.
Calendar_Month_Weekdays - builds Calendar_Day objects setting the isFirst(), isLast() and isEmpty() states, to display a month in tabular form.
Include with
<?php
require_once 'Calendar/Month/Weekdays.php';
?>
.
Calendar_Month_Weeks - builds Calendar_Week objects, representing a month in terms of the tabular weeks it contains (see FAQ for what a week represents).
Include with
<?php
require_once 'Calendar/Month/Weeks.php';
?>
.
Calendar_Week - builds a collection of seven Calendar_Day objects. If extended by a Calendar_Month_Weeks object, it represents a tabular week in a month and it sets the isEmpty() state for the appropriate days, if necessary.
Include with
<?php
require_once 'Calendar/Week.php';
?>
.
Used to validate dates. Calendar provides the methods isValid() to perform a simple check on any date and getValidator() to return an instance of Calendar_Validator for fine grained validation.
Calendar_Validator - is not instantiated by your code directly (you need need specifically include / create it) but instead returned from the Calendar::getValidator() method you can call on any subclass of Calendar. It is used to provide fine grained validation of a date to find out exactly what's wrong with it (for simple validation just call isValid() on any Date object or Tabular Date object, to know if it was created with valid values.
Calendar_Validation_Error - represents a validation error, providing methods to determine what went wrong. Available methods are getUnit() (e.g. returns Year or Month), getValue() (the value it failed with), getMessage() (the validation error message) and toString() which returns a combination of all the three preceding methods. The default validation error messages are in English but stored in the constants CALENDAR_VALUE_TOOSMALL and CALENDAR_VALUE_TOOLARGE which you can redefine in your code.
Provide a mechanism to add functionality to the main calendar objects (the subclasses of Calendar) without needing to directly extend them (and risk overwriting fields accidentally). When you create a decorator, you pass its constructor an instance of an existing calendar object. You can then make calls to the decorator in exactly the same way as you make calls to the original calendar object. This allows you to "overwrite" calendar methods, add new methods or even apply multiple decorators to the same calendar object. Decorators a typically either "injected" (via selection or the Calendar_Decorator_Wrapper decorator) into the loop where the calendar is rendered and / or to alter the output content (e.g. convert a numeric month into a textual month: 1 => January).
PEAR::Calendar provides some decorator implementations for you, to help with common tasks. These are not designed to suit everyone and hence are provided as optional code for you to use as needed (avoiding the performance cost associated with parsing code you're not using).
Calendar_Decorator - this is the base decorator class which you should extend with your own. It provides the same API as the calendar object it is decorating, and simply routes calls through to it. Example;
<?php
require_once 'Calendar/Decorator.php';
class ChristmasDecorator extends Calendar_Decorator
{
function ChristmasDecorator(&$Calendar)
{
parent::Calendar_Decorator($Calendar);
}
// Some method I've added
function getDaysToChristmas()
{
$today = parent::thisDay(true);
$Christmas = new Calendar_Day(date('Y'), 12, 25);
$xmasday = $Christmas->thisDay(TRUE);
$diff = $xmasday - $today;
if ($diff < 0) {
$NextChristmas = new Calendar_Day($Christmas->nextYear(), 12, 25);
$nextxmasday = $NextChristmas->thisDay(true);
$diff = $today - $nextxmasday;
}
return ($diff / 86400);
}
}
?>
Calendar_Decorator_Uri - provides a decorator to help with generating URLs (for example next / prev URLs).
Include with
<?php
require_once 'Calendar/Decorator/Uri.php';
?>
.
Calendar_Decorator_Textual - helps with obtaining textual month names and weekday names from a calendar object.
Include with
<?php
require_once 'Calendar/Decorator/Textual.php';
?>
.
Calendar_Decorator_Wrapper - is intended to help you add decorators to the children of the calendar object you are working with. It will wrap the children in the decorator you specify at the point fetch() or fetchAll() is called, after a build build() call.
Include with
<?php
require_once 'Calendar/Decorator/Wrapper.php';
?>
.
For the main Calendar classes (the subclasses of Calendar) all share a common API (a common set of class methods). Although there are some minor variations in specific cases, you should find it intuitive and easy to remember, once you are familiar with what's available. This section summarizes the common methods and highlights the variations. For further information, consult the API documentation.
The constructors of the Date classes accept integer date values as their arguments, beginning with the year (on the left) across to the second (on the right), depending on what class you're using, for example;
<?php
// Natural Calendar Classes
$Year = new Calendar_Year(2003); // 2003
$Month = new Calendar_Month(2003, 10); // October 2003
$Day = new Calendar_Day(2003, 10, 25); // 25th October 2003
$Hour = new Calendar_Hour(2003, 10, 25, 13); // 25th October 2003 13:00:00
$Minute = new Calendar_Minute(2003, 10, 25, 13, 31); // 25th October 2003 13:31:00
$Second = new Calendar_Second(2003, 10, 25, 13, 31, 45); // 25th October 2003 13:31:45
// Tabular Calendar Classes
$Month = new Calendar_Month_Weekdays(2003, 10); // October 2003
$Month = new Calendar_Month_Weeks(2003, 10); // October 2003
$Week = new Calendar_Week(2003, 10, 25); // week containing 25th October 2003
?>
Note that the tabular classes, Calendar_Month_Weekdays and Calendar_Month_Weeks both take an optional third argument (integer) which specifies the first day of the week and adjust tabular display (normally it defaults to Monday (1) - pass the integer value 0 to switch to Sunday as the first day, for example). Calendar_Week accepts the first day argument as the fourth to its constructor.
All date and tabular date classes share the methods defined in the abstract base class Calendar. Among these are methods for an object of any of these classes to identify itself (what's its date is) and the previous and next dates. For example;
<?php
// Create a day
$Day = new Calendar_Day(2003, 10, 1); // 1st October 2003
echo $Day->thisDay(); // Displays 1
echo $Day->prevDay(); // Displays 30 (the 30th of September)
echo $Day->nextDay(); // Displays 2
echo $Day->thisMonth(); // Displays 10
echo $Day->prevMonth(); // Displays 9
echo $Day->nextMonth(); // Displays 11
?>
Notice that from the $Day
I can find out not just about the day itself but also the month (or year/hour/minute/second). Notice also how prevDay() returned 30, taking care of working out that the previous day is in September for you. There are this***(), prev***() and next***() methods for years, months, days, hours, minutes and seconds.
Calling any of these methods and passing the value TRUE will result in a timestamp being returned (the type of timestamp depends on the Calendar Engine you are using - see the FAQ), for example;
<?php
// Create a second
$Second = new Calendar_Second(2003, 10, 25, 13, 32, 44);
echo $Second->thisYear(TRUE); // Displays 1041375600
echo $Second->thisMonth(TRUE); // Displays 1064959200
echo $Second->thisDay(TRUE); // Displays 1067032800
echo $Second->thisHour(TRUE); // Displays 1067079600
echo $Second->thisMinute(TRUE); // Displays 1067081520
echo $Second->thisSecond(TRUE); // Displays 1067081564
?>
Notice how the timestamp return increases, depending on the method that was called to obtain it. The first, obtained from thisYear(TRUE)() is a timestamp for the beginning of the year 2003 (1st January 2003 in fact) while the third from thisDay(TRUE)() is a timestamp for 25th October 2003.
The prev***() and next***() methods return values calculated relative to the current Calendar object you are working with. In the above example, if you wanted the day and month of the next day (which should be 2nd October 2003), calling nextDay() then nextMonth() would give you the 2nd of November not October. Instead you should call nextDay() passing it the argument TRUE, to get back a timestamp from which the next day can be obtained. Be aware of the Calendar_Decorator_Uri which is designed to help build URLs for next / prev links.
There are also the methods thisWeek(), prevWeek() and nextWeek() which only become available if you're using an instance of Calendar_Week. The returned value format can be chosen among 'timestamp', 'n_in_month', 'n_in_year' or 'array'. Be careful - if you select 'n_in_month', it will return NULL if it reaches the beginning or end of a month.
Finally all Calendar objects provide the methods getTimeStamp() and setTimeStamp(). The former returns a timestamp in the format used by the calendar engine you are working with (i.e. Unix Timestamp if it's the default UnixTs engine or of format YYYY-MM-DD HH:MM:SS). The later excepts a timestamp which is used to replace the values the Calendar object was constructed with.
Every date and tabular date class has a build() method which is used to generate the children of that date object. For example Calendar_Month builds Calendar_Day objects, the days being "children" of the month.
Once build() is called, the children can be fetched from the date object using the simple fetch() iterator, for example;
<?php
$Month = new Calendar_Month(2003, 10);
$Month->build();
while ($Day = $Month->fetch()) {
echo $Day->thisDay()."<br />\n";
}
?>
The fetch() method returns one child at a time, in ascending date order, and returns FALSE when there are no more children. It also automatically resets the internal collection of children, meaning you can loop over them as many times as you like.
If you don't like the iterator, and wish to use your own, you can simply extract the children with the fetchAll() method (returns an indexed array of child date objects) and check the number you got back with size(). Be careful the index of the array you get back from fetchAll(). For Calendar_Year, Calendar_Month, Calendar_Month_Weekdays, Calendar_Month_Weeks and Calendar_Week, the first index of the array is 1 while for Calendar_Day, Calendar_Hour, Calendar_Minute and Calendar_Second the index begins with 0. Why? Consider 2003-1-1 00:00:00 ...
Note: Calendar_Second::build() doesn't do anything - it has no children.
To help with rendering the calendar, the build() methods accept an index array of date objects which is compares with the children it is building. If it finds that an object that was passed to it matches a child, it set's the childs state to selected, meaning the isSelected() method (available on any date or tabular date objects) returns TRUE. For example;
<?php
$Month = new Calendar_Month(2003, 10);
$DayX = new Calendar_Day(2003, 10, 15);
$DayY = new Calendar_Day(2003, 10, 23);
$selection = array($DayX, $DayY);
$Month->build($selection);
while ($Day = $Month->fetch()) {
if ($Day->isSelected()) {
echo $Day->thisDay()."<br />\n"; // Displays 15 or 23
}
}
?>
In the above example, only 15th October 2003 and 23rd October 2003 are displayed.
The objects you pass to build() which match children that are being built replace the child (i.e. if there was a match, you get your object back). This allows you to "inject" your own objects into the loop, best accomplished by extending Calendar_Decorator.
Note: the Calendar_Year::build() method takes a second argument to specify the first day of the week (see above discussion on Constructors).
The Calendar_Day class has three unique methods, which don't appear elsewhere, and are used purely for building tabular calendars. The isEmpty() is used to determine whether the day is an empty day or not (see FAQ for explaination of empty days). The methods isFirst() and isLast() are used to mark the beginning and and of a week.
Whether you need to use these methods depends on which class was used to build the day objects. If the day was built with Calendar_Month_Weekdays, all three of these methods are applicable (you may have empty days and Calendar_Month_Weekdays builts a complete month but delimits the beginning and end of each week so you can find it with isFirst() and isLast()). If the day was built with Calendar_Week, only the isEmpty() method is applicable (the first or last week in a month may contain empty days). For day objects built in any other manner, isEmpty(), isFirst() and isLast() are meaningless.
All date objects and tabular objects (except weeks) are capable of validating themselves. By default they accept whatever arguments they are given on construction but you can validate the date with the isValid() method, for example;
<?php
$Day = new Calendar_Day(2003, 10, 32);
if (!$Day->isValid()) {
die ('Invalid date');
}
?>
For more fine grained validation, you can first call the getValidator() method, to return an instance of Calendar_Validator then list the validation errors;
<?php
$Day = new Calendar_Day(2003, 10, 32);
if (!$Day->isValid()) {
$Validator = & $Day->getValidator();
while ($Error = $Validator->fetch()) {
echo $Error->getUnit().' is invalid<br>';
}
}
?>
or...
<?php
$Day = new Calendar_Day(2003, 10, 32);
$Validator = & $Day->getValidator();
if (!$Validator->isValid()) {
while ($Error = $Validator->fetch()) {
echo $Error->toString().'<br>';
}
}
?>
Note that rather than validating dates, you may prefer to automatically adjust them with the adjust() method, for example;
<?php
$Day = new Calendar_Day(2003, 10, 32);
$Day->adjust();
echo $Day->thisYear(); // 2003
echo $Day->thisMonth(); // 11 (moved forward a month)
echo $Day->thisDay(); // 1 (the first of the month)
?>
That summarizes all the methods available within PEAR::Calendar, apart from the those provided in the Decorator classes.
The Calendar_Decorator is provided to allow you to attach functionality to existing Calendar objects without needing to subclass them. This helps in a number of situations, such as allowing results from, say, a database query to be rendered in the calendar or to modify the values returned from Calendar methods (perhaps converting a numeric month into its textual name).
Some concrete decorators are provided with PEAR::Calendar, to address what may be common problems you encounter in using the library. These are not designed to suit everyone but instead focus on solving a more narrow problem domain. They will only be parsed by the PHP engine should you explicitly include them in your code. An example of why decorators can be useful:
<?php
require_once 'Calendar/Day.php';
require_once 'Calendar/Decorator.php';
class WorkingDay extends Calendar_Decorator {
function WorkingDay(& $Calendar) {
parent::Calendar_Decorator($Calendar);
}
// Overides the default fetch method of the calendar object
function fetch() {
if ($Hour = parent::fetch()) {
// Recursive fetch, return only hours between 8am and 6pm
if ($Hour->thisHour() < 8 || $Hour->thisHour() > 18) {
return $this->fetch();
} else {
return $Hour;
}
} else {
// Make sure to return FALSE when the real fetch returned nothing
// or you will get an infinite loop
return FALSE;
}
}
}
// Create a normal day and build the hours
$Day = new Calendar_Day(date('Y'), date('n'), date('d'));
$Day->build();
// Create the decorator, passing it the normal day
$WorkingDay = new WorkingDay($Day);
// Only hours in a working day are displayed...
while ($Hour = $WorkingDay->fetch()) {
echo $Hour->thisHour().'<br />';
}
?>
The base class Calendar_Decorator "mirrors" the combined API of all the subclasses of Calendar. It accepts a Calendar object to its constructor then "takes over" the API allowing you to make calls through it rather than directly to the original calendar object. The Calendar_Decorator simply routes calls through to the calendar object it is decorating and returns values where appropriate.
One important use of decorators is to help "inject" data into the loop which renders the calendar. This helps with fetching data from some sort of "event" table in a database. When passing a selection array to any build() method, the selected date objects will replace the default built objects, allowing you to get them back as inside the fetch() loop, using the isSelected() method. You'll find an example of this in the PEAR::Calendar download. It should always be possible to fetch the event data you need with a single database query...
PEAR::Calendar already provides a few decorators:
Calendar_Decorator_Textual
Decorator to help with fetching textual representations of months and days of the week. It has
Calendar_Decorator_Uri
Decorator to help with building HTML links for navigating the calendar.
Calendar_Decorator_Weekday
Decorator for fetching the day of the week.
Calendar_Decorator_Wrapper
Decorator to help with wrapping built children in another decorator.
This decorator defines a few methods that can be useful to handle month and day names:
monthNames($format
='long')
Returns an array with month names; the format of returned months
depends on the format
parameter
(one, two, short or long)
weekdayNames($format
='long')
Returns an array with day names; the format of returned days
depends on the format
parameter
(one, two, short or long)
prevMonthName($format
='long')
Returns textual representation of the previous month of the decorated calendar object
thisMonthName($format
='long')
Returns textual representation of the month of the decorated calendar object
nextMonthName($format
='long')
Returns textual representation of the next month of the decorated calendar object
prevDayName($format
='long')
Returns textual representation of the previous day of the decorated calendar object
thisDayName($format
='long')
Returns textual representation of the day of the decorated calendar object
nextDayName($format
='long')
Returns textual representation of the next day of the decorated calendar object
orderedWeekdays($format
='long')
Returns the days of the week using the order defined in the decorated calendar object. Only useful for Calendar_Month_Weekdays, Calendar_Month_Weeks and Calendar_Week. Otherwise the returned array will begin on Sunday.
Methods defined by this decorator:
setFragments($y, $m=null, $d=null, $h=null, $i=null, $s=null)
Set the names of the URI vars for each date element
setSeparator($separator
)
Set the fragments separator, for instance '/' (default: &).
setScalar(boolean $state
=TRUE)
Puts Uri decorator into "scalar mode" - URI variable names are not returned
prev($method
)
Gets the URI string for the previous calendar unit (year, month, week or day etc)
this($method
)
Gets the URI string for the current calendar unit (year, month, week or day etc)
next($method
)
Gets the URI string for the next calendar unit (year, month, week or day etc)
A simple usage example:
<?php
$Day = new Calendar_Day(2003, 10, 23);
$Uri = & new Calendar_Decorator_Uri($Day);
$Uri->setFragments('year', 'month', 'day');
echo $Uri->prev('day');
// Displays year=2003&month=10&day=22
?>
Methods defined by this decorator:
setFirstDay($firstDay)
Sets the first day of the week (0 = Sunday, 1 = Monday [default] etc)
prevWeekDay($format='int')
Returns the previous weekday, formatted according the $format
parameter (int, array, object, timestamp)
thisWeekDay($format='int')
Returns the current weekday, formatted according the $format
parameter (int, array, object, timestamp)
nextWeekDay($format='int')
Returns the next weekday, formatted according the $format
parameter (int, array, object, timestamp)
Example:
<?php
$Day = new Calendar_Day(2003, 10, 23);
$Weekday = & new Calendar_Decorator_Weekday($Day);
$Weekday->setFirstDay(0); // Set first day of week to Sunday (default Mon)
echo $Weekday->thisWeekDay(); // Displays 5 - fifth day of week relative to Sun
?>
<?php
require_once 'Calendar/Month.php';
require_once 'Calendar/Decorator.php'; // Not really needed but added to help this make sense
require_once 'Calendar/Decorator/Wrapper.php';
class MyBoldDecorator extends Calendar_Decorator
{
function MyBoldDecorator(&$Calendar)
{
parent::Calendar_Decorator($Calendar);
}
function thisDay()
{
return '<b>'.parent::thisDay().'</b>';
}
}
$Month = new Calendar_Month(date('Y'), date('n'));
$Wrapper = & new Calendar_Decorator_Wrapper($Month);
$Wrapper->build();
echo '<h2>The Wrapper decorator</h2>';
echo '<i>Day numbers are rendered in bold</i><br /> <br />';
while ($DecoratedDay = $Wrapper->fetch('MyBoldDecorator')) {
echo $DecoratedDay->thisDay().'<br />';
}
?>
What if you want WML, SOAP, PDF, GIF, command line, etc. etc.? PEAR::Calendar can be used to generate any output format you like (see the examples for SOAP and WML). Tying it to a particular output content type will limit its use (a problem that every public domain PHP Calendar library I've looked at suffers from). A PEAR::HTML_Calendar is likely to be developed using PEAR::Calendar.
Running the examples on Sourceforge's servers (which are always overloaded), example 3.php renders in under 0.1 seconds (usually half that). The code is highly optimized and every "trick in the book" has been applied to make sure PHP only parses / executes the subset of logic you need for your specific problem. If in doubt, use Cache_Lite to cache the output HTML.
All calculations are handled by a class implementing the Calendar_Engine interface. The default implemention is based on PHP's date() and mktime() functions (so Unix timestamps are required for that engine). A second engine exists which uses PEAR::Date. It's a bit slower but overcomes the limit on the range of Unixstamps. To switch between engines use the constant CALENDAR_ENGINE e.g.
<?php
// The default Unix timestamp engine (this definition is not required)
// define('CALENDAR_ENGINE', 'UnixTs');
// Switch to PEAR::Date engine
define('CALENDAR_ENGINE', 'PearDate');
?>
Note that the PearDate engine is based on PEAR::Date version 1.4 or newer.
PEAR::Calendar only uses base 10 numbers for calculations - the names of months and days of the week and generated as the calendar is being rendered (by you). You should only need to change PHP's locale with setlocale() and use the strftime() function e.g.:
<?php
$Day = & new Calendar_Day(2003, 10, 23);
setlocale (LC_TIME, 'de_DE'); // German
echo strftime('%A %d %B %Y', $Day->getTimeStamp());
?>
Note that Calendar_Decorator_Textual provides help in generating month and day of week names in a manner which is independent of the Calendar Engine you are using and can be modified with setlocale().
PEAR::Calendar makes it easy to render calendars in tabular format (like humans are used to) such as:
October 2003
M T W T F S S
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Notice the top left and botton right of this example - these are "empty days". Empty days are generated only by two calendar classes: Calendar_Month_Weekdays and Calendar_Week. For example using Calendar_Month_Weekdays;
<?php
require_once 'Calendar/Month/Weekdays.php';
$Month = & new Calendar_Month_Weekdays(2003, 10);
$Month->build();
while ($Day = & $Month->fetch()) {
if ($Day->isFirst()) // Check for the start of a week
echo "\n";
if ($Day->isEmpty()) // Check to see if day is empty
echo "\t";
else
echo $Day->thisDay()."\t";
if ($Day->isLast()) // Check for the end of a week
echo "\n";
}
?>
An empty day can still return values, the date it represents being from the previous or next month in the calendar. You may get empty days for Calendar_Month_Weekdays and Calendar_Week. Using Calendar_Week, you will only build 7 days (use Calendar_Month_Weeks to build Calendar_Week objects), so the isFirst() and isLast() methods are not applicable.
All calendar objects (except Calendar_Second, which has no "children") have the method build() to build the "children" of that object. For example Calendar_Year::build() builds Calendar_Month objects while Calendar_Hour::build() builds Calendar_Minute objects. You have the option of passing this method an indexed array of Calendar objects which will be used to "select" the matching built children. For example:
<?php
$Month = & new Calendar_Month(2003, 10); // Oct 2003
$SelectedDay1 = & new Calendar_Day(2003, 10, 5); // Oct 5th 2003
$SelectedDay2 = & new Calendar_Day(2003, 10, 21); // Oct 21st 2003
// Place in an array...
$selection = array($SelectedDay1, $SelectedDay2);
$Month->build($selection);
while ($Day = & $Month->fetch()) {
if ($Day->isSelected())
echo $Day->thisYear().' '.$Day->thisMonth().' '.$Day->thisDay().' is selected'."\n";
}
?>
Note: the date objects you pass to a build() method replace the corresponding built date objects, allowing you to do things like attach your own subclass of Calendar_Decorator to them, then access the decorating functionality inside the loop which renders the calendar. You might display the contents from an "events database table" using this approach.
First and foremost, for performance. Building the children has a performance cost and you won't always need to have the children, so it should be called explicitly, otherwise you might have $Year->build(), expecting to get just months but behind the scenes, months built days, which built hours, which build minutes etc. Also calling build() yourself give you a chance to "select" some of the children.
Validity is determined by the Calendar_Engine being used as well as the time the date object you're working with represents (e.g. $Month = & new Month(2003, 2, 29); is invalid, because Feb 2003 was not a leap year). For quick validation, you can call the method isValid() on any date object, which will return FALSE if there's a problem. For more information of more detailed validation, you can call the method getValidator() on any date object, which returns an instance of the class Calendar_Validator. For example;
<?php
$Month = & new Month(2003, 2, 29); // 29th Feb 2003 (?!?)
if (!$Month->isValid()) {
$Validator = & $Month->getValidator();
while ($Error = $Validator->fetch()) {
echo $Error->toString();
}
}
?>
You can also begin validation by calling getValidator() then either isValidYear(), isValidMonth(), isValidDay(), isValidHour(), isValidMinute() and isValidSecond() (or just isValid() which calls all of the isValidxxx methods).
If you're allowing end users to navigate your calendar via the URL, were they to modify the URL to something like calendar.php?year=2003&month=13, instead of throwing a validation error at them, you could call the Calendar::adjust() method on the calendar object you create with that URL. You should then end up with January 2004 (in this example). This behaviour is possible thanks to mktime() for the Unix Timestamp engine, while being built into the PearDate engine for you.
The method fetchAll() can be called on any date object to get an indexed array of all the children which have been built, allowing you to reference them directly. Be careful with the first index of this array - in some cases it will be [1] not [0], depending on the type of date object built. For example;
<?php
$Month = & new Calendar_Month(2003, 10);
$Month->build();
$days = & $Month->fetchAll(); // Now all in array
echo $days[1]->thisDay(); // The first day has index 1
$Hour = & new Calendar_Hour(2003, 10, 25, 15); // Oct 25th 2003, 3pm
$Hour->build();
$hours = & $Hour->fetchAll(); // Now all in array
echo $hours[0]->thisHour(); // The first hour has index 0
?>
The following classes are always built to have the first index as 1: Calendar_Month, Calendar_Month_Weekdays, Calendar_Month_Weeks, Calendar_Week and Calendar_Day The following classes are always built to have the first index as 0: Calendar_Hour, Calendar_Minute and Calendar_Second Note also the method size() can be called on any date object, after build() has been called, to get the number of children.
Weeks are "pseudo" dates. They're useful for formatting the user interface for end users. Weeks are instantiated with a year, a month and a day of the month. You can then have the week tell you its timestamp (which will be the same as the timestamp for the first day in the week), its numeric position within the tabular month (see empty days above), its numeric position within the year (this is an ISO-8601 week number of year, weeks starting on Monday) or an array containing the numeric year, month and the first day of the week (as a number within the month). For example:
<?php
$Week = & new Calendar_Week(2003, 10, 15);
$Week = new Calendar_Week(2003, 10, 15);
echo $Week->thisWeek(); // Displays 2 (week num in month)
echo $Week->thisWeek('n_in_month'); // Display 2 - same as above
echo $Week->thisWeek('n_in_year'); // Displays 41 (week in year)
echo $Week->thisWeek('timestamp'); // Displays unix timestamp or an ISO-8601 datetime
// (YYYY-MM-DD HH:MM:SS), depending on the engine.
print_r $Week->thisWeek('array'); // [year] => 2003 [month] => 10 [day] => 12
?>
When working with a Calendar_Year, the constants CALENDAR_MONTH_STATE controls what type of month object is built. You can define CALENDAR_MONTH_STATE to CALENDAR_USE_MONTH_WEEKDAYS or CALENDAR_USE_MONTH_WEEKS for the Calendar_Month_Weekdays and Calendar_Month_Week classes, respectively.
Yes. For the classes which are concerned with the notion of a "week", you can can pass a value which defines the first day of the week. For the default timestamp based Calendar engine, this is a number from 0 to 6, 0 being Sunday through to 6 being Saturday. This value can be passed to the following:
<?php
$Year = new Calendar_Year(2003);
$selection = array();
$Year->build($selection, 0); // the second argument is the first day of the week (Sunday)
$MonthWeekdays = new Calendar_Month_Weekdays(2003, 10, 6); // Third argument - Saturday
$MonthWeeks = new Calendar_Month_Weekdays(2003, 10, 2); // Third argument - Tuesday
$Week = new Calendar_Week(2003, 10, 15, 5) // Fourth argument - Friday
?>
Calendar API.
void constructor Calendar::Calendar (
int $y = 2000
, int $m = 1
, int $d = 1
, int $h = 0
, int $i = 0
, int $s = 0
)
This package is not documented yet.
$y
year
$m
month
$d
day
$h
hour
$i
minute
$s
second
throws no exceptions thrown
This function can not be called statically.
void Calendar::adjust (
)
Helper method. You can adjust calling object's date just like mktime does. For instance, (2003, 11, -1) will be translated to (2003, 10, 31). This method allows math operations on dates.
This function can not be called statically.
boolean Calendar::build (
array $sDates = array()
)
Implemented by Calendar subclasses
$sDates
containing Calendar objects to select (optional)
abstract
This function can not be called statically.
mixed Calendar::fetch (
)
This package is not documented yet.
returns either an object subclass of Calendar or false
throws no exceptions thrown
This function can not be called statically.
array Calendar::fetchAll (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
int Calendar::getTimestamp (
)
Format of timestamp depends on Calendar_Engine implementation being used
returns timestamp
throws no exceptions thrown
This function can not be called statically.
Calendar_Validator& Calendar::getValidator (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean Calendar::isSelected (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean Calendar::isValid (
)
Determine whether this date is valid with the bounds determined by the Calendar_Engine. The call is passed on to Calendar_Validator::isValid
throws no exceptions thrown
This function can not be called statically.
int Calendar::nextDay (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 12 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::nextHour (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 14 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::nextMinute (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 25 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::nextMonth (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 6 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::nextSecond (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 45 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::nextYear (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 2004 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::prevDay (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 10 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::prevHour (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 13 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::prevMinute (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 23 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::prevMonth (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 4 or Unix timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::prevSecond (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 43 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::prevYear (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 2002 or timestamp
throws no exceptions thrown
This function can not be called statically.
void Calendar::setSelected (
boolean $state
= true
)
This package is not documented yet.
$state
state whether Calendar subclass
throws no exceptions thrown
This function can not be called statically.
boolean Calendar::setSelection (
array $sDates
)
This package is not documented yet.
$sDates
abstract
This function can not be called statically.
void Calendar::setTimestamp (
int $ts
)
Defines the calendar by a Unix timestamp, replacing values passed to the constructor
$ts
Unix timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::size (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
int Calendar::thisDay (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 11 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::thisHour (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 14 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::thisMinute (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 24 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::thisMonth (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 5 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::thisSecond (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 44 or timestamp
throws no exceptions thrown
This function can not be called statically.
int Calendar::thisYear (
string $format = 'int'
)
This package is not documented yet.
$format
'int', 'timestamp' , 'array' or 'object'
returns e.g. 2003 or timestamp
throws no exceptions thrown
This function can not be called statically.
Calendar_Year API.
void constructor Calendar_Year::Calendar_Year (
int $y
)
This package is not documented yet.
$y
year e.g. 2003
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Year::build (
array$sDates = array()
, int$firstDay
= null
)
Note: by defining the constant CALENDAR_MONTH_STATE you can control what class of Calendar_Month is built e.g.;
1 require_once 'Calendar/Calendar_Year.php';
2
define('CALENDAR_MONTH_STATE',CALENDAR_USE_MONTH_WEEKDAYS); // Use Calendar_Month_Weekdays
3 // define ('CALENDAR_MONTH_STATE',CALENDAR_USE_MONTH_WEEKS); // Use Calendar_Month_Weeks
4
It defaults to building Calendar_Month objects.
$sDates
(optional) array of Calendar_Month objects representing selected months
$firstDay
(optional) first day of week (e.g. 0 for Sunday, 2 for Tuesday etc.)
throws no exceptions thrown
This function can not be called statically.
Calendar_Month API.
void constructor Calendar_Month::Calendar_Month (
int $y
, int $m
, int $firstDay
= null
)
This package is not documented yet.
$y
year e.g. 2003
$m
month e.g. 5
$firstDay
(optional) unused in this class
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Month::build (
array $sDates = array()
)
as there are days in the month
$sDates
(optional) Calendar_Day objects representing selected dates
throws no exceptions thrown
This function can not be called statically.
Calendar_Month_Weekdays API.
void constructor Calendar_Month_Weekdays::Calendar_Month_Weekdays (
int $y
, int $m
, int $firstDay
= false
)
This package is not documented yet.
$y
year e.g. 2003
$m
month e.g. 5
$firstDay
(optional) first day of week (e.g. 0 for Sunday, 2 for Tuesday etc.)
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Month_Weekdays::build (
array $sDates = array()
)
This package is not documented yet.
$sDates
(optional) Calendar_Day objects representing selected dates
throws no exceptions thrown
This function can not be called statically.
Calendar API.
void constructor Calendar_Month_Weeks::Calendar_Month_Weeks (
int $y
, int $m
, int $firstDay
= false
)
This package is not documented yet.
$y
year e.g. 2003
$m
month e.g. 5
$firstDay
(optional) first day of week (e.g. 0 for Sunday, 2 for Tuesday etc.)
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Month_Weeks::build (
array $sDates = array()
)
builds Calendar_Day object in tabular form (with Calendar_Day->empty)
$sDates
(optional) Calendar_Week objects representing selected dates
throws no exceptions thrown
This function can not be called statically.
Calendar_Week API.
void constructor Calendar_Week::Calendar_Week (
int $y
, int $m
, int $d
, int $firstDay
= false
)
This package is not documented yet.
$y
year e.g. 2003
$m
month e.g. 5
$d
a day of the desired week
$firstDay
(optional) first day of week (e.g. 0 for Sunday, 2 for Tuesday etc.)
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Week::build (
array $sDates = array()
)
This package is not documented yet.
$sDates
(optional) Calendar_Day objects representing selected dates
throws no exceptions thrown
This function can not be called statically.
mixed Calendar_Week::nextWeek (
string $format
)
This package is not documented yet.
$format
['timestamp' | 'n_in_month' | 'n_in_year' | 'array']
throws no exceptions thrown
This function can not be called statically.
mixed Calendar_Week::prevWeek (
string $format
)
This package is not documented yet.
$format
['timestamp' | 'n_in_month' | 'n_in_year' | 'array']
throws no exceptions thrown
This function can not be called statically.
mixed Calendar_Week::thisWeek (
string $format = 'n_in_month'
)
This package is not documented yet.
$format
['timestamp' | 'n_in_month' | 'n_in_year' | 'array']
throws no exceptions thrown
This function can not be called statically.
Calendar_Day API.
void constructor Calendar_Day::Calendar_Day (
int $y
, int $m
, int $d
)
This package is not documented yet.
$y
year e.g. 2003
$m
month e.g. 8
$d
day e.g. 15
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Day::build (
array $sDates = array()
)
This package is not documented yet.
$sDates
(optional) Calendar_Hour objects representing selected dates
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Day::isEmpty (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Day::isFirst (
)
Only relevant when Day is created by Calendar_Month_Weekdays::build()
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Day::isLast (
)
Only relevant when Day is created by Calendar_Month_Weekdays::build()
throws no exceptions thrown
This function can not be called statically.
Calendar_Hour API.
void constructor Calendar_Hour::Calendar_Hour (
int $y
, int $m
, int $d
, int $h
)
This package is not documented yet.
$y
year e.g. 2003
$m
month e.g. 5
$d
day e.g. 11
$h
hour e.g. 13
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Hour::build (
array $sDates = array()
)
This package is not documented yet.
$sDates
(optional) Calendar_Minute objects representing selected minutes
throws no exceptions thrown
This function can not be called statically.
Calendar_Minute API.
void constructor Calendar_Minute::Calendar_Minute (
int $y
, int $m
, int $d
, int $h
, int $i
)
This package is not documented yet.
$y
year e.g. 2003
$m
month e.g. 5
$d
day e.g. 11
$h
hour e.g. 13
$i
minute e.g. 31
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Minute::build (
array $sDates = array()
)
This package is not documented yet.
$sDates
(optional) Calendar_Second objects representing selected seconds
throws no exceptions thrown
This function can not be called statically.
Calendar_Second API.
void constructor Calendar_Second::Calendar_Second (
int $y
, int $m
, int $d
, int $h
, int $i
, int $s
)
This package is not documented yet.
$y
year e.g. 2003
$m
month e.g. 5
$d
day e.g. 11
$h
hour e.g. 13
$i
minute e.g. 31
$s
second e.g. 45
throws no exceptions thrown
This function can not be called statically.
NULL Calendar_Second::build (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
NULL Calendar_Second::fetch (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
NULL Calendar_Second::fetchAll (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
NULL Calendar_Second::size (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
Calendar_Validation_Error API.
void constructor Calendar_Validation_Error::Calendar_Validation_Error (
string $unit
, int $value
, string $message
)
This package is not documented yet.
$unit
Date unit (e.g. month,hour,second)
$value
Value of unit which failed test
$message
Validation error message
throws no exceptions thrown
This function can not be called statically.
string Calendar_Validation_Error::getMessage (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
string Calendar_Validation_Error::getUnit (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
int Calendar_Validation_Error::getValue (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
string Calendar_Validation_Error::toString (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
Calendar_Validator API.
void constructor Calendar_Validator::Calendar_Validator (
object subclass &$calendar
)
This package is not documented yet.
&$calendar
of Calendar
throws no exceptions thrown
This function can not be called statically.
mixed Calendar_Validator::fetch (
)
This package is not documented yet.
returns either Calendar_Validation_Error or false
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Validator::isValid (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Validator::isValidDay (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Validator::isValidHour (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Validator::isValidMinute (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Validator::isValidMonth (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Validator::isValidSecond (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean Calendar_Validator::isValidYear (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
Name | Value | Line Number |
---|---|---|
CALENDAR_ENGINE | 'UnixTS' | 39 |
CALENDAR_ROOT | 'Calendar'.DIRECTORY_SEPARATOR | 32 |
Name | Value | Line Number |
---|---|---|
CALENDAR_VALUE_TOOLARGE | 'Too large: max = ' | 34 |
CALENDAR_VALUE_TOOSMALL | 'Too small: min = ' | 31 |
Name | Value | Line Number |
---|---|---|
CALENDAR_USE_MONTH | 1 | 43 |
CALENDAR_USE_MONTH_WEEKDAYS | 2 | 44 |
CALENDAR_USE_MONTH_WEEKS | 3 | 45 |
PEAR Date is a generic class to represent and work with dates. It does not use timestamps which means it can be used for dates before 1970 and after 2038. The class provides methods to convert a date in different formats, calculate differences, weekdays and more.
Following the saying "learning by doing", we start with some examples.
The first step to work with the class is to instantiate a Date, object.
Creating a Date object
<?php
require_once 'Date.php';
//without parameter == now
$now = new Date();
//pass a string in ISO format
$longAgo = new Date('1813-02-23T05:34:23');
//create a Date object from a PHP unix timestamp
$timestamp = time();
$nearlyNow = new Date($timestamp);
//make a copy of an existing Date object
$copyDate = new Date($longAgo);
?>
After finishing your work with the date object, you probably want to have it back. This can be done in different ways, for example by using getTime() or getDate()
Getting the date in different formats
<?php
require_once 'Date.php';
$now = new Date();
//UNIX timestamp: 1183054688
echo $now->getTime();
//ISO formatted date: 2007-06-28 20:18:08
echo $now->getDate();
?>
Date has a lot more methods to output your date, but that is subject of a later chapter.
Now that we know how to create a Date instance, we'll do some easy tasks.
You often have the task to know which time span lies between two dates. With Date, this is easy to accomplish. First we use setFromDateDiff() on a fresh Date_Span object and then toDays() to get the exact number of days between the two dates.
Calculating a time span
<?php
require_once 'Date.php';
$someDate = new Date('1813-02-23T05:34:23');
$otherDate = new Date('1789-12-21T18:23:42');
$span = new Date_Span();
$span->setFromDateDiff($someDate, $otherDate);
//time span in days: 8463,46575231
echo $span->toDays();
//time span in full years: 23
echo (int)($span->toDays() / 365);
?>
Date_Span works, unlike Date, internally with integers, which means that you have a precision of 32 bit or ~68 years. Date span objects with more that 67 years will lead to unexpected results!
Date can help you working with time zones. An array with all supported timezones can be retrieved by using Date_Timezone::getAvailableIDs(), statically as in $list = Date_TimeZone::getAvailableIDs();.
convertTZ
converts the Date object's
internal settings to the given time zone.
Using
format()
you can display the timezone
setting. With Date_TimeZone's
getDefault
method the default time
zone for this computer can be obtained.
Converting timezones
<?php
require_once 'Date.php';
//assume it's 2007-06-28 18:42:12
$now = new Date();
$timezone = new Date_TimeZone('Australia/Adelaide');
//convert the date object to the timezone
$now->convertTZ($timezone);
//will give you: 2007-06-29 02:12:12
echo $now->getDate();
//now with timezone: 2007-06-29 02:12:12+09:30
echo $now->format('%Y-%m-%d %H:%M:%S%O');
//switch back
$defaultZone = Date_TimeZone::getDefault();
$now->convertTZ($defaultZone);
//we have our normal zone now: 2007-06-28 18:42:12+02:00
echo $now->format('%Y-%m-%d %H:%M:%S%O');
?>
Once you have an array of Date objects, you might want to sort it. The class provides a static method compare() that helps with this.
Sorting dates
<?php
require_once 'Date.php';
$dates = array(
new Date('1813-02-23T05:34:23'),
new Date(),
new Date('1714-12-21T18:23:42'),
);
usort($dates, array('Date', 'compare'));
/*
* prints the dates correctly sorted:
* 1714-12-21 18:23:42
* 1813-02-23 05:34:23
* 2007-06-28 20:59:39
*/
foreach ($dates as $date) {
echo $date->getDate() . "\n";
}
?>
The auto-generated API documentation is very valuable to get information about this packages' methods and which parameters they require. But of course you first need to have an overview about the abilities of the class.
There are a number of simple getter methods that let you retrieve single pieces of a date currently set.
Date does also have some advanced methods to obtain data from it.
Date::getDayName()
returns the name of the day in short (Mon
)
or long form (Monday
)
Date::getMonthName()
returns the name of the month in short (Jan
) or
long form (January
)
Date::getDayOfWeek() calculates the number of the day in the week (0-6, Sunday being 0)
Date::getWeekOfYear() returns the week number of this date
Holiday calculation package.
This chapter describes how to use PEAR::Date_Holidays
Date_Holidays is a driver-based holiday calculation package. It helps you check whether a specific date is a holiday in a specific country or religion. Furthermore you can calculate the date of any holiday supported in the driver for the country, region or religion.
Currently the following drivers are supported:
Australia
Austria
Brazil
Christian, calculates Christian holidays (used as base driver for other drivers)
Croatia
Denmark
Discordian
England & Wales
Finland
Germany, calculates German holidays
Ireland, calculates Irish holidays
Italy
Japan
Jewish, calculates Jewish holidays
The Netherlands
Norway
PHP.net
Portugal
Romania
San Marino
Slovenia
Spain
Sweden, calculates Swedish holidays
Ukraine
USA, calculates holidays in the United States of America
UNO, calculates UNO (United Nations Organization) holidays
Venezeula
Composite, a driver that is used to combine any number of the other drivers so they can be queried at once.
If you have written a custom driver for Date_Holidays that could be included in the distribution, please contact the package maintainers or open a feature request and attach a patch in the bug tracking tool.
Date_Holidays supports I18N by storing the names of the different holidays in INI files for each language. These files will be stored in the data directory of your PEAR installation.
This example shows you, how to calculate the Easter date of 2005.
<?php
require_once "Date/Holidays.php";
$germany = &Date_Holidays::factory('Germany', 2004, 'en_EN');
if (Date_Holidays::isError($germany)) {
die('Factory was unable to produce driver-object');
}
$easter = &$germany->getHoliday('easter', 'de_DE');
if (!Date_Holidays::isError($easter)) {
print_r($easter->toArray());
}
?>
This will return an array in the following format:
Array ( [internalName] => easter [title] => Easter Sunday [date] => date Object ( [year] => 2004 [month] => 04 [day] => 11 [hour] => 0 [minute] => 0 [second] => 0 [tz] => date_timezone Object ( [id] => UTC [longname] => Coordinated Universal Time [shortname] => UTC [hasdst] => [dstlongname] => Coordinated Universal Time [dstshortname] => UTC [offset] => 0 [default] => ) ) )
Generate textual time differences that are easily understandable by humans ("5 minutes ago").
The package supports minutes, hours, days, weeks, months and years.
Just include the Date/HumanDiff.php
file,
instantiate a Date_HumanDiff object and run
get()
on it.
<?php
require_once 'Date/HumanDiff.php';
$dh = new Date_HumanDiff();
echo $dh->get(time() - 5 * 60) . "\n";
//prints out "5 minutes ago"
?>
get() accepts two parameters: The timestamp and the reference time. If the reference time is not provided, the current time is used. Both parameters may be unix timestamps, DateTime objects as well as strings that can be converted to a unix timestamp with strtotime.
The difference between timestamp and reference time is then converted into a human readable string.
Date_HumanDiff ships a number of translations that
provide localized messages.
Version 0.4.0 comes with
German (de
),
Greek (el
)
and Persian (fa
)
translations.
The
setLocale
method accepts locale names
(de_AT
, en_US
)
as well as ISO 639-1 two-letter language codes
(de
, fr
, en
).
If the combination of language code + country code does not match,
the language code alone is tried.
If that also fails, the english variant is used.
Using the German translation
<?php
require_once 'Date/HumanDiff.php';
$dh = new Date_HumanDiff();
$dh->setLocale('de_DE');
echo $dh->get(time() - 5 * 60) . "\n";
//prints out "vor 5 Minuten"
?>
When generating HTML you should keep in mind that the relative time difference generated by Date_HumanDiff is only valid for the very moment it got created. A couple of seconds later it may be wrong - if it was "one minute ago", it needs to be "two minutes ago" then.
The first step to cater for that problem is to provide the original
date and time value together with the relative difference.
The Microformats project has a matching convention for this task,
the datetime-design-pattern,
and HTML5 has a semantically equivalent element: <time>
.
Both methods use RFC 3339
style date and time values, for example
1996-12-19T16:39:57-08:00
.
Expressing the original timestamp using a microformat
<?php
require_once 'Date/HumanDiff.php';
$dh = new Date_HumanDiff();
//fictive creation date of e.g. a blog post
$creationDate = 1346277600;
$relDiff = $dh->get($creationDate);//age of blog post
?>
<abbrev title="<?php echo date('c', $creationDate); ?>">
<?php echo htmlspecialchars($relDiff); ?>
</abbrev>
Expressing the original timestamp with the HTML5 time tag
<?php
require_once 'Date/HumanDiff.php';
$dh = new Date_HumanDiff();
//fictive creation date of e.g. a blog post
$creationDate = 1346277600;
$relDiff = $dh->get($creationDate);//age of blog post
?>
<time datetime="<?php echo date('c', $creationDate); ?>">
<?php echo htmlspecialchars($relDiff); ?>
</time>
Provides packages for encryption, decryption, signing, verifying and key management.
Classes for generating packets for various CHAP Protocols
CHAP is a part usualy of PPP (Point-to-Point Protocol) software, implemented in the authentication subsystem. CHAP avoid's sending plaintext passwords over an insecure link. The traditional CHAP-MD5 needs the plaintext password stored on the server. MS-CHAP doesen't need this, but also needs the password either as NT-Hash and/or as LAN-Manager-Hash. LAN-Manager-Hashes are weak and shouldn't be used anymore.
This package provides 3 classes:
Crypt_CHAP::Crypt_CHAP is an abstract base class.
In order to get the MS-CHAP* to work you need the mhash extension.
string Crypt_CHAP::generateChallenge (
string $varname = 'challenge'
, int $size = 8
)
This method generates a new random challenge and stores it in the given property, the default size of the challenge is 8 bytes.
string $varname
- name of the property for storing the challenge
int $size
- the size of the challenge
string
- a String containing the challenge
This function can not be called statically.
Using Crypt_CHAP::generateChallenge()
<?php
require_once 'File/SMBPasswd.php';
$cr = new Crypt_CHAP_MD5();
echo bin2hex($cr->challenge);
// or generate a new challenge
echo bin2hex(echo $cr->generateChallenge());
?>
void
Crypt_CHAP_MD5::Crypt_CHAP_MD5 (
)
Generates a new Object for generating CHAP-MD5 compliant pakets.
string Crypt_CHAP_MD5::challengeResponse (
)
This method generates the challenge-response paket, by doing: md5(ID + Password + Challenge).
string
- a String containing the challenge-response paket
This function can not be called statically.
Using Crypt_CHAP_MD5::challengeResponse()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MD5();
$cr->password = 'MyPw';
echo bin2hex($cr->challengeResponse());
?>
void
Crypt_CHAP_MSv1::Crypt_CHAP_MSv1 (
)
Generates a new Object for generating MS-CHAPv1 compliant pakets.
You need the mhash extension in order to get this class to work.
string Crypt_CHAP_MSv1::challengeResponse (
)
This method generates the Challenge-Response paket.
string
- a String containing the challenge-response paket
This function can not be called statically.
Using Crypt_CHAP_MSv1::challengeResponse()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MSv1();
$cr->chapid = 1;
$cr->password = 'MyPw';
echo bin2hex($cr->challengeResponse());
?>
string Crypt_CHAP_MSv1::lmChallengeResponse (
)
This method generates the Challenge-Response using the LAN-Manager Hash.
string
- a String containing the challenge-response
This function can not be called statically.
Using Crypt_CHAP_MSv1::lmChallengeResponse()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MSv1();
$cr->password = 'MyPw';
echo bin2hex($cr->lmChallengeResponse());
?>
string Crypt_CHAP_MSv1::ntChallengeResponse (
)
This method generates the Challenge-Response using the NT-Hash.
string
- a String containing the challenge-response
This function can not be called statically.
Using Crypt_CHAP_MSv1::ntChallengeResponse()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MSv1();
$cr->password = 'MyPw';
echo bin2hex($cr->ntChallengeResponse());
?>
string Crypt_CHAP_MSv1::ntPasswordHash (
string $password = ''
)
This method generates NT-Hash from the given plaintext-password or from the password property. The NT-Hash is computed like this: md4(str2unicode(plaintext))
string $password
- the password to be hashed
string
- a String containing the NT-Hash
This function can not be called statically.
Using Crypt_CHAP_MSv1::ntPasswordHash()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MSv1();
$cr->password = 'MyPw';
echo bin2hex($cr->ntPasswordHash());
// or
echo bin2hex($cr->ntPasswordHash('MySecret'));
?>
string Crypt_CHAP_MSv1::lmPasswordHash (
string $password = ''
)
This method generates LAN-Manager-Hash from the given plaintext-password or from the password property.
string $password
- the password to be hashed
string
- a String containing the LAN-Manager-Hash
This function can not be called statically.
LAN-Manager Hash are weak and should not be used anymore.
Using Crypt_CHAP_MSv1::lmPasswordHash()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MSv1();
$cr->password = 'MyPw';
echo bin2hex($cr->lmPasswordHash());
// or
echo bin2hex($cr->lmPasswordHash('MySecret'));
?>
string Crypt_CHAP_MSv1::str2unicode (
string $str
)
This method generates converts the given string to unicode.
string $str
- the string to be unicoded
string
- a String containing unicode representation of the given string
This function can not be called statically.
Using Crypt_CHAP_MSv1::str2unicode()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MSv1();
echo bin2hex($cr->str2unicode('MyPw'));
?>
string Crypt_CHAP_MSv1::response (
bool $lm
= false
)
This method generates the response paket, containing the NT-Challenge-Response and/or the LM-Challenge-Response. By default the LM-Challenge-Response is not included.
bool $lm
- wether including the LM-Challenge-Response
string
- a String containing the paket
This function can not be called statically.
Using Crypt_CHAP_MSv1::response()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MSv1();
$cr->password = 'MyPw';
echo bin2hex($cr->response());
?>
void
Crypt_CHAP_MSv2::Crypt_CHAP_MSv2 (
)
Generates a new Object for generating MS-CHAPv2 compliant pakets. This version of CHAP uses also a Peer-Challenge, LM-Hashes are not used anymore. The Constructor generates automatically a Peer-Challenge and the Authenticator-Challenge.
string Crypt_CHAP_MSv2::challengeHash (
)
This method generates the (SHA1) Challenge-Hash containing the authenticator and the peer challenge and the username.
string
- a String containing the Challenge-Hash
This function can not be called statically.
Using Crypt_CHAP_MSv2::challengeHash()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MSv2();
$cr->username = 'billy';
$cr->peerChallenge = $peerChallenge;
$cr->authChallenge = $authChallenge;
echo bin2hex($cr->challengeHash());
?>
string Crypt_CHAP_MSv2::ntPasswordHashHash (
string $nthash
)
This method generates an MD4 Hash from the given NT-Hash.
string $nthash
- the NT-Hash to be hashed
string
- a String containing the NT-Hash
This function can not be called statically.
Using Crypt_CHAP_MSv2::ntPasswordHashHash()
<?php
require_once 'Crypt/CHAP.php';
$cr = new Crypt_CHAP_MSv2();
$nthash = $cr->ntPasswordHash('MyPw');
echo bin2hex($cr->ntPasswordHashHash($nthash));
?>
Crypt_GPG is a PHP package to interact with the GNU Privacy Guard (GnuPG). GnuPG is a free and open-source implementation of the OpenPGP protocol, providing key management, data encryption and data signing. Crypt_GPG provides an object-oriented API for performing OpenPGP actions using GnuPG.
GnuPG is distributed as an executable program with a command-line argument syntax for performing actions. Crypt_GPG uses PHP's program execution functions to run GnuPG as a subprocess, performing the desired action. Crypt_GPG automatically handles process control, stream handling and error checking of the GnuPG subprocess. Crypt_GPG uses PHP streams internally for most actions, allowing (among other things) any streamable resource to be used with the Crypt_GPG file commands.
Though GnuPG can support symmetric-key cryptography, this package is intended only to facilitate public-key cryptography.
The basic internal overview of a GnuPG command executed in Crypt_GPG is as follows:
Crypt_GPG handles all these details internally so GnuPG can be used with minimal effort from the developer.
Outlines how to generate a GnuPG key for use with Crypt_GPG.
Crypt_GPG does not yet support generating GnuPG keys. Generating a GnuPG key for use with Crypt_GPG is much the same as generating any other GnuPG key on a system.
Though Crypt_GPG supports specifying the keyring to use, Crypt_GPG, by default, uses the keyring of the current user. If using Crypt_GPG with a webserver such as Apache, the current user is the Apache user and the key will need to be generated as the Apache user. To do this, run the gen-key command as:
$
sudo -u apache gpg --gen-key
The following example walks through the process of generating a key that supports both encrypting and signing. First, run the command:
$
gpg --gen-key
This will display the following copyright information and a list of available key types:
gpg (GnuPG) 1.4.6; Copyright (C) 2006 Free Software Foundation, Inc.
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. See the file COPYING for details.
Please select what kind of key you want:
(1) DSA and Elgamal (default)
(2) DSA (sign only)
(5) RSA (sign only)
Your selection? 1
DSA keypair will have 1024 bits.
Select (1) DSA and Elgamal (default) to allow the generated key to both encrypt and sign data. This will generate a public-private key pair in the GPG keyring and prompt for the size of the encryption key:
ELG-E keys may be between 1024 and 4096 bits long. What keysize do you want? (2048)
Select the default value of 2048. Enter greater or fewer bits depending on how secure the encryption must be. The default value is considered safe for most applications. GnuPG then prompts for the time period over which the generated key will be valid:
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y
Unless the key needs to expire after a certain time period (preventing subsequent decryption), a key that does not expire should be used. Next, enter the three parts of the key's user id. The first part of the user id is the real name of the person or organization that will use the key to sign or encrypt data. The second part is an email address and the third is a comment about the key. Both the email address and comment are optional:
You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"
Real name: Test User
Email address: test@example.com
Comment: test key
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
After entering the primary user id of the new key, the passphrase must be selected. A secret passphrase is essential to securing encrypted data. Guessable passphrases will render encryption useless. For critical data such as credit card numbers, a non-dictionary word that is at least 8 characters long is recommended.
You need a Passphrase to protect your secret key. Enter passphrase: Repeat passphrase:
Following the passphrase, GnuPG will gather entropy for a period to ensure the generated key uses sutitably random numbers. When enough entropy is collected the key is generated and added to the keyring:
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
... snip ...
gpg: key DB15A2C9 marked as ultimately trusted
public and secret key created and signed.
gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
pub 1024D/DB15A2C9 2008-08-05
Key fingerprint = F94A F628 5725 7147 0569 F9FF E995 8292 DB15 A2C9
uid Test User (test key) <test@example.com>
sub 2048g/6AD96F48 2008-08-05
At this point the key is in the GnuPG keyring and ready to be used by Crypt_GPG.
The Crypt_GPG class is the main entry point for using Crypt_GPG. To use GnuPG in your project, create an instance of Crypt_GPG and then call methods on the object to perform GPG actions.
The Crypt_GPG class supports several options, which may be specified in the constructor. Options may be used for the following:
Sometimes, specifying the location of the keyring is required. One such
case case is when using Crypt_GPG from the context
of a Web page when the Web-server's user does not have a home directory, or
does not have write access to its home directory. This is often the case on
shared hosts. In this case, you should specify the GnuPG keyring location
as an existing writeable directory. This is done using the
homedir
option. For example:
<?php
require_once 'Crypt/GPG.php';
// Specify homedir as an existing writeable directory if the web user
// does not have a home directory, or if the web user's home directory
// is not writeable.
$gpg = new Crypt_GPG(array('homedir' => '/my/writeable/directory'));
?>
If, for some reason, Crypt_GPG does not seem to work
correctly, detailed debugging information may be turned on. This is done
using the debug
option. When providing bug reports for
the Crypt_GPG package, you may be asked to turn on deubg
mode. Example:
<?php
require_once 'Crypt/GPG.php';
// Enable debug mode. This will dump a lot of output when Crypt_GPG
// actions are performed.
$gpg = new Crypt_GPG(array('debug' => true));
?>
Crypt_GPG works by talking to the GnuPG subprocess. As a
result, it needs to know the location of the GnuPG binary to work properly.
In most cases, Crypt_GPG will detect the location of the
GnuPG binary automatically. If the location is detected incorrectly, or if
the GnuPG binary is installed in a custom location, the
binary
option may be used. For example:
<?php
require_once 'Crypt/GPG.php';
// Specify custom location of GnuPG binary.
$gpg = new Crypt_GPG(array('binary' => '/home/joe/bin/gpg'));
?>
If an invalid binary location is specified, Crypt_GPG will throw an exception.
Outlines how to refer to GnuPG keys in code using Crypt_GPG.
Crypt_GPG supports referring to a key in several ways.
The most definitive way to refer to a specific key is to use the key's
fingerprint. Key fingerprints are generated by performing a checksum on the
actual content of a key. A fingerprint appears as a string of hexadecimal
characters, sometimes separated by spaces or colons. For example:
F94A F628 5725 7147 0569 F9FF E995 8292 DB15 A2C9
. The
fingerprint of a key can be retrieved using the
Crypt_GPG::getKeys()
and
Crypt_GPG::getFingerprint()
methods. Alternatively, the following command may be used to list keys on
a console:
$
gpg --list-keys --with-fingerprint --with-fingerprint
--with-fingerprint is doubled intentionally.
Keys may also be referenced by the key id. The key id is an eight-octal long
hexadecimal number. The key id can be obtained using
Crypt_GPG::getKeys()
.
Though rare, it is possible to have two keys with the same key id. The key
id may also be obtained using the following command:
$
gpg --list-keys --with-colons
The key id is the fifth colon-separated field. A partial key id may also be used to reference a key. The partial key id is the lower four octals of a full key id and may be obtained using the following command:
$
gpg --list-keys
Lastly, keys may be referenced by all or part of the key's user id. For example, Test User (test key) <test@example.com>, Test User <test@example.com> and test@example.com may all be used to refer to the same key. When there is more than on key in the keyring with the same user id (or partial user id), the first key is used. In these cases, it is important to use a more specific identifier to ensure the correct key is used. In general, unless the keyring contains many keys, the less specific but more convenient form of test@example.com is fine to use.
The following examples assume you have created a key following the instructions for generating a key that can both encrypt and sign data. The example key has a user id of test@example.com and a passphrase of test. All signature data is fictitious, but is formatted like real signature data.
<?php
require_once 'Crypt/GPG.php';
$gpg = new Crypt_GPG();
$gpg->addSignKey('test@example.com', 'test');
$signature = $gpg->signFile($filename, Crypt_GPG::SIGN_MODE_DETACHED);
echo "Package signature is: ", $signature, "\n";
?>
<?php
require_once 'Crypt/GPG.php';
$signature = <<<DATA
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQBIl9Tf6ZWCktsVoskRAoAKAJ9VkbFDTSGY2ygaEGBcMOE8Or9puwCgppYm
0qq0bhtw5vsi0cJF5oC52RY=
=VfxI
-----END PGP SIGNATURE-----
DATA;
$gpg = new Crypt_GPG();
$signatures = $gpg->verifyFile($filename, $signature);
if ($signatures[0]->isValid()) {
echo "Package is valid.\n";
} else {
echo "Package is invalid!\n";
}
?>
<?php
require_once 'Crypt/GPG.php';
$gpg = new Crypt_GPG();
$gpg->addEncryptKey('test@example.com');
// you can use any fopen-able stream
$gpg->encryptFile('http://example.com/file.html', '~/file.html.asc');
?>
<?php
require_once 'Crypt/GPG.php';
// ... connect to database ...
$card_number = '411111111111';
$card_type = 'visa';
$gpg = new Crypt_GPG();
$gpg->addEncryptKey('test@example.com');
$encrypted = $gpg->encrypt($card_number);
$sql = sprintf('insert into payments (card_type, card_number) ' .
'values (%s, %s)',
mysql_real_escape_string($card_type),
mysql_real_escape_string($encrypted));
mysql_exec($sql);
?>
<?php
require_once 'Crypt/GPG.php';
// ... connect to database ...
$gpg = new Crypt_GPG();
$gpg->addDecryptKey('test@example.com', 'test');
$sql = 'select card_type, card_number from payments';
$rs = mysql_query($sql);
while ($row = mysql_fetch_object($rs)) {
echo "Card type: ", $row->card_type, "\n";
echo "Card number: ", $gpg->decrypt($row->card_number), "\n";
}
?>
<?php
require_once 'Crypt/GPG.php';
$gpg = new Crypt_GPG();
echo "My public key is: ", $gpg->exportPublicKey('test@example.com'), "\n";
echo "My key fingerprint is: ",
$gpg->getFingerprint('test@example.com', Crypt_GPG::FORMAT_CANONICAL), "\n";
?>
<?php
require_once 'Crypt/GPG.php';
$data = 'Hello, World!';
$gpg = new Crypt_GPG();
$gpg->addSignKey('test@example.com', 'test');
$signedData = $gpg->sign($data, Crypt_GPG::SIGN_MODE_CLEAR);
echo "Clearsigned message is: ", $signedData, "\n";
?>
<?php
require_once 'Crypt/GPG.php';
$signedData = <<<DATA
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hello, World!
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFIl9Sb6ZWCktsVoskRArWDAJ9D5mq6p+4JnBy11OaAhnIA+uRSSACgoM5T
WcUHQ9pKf9PvNUn1Izy6c9E=
=k8+7
-----END PGP SIGNATURE-----
DATA;
$gpg = new Crypt_GPG();
$signatures = $gpg->verify($signedData);
if ($signatures[0]->isValid()) {
echo "Message is valid.\n";
} else {
echo "Message is invalid!\n";
}
?>
Provides packages for event based development.
Dispatch notifications using PHP callbacks
Event_Dispatcher acts as a notification dispatch table. It is used to notify other objects of interesting things. This information is encapsulated in Event_Notification objects.
Client objects register themselves with the Event_Dispatcher as observers of specific notifications posted by other objects. When an event occurs, an object posts an appropriate notification to the Event_Dispatcher. The Event_Dispatcher dispatches a message to each registered observer, passing the notification as the sole argument.
Event_Dispatchers allows you to use event bubbling similar to JavaScript's event management. If an event is not handled by the dispatcher that triggered the event, it may bubble up to the next dispatcher.
The following examples show you how to use Event_Dispatcher to create more flexible applications.
Basic example
<?php
require_once 'Event/Dispatcher.php';
/**
* Dummy class that simulated authentication
*/
class Auth
{
var $_dispatcher = null;
var $_user;
function Auth(&$dispatcher)
{
$this->_dispatcher = &$dispatcher;
}
function login($username, $password)
{
// Your code that authenticates goes here
// ....
// imagine $this->_user contains a User object
$this->_dispatcher->post($this->_user, 'onLogin');
}
}
function logAuth(&$notification)
{
$user = &$notification->getNotificationObject();
$username = $user->getUsername();
// write logfile
error_log("$username logged in.", 3, '/tmp/auth.log');
}
$dispatcher = &Event_Dispatcher::getInstance();
// catch all onLogin events to write a logfile
$dispatcher->addObserver('logAuth', 'onLogin');
$auth = &new Auth($dispatcher);
// simulate login
$auth->login($_GET['user'], $_GET['pass']);
?>
In this example, Event_Dispatcher is used to allow observers to hook into the authentication process. Whenever a user authenticates, a notification onLogin is sent.
This can be used to write logfiles or block the application for other users.
Cancelling notifications
<?php
require_once 'Event/Dispatcher.php';
/**
* Dummy class that simulated authentication
*/
class Auth
{
var $_dispatcher = null;
var $_user;
function Auth(&$dispatcher)
{
$this->_dispatcher = &$dispatcher;
}
function login($username, $password)
{
// Your code that authenticates goes here
// ....
// imagine $this->_user contains a User object
$notification = $this->_dispatcher->post($this->_user, 'onLogin');
if ($notification->isNotificationCancelled()) {
echo "You are not allowed to login";
$this->_user->logout();
}
}
}
function logAuth(&$notification)
{
$user = &$notification->getNotificationObject();
$username = $user->getUsername();
// If a special user authenticated, cancel
// the notification
if ($username === 'foo') {
$notification->cancelNotification();
} else {
// write logfile
error_log("$username logged in.", 3, '/tmp/auth.log');
}
}
$dispatcher = &Event_Dispatcher::getInstance();
// catch all onLogin events to write a logfile
$dispatcher->addObserver('logAuth', 'onLogin');
$auth = &new Auth($dispatcher);
// simulate login
$auth->login($_GET['user'], $_GET['pass']);
?>
In this case, the cancelNotification() method is used to cancel the notification if a certain user tries to authenticate.
The login method has been changed as well to check whether the notification has been cancelled and to take the necessary steps.
This allows you to add some flexible rules to your authentication system.
object Event_Dispatcher Event_Dispatcher::getInstance (
string $name = '__default'
)
Create a new Event_Dispatcher object.
As Event_Dispatcher uses the singleton pattern, you must not use the new operator to create a new instance of Event_Dispatcher, but use getInstance() instead.
If you need more than one instance of Event_Dispatcher, pass different names to the method.
string $name = '__default'
object Event_Dispatcher Event_Dispatcher instance
This function should be called statically.
string Event_Dispatcher::getName (
)
Get the name of the dispatcher.
The name of the dispatcher is used as a unique identifier. This is important for the methods getInstance() and removeNestedDispatcher().
string name of the dispatcher
This function can not be called statically.
void Event_Dispatcher::addObserver (
mixed $callback
, string $nName = EVENT_DISPATCHER_GLOBAL
, string $class = ''
)
Adds a new observer to the dispatcher.
Observers are PHP callbacks. That means you may either pass a function name as a string or an array containing an object or class and a method to call.
The callback is used as a signature for the observer, which allows you to remove it by passing the exact same parameters to removeObserver().
mixed $callback
Callback to notity, may either be a string containing the name of a global function or an array containing class or object and the name of the method to call.
string $nName = EVENT_DISPATCHER_GLOBAL
Acts as a filter: notify the observer only if the notification name matches the name passed in this parameter. Use EVENT_DISPATCHER_GLOBAL if the observer should be notified regardles of the notification name.
string $class = ''
Acts as a filter: notify the observer only if the sender of the notification matches the class passed in this parameter.
void
This function can not be called statically.
bool Event_Dispatcher::removeObserver (
mixed $callback
, string $nName = EVENT_DISPATCHER_GLOBAL
, string $class = ''
)
Remove an observer from dispatcher.
To remove an observer, specify the same parameters as used in the call to addObserver().
mixed $callback
Callback to notity, may either be a string containing the name of a global function or an array containing class or object and the name of the method to call.
string $nName = EVENT_DISPATCHER_GLOBAL
Acts as a filter: notify the observer only if the notification name matches the name passed in this parameter. Use EVENT_DISPATCHER_GLOBAL if the observer should be notified regardles of the notification name.
string $class = ''
Acts as a filter: notify the observer only if the sender of the notification matches the class passed in this parameter.
bool TRUE if the observer could be removed, FALSE otherwise
This function can not be called statically.
bool Event_Dispatcher::setNotificationClass (
string $class
)
Set the name of the class that will be used as a notification object when post() is called.
You may call this method on an object to change it for a single dispatcher or statically, to set the default for all dispatchers that will be created.
string $class
Name of the class that is used as a notification container when the post() method is called. Make sure the class is loaded before using it as notification class.
This method always returns true.
This function can be called statically.
object Event_Notification Event_Dispatcher::post (
object &$object
, string $nName
, mixed $info = array()
, bool $pending
= true
, bool $bubble
= true
)
Post a new notification to all observers.
object &$object
Reference to the object that posts the notification (the sender). May be used to filter notifications in the callbacks.
string $nName
Name of the notification.
mixed $info = array()
Additional information about the notification.
bool $pending
= true
Notifications are by default added to a pending notification list. This way, if an observer is not registered by the time they are posted, it will still be notified when it is added as an observer.
This behaviour can be turned off in order to make sure that only the registered observers will be notified.
bool $bubble
= true
Notifications are by default added broadcasted to any nested dispatchers that have been added using addNestedDispatcher().
This behaviour can be turned off in order to make sure that only the observers added the posting dispatcher will be notified. This allows you to differentiate between global and local notifications.
object Event_Notification The notification object.
This function can not be called statically.
void Event_Dispatcher::addNestedDispatcher (
object Event_Dispatcher &$dispatcher
)
Adds a nested dispatcher to the dispatcher.
Nested dispatchers allow you to create event bubbling like it is implemented in Javascript. After an event has been posted to all observers of the dispatcher, it will be broadcasted to all nested dispatchers.
If you have one dispatcher that dispatches events of a component in your framework and one dispatcher that dispatches global events that are triggered by the framework itself it could make sense that you nest these dispatchers, so that events posted by the component dispatcher will also be broadcasted to the global dispatcher.
object Event_Dispatcher &$dispatcher
Dispatcher that should be added as a nested dispatcher to the current dispatcher.
void
This function can not be called statically.
boolean Event_Dispatcher::removeNestedDispatcher (
object Event_Dispatcher &$dispatcher
)
Removes a nested dispatcher from the dispatcher.
To remove a dispatcher from the list of nested dispatcher, just pass the same object to removeNestedDispatcher().
object Event_Dispatcher &$dispatcher
Dispatcher that should be removed from the list of nested dispatchers.
boolean TRUE if the dispatcher could be removed, FALSE otherwise.
This function can not be called statically.
The Event_Notification class acts as a container for event information. It provides some setters and getters to access the contained information.
If you need to store additional information about the events or provide additional features, you may change the class that is used by Event_Dispatcher, but it is recommended to extend Event_Notification.
object Event_Notification Event_Notification::Event_Notifcation (
object &$object
, string $nName
, mixed $info = array()
)
Constructor of the Event_Notification class.
In most cases, you will not need to create the notification objects yourself, as this is done automatically by the Event_Dispatcher::post() method..
object &$object
Reference to the object that posts the notification (the sender). May be used to filter notifications in the callbacks.
string $nName
Name of the notification.
mixed $info = array()
Additional information about the notification.
string name of the notification
This function can not be called statically.
string Event_Notification::getNotificationName (
)
Get the name of the notification.
string name of the notification
This function can not be called statically.
object &Event_Notification::getNotificationObject (
)
Get a reference to the object that sent the notification.
object sender of the notification
This function can not be called statically.
mixed Event_Notification::getNotificationInfo (
)
Get additional information that has been stored in the notification.
mixed additional information.
This function can not be called statically.
int Event_Notification::getNotificationCount (
)
Retrieves the amount of observers that have been notified by the notification.
int number of observers notified.
This function can not be called statically.
void Event_Notification::cancelNotification (
)
Cancels the notification.
If a notification is cancelled, no more observers will be notified by this notification.
void
This function can not be called statically.
bool Event_Notification::isNotificationCancelled (
)
Checks, whether the notification has been cancelled.
bool TRUE if the notification has been cancelled, FALSE otherwise.
This function can not be called statically.
Provides Packages for working with different file formats.
Parse and create vCards.
The Contact_Vcard classes for PHP allow you to parse and build vCards that match the IMC standards.
A vCard is a plain text file that contains an "electronic business card" of contact information suitable for including in an address book. The vCard format is a standardized way of trading personal and organizational contact information.
For a full overview of the vCard 3.0 specification, refer to Internet Engineering Task Force RFC 2426.
For a full overview of the vCard 2.1 specification (version 2.1), refer to the Internet Mail Consortium Product Developer Information web page.
These are some, but not all, of the components of a vCard file:
N
: The name of the person or organization
represented by the vCard, in a structured format:
family name, given name, additional (middle) names, any honorific
prefixes (Mr., Dr., Miss, etc), and any honorific suffixes
(Jr., III, Ph.D., etc).
ADR
: Physical address. There may be one or more
addresses in a vCard; one for home, one for work, one for deliveries,
and so on.
The ADR element has structured components for p.o. box,
extended information, street, city/locality, state/region,
postal code, and country.
TEL
: Telephone. There may be one or more telephone
numbers in a vCard; one for home, one for work, one for fax, and so on.
The TEL element has a single text value indicating the phone number.
EMAIL
: Email address. There may be one or more email
addresses in a vCard; one for home, one for work, and so on.
The EMAIL element has a single text value indicating the email address.
URL
: One URL associated with this vCard; e.g.,
a personal or organizational home page.
ORG
: The organization in which this vCard is a part.
There may be one or more suborgnizations as well.
TITLE
: A single job title.
ROLE
: A single, longer description of the job role.
Whereas TITLE might be "Senior VP Of Custodial Enforcement" the
ROLE might be "Sweep floors, empty trash, etc."
CATEGORIES
: The category or categories of this vCard:
Personal, Business, Family, and so on.
BDAY
: Birthday in yyyy-mm-dd format.
It might also have additional time and timezone information.
PHOTO
, LOGO
, KEY
,
SOUND
: Respectively, a binary-encoded file for a personal
photo, corporate logo, encryption key, or name-pronounciation sound associated
with the vCard. The information may also be a URI link to a binary file.
NOTE
: Any other arbitrary information you might
want to keep with the vCard.
Each line in a vCard is a separate component of the vCard. A component line consists of three things:
the component type-defnition (N
,
ADR
, TEL
, etc);
optionally, parameters for the component, such as whether or not the data is binary-encoded, or the type of data (home/work/preferred/etc), and so on; and
the component value (which may be a single text value, repeated text values, or a structured text value).
For example, consider the following component lines:
ADR;TYPE=WORK,PREF:;;123 Main;Beverly Hills;CA;90210;US
EMAIL;TYPE=HOME;TYPE=INTERNET:nobody@example.com
CATEGORIES:Personal,Business,Family
PHOTO;TYPE=JPEG;VALUE=URI:http://example.com/photo.jpg
Component line 1 has an ADR
type-definition
(meaning a delivery address).
It has a TYPE
parameter of
WORK
and PREF
,
indicating that this a work address and is
the preferred address for deliveries.
Finally, the value of the component is a structured text value.
Structured text value parts are delimited by semicolons;
in the case of ADR
components, the structured
value parts are
p.o. box, extended address, street address, city/locality,
state/region, zip/postal code, and country.
Component line 2 has an EMAIL
type-definition
(meaning an email address).
It has a TYPE
parameter of
HOME
and INTERNET
(meaning an internet email address for home).
The component value is a single text value indicating the
email address.
Component line 3 has a CATEGORIES
type-definition
(meaning the general categories of of this vCard).
There are no parameters for this component.
The component value is a repeating-text value.
Repeated text values a delimited by commas.
Component line 4 has a PHOTO
type-definition
(meaning a personal photograph).
The parameters indicate that the photo TYPE
is JPEG
(giving a hint to vCard decoders how to handle the
PHOTO
value) and that the component VALUE
will be
a URI
(that is, a link to an external photo file).
Finally, the component value is a single text value,
revealing in this case the URI
of the photo.
Frank Hellwig has a 2.1/3.0 parser and address-book page generator. See his site at vcardphp.sf.net.
Kai Blankenhorn has a 2.1 card generator (not a parser). See his work at www.bitfolge.de/?s=phpvcard.
Flaimo has a vCard generator, too, at flaimo.com/php_scripts.php (scroll down past the iCalendar stuff).
HORDE has a vCard data element in their application framework, but I don't quite see how to use it outside that framework. The doc pages for it are at http://dev.horde.org/api/horde/dev-doxygen/html/classData__vcard.html .
Of course, you can always search for more vCard stuff under PHP, too: http://www.google.com/search?q=vcard+php.
Build (create) and fetch vCard 2.1 and 3.0 text blocks.
Please read About Contact_Vcard first.
Allows you to programmatically create a vCard, version 2.1 or 3.0, and fetch the vCard text.
Download and un-compress Contact_Vcard_Build from the PEAR archive.
Include Contact_Vcard_Build.php
in your PHP script.
Instantiate a new Contact_Vcard_Build object (by default, the vCard version is 3.0, but 2.1 vCards are also supported).
Set or add values and parameters that you want in the vCard.
Fetch the completed vCard, then use print_r() to view the results.
Example code:
<?php
// include the class file
require_once 'Contact/Vcard/Build.php';
// instantiate a builder object
// (defaults to version 3.0)
$vcard = new Contact_Vcard_Build();
// set a formatted name
$vcard->setFormattedName('Bolivar Shagnasty');
// set the structured name parts
$vcard->setName('Shagnasty', 'Bolivar', 'Odysseus',
'Mr.', 'III');
// add a work email. note that we add the value
// first and the param after -- Contact_Vcard_Build
// is smart enough to add the param in the correct
// place.
$vcard->addEmail('boshag@example.com');
$vcard->addParam('TYPE', 'WORK');
// add a home/preferred email
$vcard->addEmail('bolivar@example.net');
$vcard->addParam('TYPE', 'HOME');
$vcard->addParam('TYPE', 'PREF');
// add a work address
$vcard->addAddress('POB 101', 'Suite 202', '123 Main',
'Beverly Hills', 'CA', '90210', 'US');
$vcard->addParam('TYPE', 'WORK');
// get back the vCard and print it
$text = $vcard->fetch();
echo '<pre>';
print_r($text);
echo '</pre>';
?>
The 2.1
specification uses CRLF to terminate lines
(\r\n
).
It allows the following components and parameters:
Parameters:
TYPE of DOM, INTL, POSTAL, PARCEL, HOME, WORK, PREF, VOICE, FAX, MSG, CELL, PAGER, BBS, MODEM, CAR, ISDN, VIDEO, AOL, APPLELINK, ATTMAIL, CIS, EWORLD, INTERNET, IBMMAIL, MCIMAIL, POWERSHARE, PRODIGY, TLX, X400, GIF, CGM, WMF, BMP, MET, PMB, DIB, PICT, TIFF, PDF, PS, JPEG, QTIME, MPEG, MPEG2, AVI, WAVE, AIFF, PCM, X509, or PGP.
ENCODING of 7BIT, 8BIT, BASE64, QUOTED-PRINTABLE
VALUE of INLINE, CONTENT-ID, CID, URL, VCARD
CHARSET of any ISO charset specification.
LANGUAGE is very lenient, basically anything so long as it uses only the characters a-z, A-Z, 0-9, and dash (-).
Components and methods
VERSION (setVersion())
FN (setFormattedName())
N (setName())
PHOTO (setPhoto())
BDAY (setBirthday())
ADR (addAddress())
LABEL (addLabel())
TEL (addTelephone())
EMAIL (addEmail())
MAILER (setMailer())
TZ (setTZ())
GEO (setGeo())
TITLE (setTitle())
ROLE (setRole())
LOGO (setLogo())
AGENT (setAgent())
ORG (addOrganization())
NOTE (setNote())
REV (setRevision())
SOUND (setSound())
URL (setURL())
KEY (setKey())
The 3.0
specification uses LF to terminate lines
(\n
).
It allows the following components and parameters:
Parameters:
TYPE of any of the 2.1 TYPE values, or any other value so long as it uses only the characters a-z, A-Z, 0-9, and dash (-).
ENCODING of 8BIT and B ("binary").
VALUE of BINARY, PHONE-NUMBER, TEXT, URI, UTC-OFFSET, or VCARD.
Components and Methods:
VERSION (setVersion())
FN (setFormattedName())
N (setName())
NAME (setSourceName())
SOURCE (setSource())
NICKNAME (addNickname())
PHOTO (setPhoto())
BDAY (setBirthday())
ADR (addAddress())
LABEL (addLabel())
TEL (addTelephone())
EMAIL (addEmail())
MAILER (setMailer())
TZ (setTZ())
GEO (setGeo())
TITLE (setTitle())
ROLE (setRole())
LOGO (setLogo())
AGENT (setAgent())
ORG (addOrganization())
CATEGORIES (addCategories())
NOTE (setNote())
PRODID (setProductID())
REV (setRevision())
SORT-STRING (setSortString())
SOUND (setSound())
UID (setUniqueID())
URL (setURL())
CLASS (setClass())
KEY (setKey())
The basic use of Contact_Vcard_Build is straightforward: instantiate a builder object, add values and parameters, then fetch the resulting vCard.
To create an instance of a Contact_Vcard_Build ("builder") object, include the class file and issue a new directive:
<?php
require_once 'Contact/Vcard/Build.php';
$vcard = new Contact_Vcard_Build();
?>
By default, this creates a builder object that will allow you to fetch a
version 3.0 vCard.
If you want to build a version 2.1 vCard, pass '2.1
'
as the only constructor argument:
<?php
$vcard = new Contact_Vcard_Build('2.1');
?>
There are two ways to set the value of a component: use the method specifically for the component you want to add, or use the generic setValue() and addValue() methods. While the generic methods allow you direct control over every individual component, iteration, structured part, and value repetition, the component-specific methods are often easier to use.
You must set the
FN
(formatted name) andN
(personal name) components; these are required by both 2.1 and 3.0 version vCards.
For example, if you want to add two ADR
components
to the vCard, you can do it with the ADR-specific method...
<?php
// add first address iteration
$vcard->addAddress($pobox0, $extend0, $street0, $city0,
$state0, $zip0, $country0);
// add second address iteration
$vcard->addAddress($pobox1, $extend1, $street1, $city1,
$state1, $zip1, $country1);
?>
...or you can use the generic methods:
<?php
// add first address (iteration = 0)
$vcard->addValue('ADR', 0, VCARD_ADR_POB, $pobox0);
$vcard->addValue('ADR', 0, VCARD_ADR_EXTEND, $extend0);
$vcard->addValue('ADR', 0, VCARD_ADR_STREET, $street0);
$vcard->addValue('ADR', 0, VCARD_ADR_LOCALITY, $city0);
$vcard->addValue('ADR', 0, VCARD_ADR_REGION, $state0);
$vcard->addValue('ADR', 0, VCARD_ADR_POSTCODE, $zip0);
$vcard->addValue('ADR', 0, VCARD_ADR_COUNTRY, $country0);
// add second address (iteration = 1)
$vcard->addValue('ADR', 1, VCARD_ADR_POB, $pobox1);
$vcard->addValue('ADR', 1, VCARD_ADR_EXTEND, $extend1);
$vcard->addValue('ADR', 1, VCARD_ADR_STREET, $street1);
$vcard->addValue('ADR', 1, VCARD_ADR_LOCALITY, $city1);
$vcard->addValue('ADR', 1, VCARD_ADR_REGION, $state1);
$vcard->addValue('ADR', 1, VCARD_ADR_POSTCODE, $zip1);
$vcard->addValue('ADR', 1, VCARD_ADR_COUNTRY, $country1);
?>
Please see the Contact_Vcard_Build.php
inline
comments for descriptions of how to use each component-specific method.
There is only one way to add a parameter: use the addParam() method. Unlike with adding values, there are no component-specifc methods to add parameters.
In general, you should add the parameters of a component immediately after you add the complete value of a component, because the builder object keeps track of what was the last component value added. (This is why there are no component-specific add-parameter methods.)
For example, we can set the params for the ADR
components as in the above code:
<?php
// add first address iteration
$vcard->addAddress($pobox0, $extend0, $street0, $city0,
$state0, $zip0, $country0);
// add parameters to the first address
$vcard->addParam('TYPE', 'HOME');
$vcard->addParam('TYPE', 'PREF');
// add second address iteration
$vcard->addAddress($pobox1, $extend1, $street1, $city1,
$state1, $zip1, $country1);
// add parameters to the second address
$vcard->addParam('TYPE', 'WORK');
?>
Thus, the first address will have TYPE=HOME,PREF
and the second will have TYPE=WORK
as their parameters.
Alternatively, you can add parameters directly using the same addParam() method, with some additional arguments:
<?php
// add parameters to the first address iteration
// (component = ADR, iteration = 0)
$vcard->addParam('TYPE', 'HOME', 'ADR', 0);
$vcard->addParam('TYPE', 'PREF', 'ADR', 0);
// add parameters to the second address iteration
// (component = ADR, iteration = 1)
$vcard->addParam('TYPE', 'WORK', 'ADR', 1);
?>
This does the same thing as the earlier addParam() code.
Although the version 2.1 specification optionally allows parameter values to be indicated without without specified types (i.e, "
HOME
" instead of "TYPE=HOME
") the Contact_Vcard_Build class is not so lenient. With Contact_Vcard_Builder, you must set both the parameter kind and parameter value.
After you have added all the values and parameters that you want,
you get back the vCard using the fetch() method.
This will return the components, parameters, and values
(including BEGIN:VCARD
and END:VCARD
)
in proper format for the vCard version.
<?php
$text = $vcard->fetch();
?>
If you set values and parameters for components that are not part of the selected vCard version, they will not be included in the fetched vCard text.
You must have set the
FN
(formatted name) andN
(personal name) components, or the fetch() method will return a PEAR_Error object. TheFN
andN
components are required by both 2.1 and 3.0 version vCards.
Parse vCard 2.1 and 3.0 files.
Please read About Contact_Vcard first.
Here are some quick instructions for the impatient. :-)
Download and un-compress Contact_Vcard_Parse from the PEAR archive.
Include Contact_Vcard_Parse.php
in your PHP script.
Instantiate a new Contact_Vcard_Parse object.
Use the fromFile() method to parse any file which may
have one or more vCards in it; try the sample.vcf
file for a start.
Contact_Vcard_Parse should work with both
2.1 and 3.0 vCard files.
Use print_r() to view the resulting array of data from the parsed file.
Do what you want with the data, such as insert into a table.
Example code
<?php
// include the class file
require_once 'Contact_Vcard_Parse.php';
// instantiate a parser object
$parse = new Contact_Vcard_Parse();
// parse a vCard file and store the data
// in $cardinfo
$cardinfo = $parse->fromFile('sample.vcf');
// view the card info array
echo '<pre>';
print_r($cardinfo);
echo '</pre>';
?>
Contact_Vcard_Parse reads a file or block of text for vCard data, then converts that data into a series of nested arrays. I used to present a detailed prose explanation of the array, but I think it's easier to just give a generic outline of the array:
$parse_result = array (
[int_cardnumber] => array (
[string_datatype] => array (
["param"] => array (
[string_paramname] => array (
[int_repetitionnumber] => string_paramtext
)
)
["value"] => array (
[int_partnumber] => array (
[int_repetitionnumber] => string_valuetext
)
)
)
)
)
By way of example, let's take a look at the vCard of my friend Bolivar Shagnasty.
BEGIN:VCARD
VERSION:3.0
N:Shagnasty;Bolivar;Odysseus;Mr.;III,B.S.
FN:Bolivar Shagnasty
ADR;TYPE=HOME,WORK:;;123 Main,Apartment 101;Beverly Hills;CA;90210
EMAIL;TYPE=HOME;TYPE=WORK:boshag@example.com
EMAIL;TYPE=PREF:boshag@ciaweb.net
END:VCARD
This is a pretty simple vCard: my buddy Bolivar's name, one address (looks like Bolivar works from home), two email addresses (one for work and home, and one as his "preferred" address). This simple vCard, when it gets parsed, looks like this:
(
[0] => Array
(
[VERSION] => Array
(
[0] => Array
(
[param] => Array
(
)
[value] => Array
(
[0] => Array
(
[0] => 3.0
)
)
)
)
[N] => Array
(
[0] => Array
(
[param] => Array
(
)
[value] => Array
(
[0] => Array // family
(
[0] => Shagnasty
)
[1] => Array // first
(
[0] => Bolivar
)
[2] => Array // additional or middle
(
[0] => Odysseus
)
[3] => Array // honorifix prefix
(
[0] => Mr.
)
[4] => Array // honorifix suffix
(
[0] => III
[1] => B.S.
)
)
)
)
[FN] => Array
(
[0] => Array
(
[param] => Array
(
)
[value] => Array
(
[0] => Array
(
[0] => Bolivar Shagnasty
)
)
)
)
[ADR] => Array
(
[0] => Array
(
[param] => Array
(
[TYPE] => Array
(
[0] => HOME
[1] => WORK
)
)
[value] => Array
(
[0] => Array // p.o. box
(
[0] =>
)
[1] => Array // extended
(
[0] =>
)
[2] => Array // street
(
[0] => 123 Main
[1] => Apartment 101
)
[3] => Array // locality or city
(
[0] => Beverly Hills
)
[4] => Array // region, state, or province
(
[0] => CA
)
[5] => Array // postal code
(
[0] => 90210
)
[6] => Array // country
(
[0] =>
)
)
)
)
[EMAIL] => Array
(
[0] => Array
(
[param] => Array
(
[TYPE] => Array
(
[0] => HOME
[1] => WORK
)
)
[value] => Array
(
[0] => Array
(
[0] => boshag@example.com
)
)
)
[1] => Array
(
[param] => Array
(
[TYPE] => Array
(
[0] => PREF
)
)
[value] => Array
(
[0] => Array
(
[0] => boshag@ciaweb.net
)
)
)
)
)
)
Sweet Jebus! That's an ugly mess. But it retains every bit of info about the vCard so you can do what you like with it. It keeps (separately) every element and component so you can see the underlying structure of the vCard.
Yes, I know it's a deeply-nested array set, and is ugly and probably inefficient. The problem (or genius?) of the vCard format is that just about every part of a vCard element can have multiple values. While this makes the vCard format very flexible, it makes it a little difficult to parse and interpret in a simple fashion. The easiest way I could think of was a series of nested arrays. An object-oriented approach might be better, but even then you're going to have nested objects or nested arrays within the vCard object to represent multiple values of a vCard data element.
When I wrote this parser, my primary goal was to be able to read vCard
files produced by the Mac OS X Address Book application.
However, it looks like Address Book puts some weird character after every
single text character in the output, in addition to some weird line endings.
If you want to use .vcf
files generated by the Mac OS X
Address Book, you might need to massage the file in BBEdit or TextWrangler
first; turn on "show invisibles" to see the offending characters, then do
a search-and-replace to delete them all at once (or perhaps "Zap Gremlins").
UPDATE: David Weingart writes, "That's probably Unicode. In my extremely limited testing, it looks like in some cases you get plain vanilla ISO Latin 1, but if there are any high ascii characters in the entry, they export UTF 16 (double-byte) Unicode." Thanks, David. (Contact_Vcard_Parse does not do Unicode at this time.)
Contact_Vcard_Parse does not validate the information or formatting in the vCard (although it does decode quoted-printable text). In the spirit of "be lenient in what you accept and strict in what you produce", Contact_Vcard_Parse should be able to read just about anything from a vCard file, but it's up to you as the programmer to make sense of the data.
Contact_Vcard_Parse should work on file with any kind of line endings (Mac \r, Unix \n, and DOS \r\n) automatically. It also unfolds lines automatically, so data elements spread across multiple lines should come through OK.
If you discover a new bug or want to contribute code to
Contact_Vcard_Parse, contact Paul M. Jones at
pjones at ciaweb dot net;
the subject line should start with [VCARD]
.
This package will let you manipulate easily
tar
, gz
, bz2
,
tgz
, tbz
, zip
,
ar
and deb
files.
Here we simply take a tar archive called
archive.tar
and extract its contents to the folder
<?php
require_once "File/Archive.php";
File_Archive::extract('archive.tar/', 'output');
?>
<?php
require_once "File/Archive.php";
File_Archive::extract(
//The content of archive.tar appears in the root folder (default argument)
'archive.tar/',
//And is written to ...
File_Archive::toArchive( // ... a zip archive
'archive.zip', // called archive.zip
File_Archive::toOutput() // that will be sent to the standard output
)
);
?>
Use extract() to get files out of an archive. When specifying the file to extract, make sure to use the archive name as first folder.
<?php
require_once "File/Archive.php";
File_Archive::extract(
'archive.tar/inner.tgz/file.txt',
File_Archive::toOutput()
);
?>
One possible use case of File_Archive is to dynamically generate archives that contain pictures or videos from a gallery.
The choice of the file format is important if you want an efficient generation. Let's see what are the possibilities:
Pros: Generation very efficient, constant memory usage, no need to cache
Cons: No compression (but anyway images or video can hardly be compressed), not as widely used as Zip
Pros: Very high compression ratio, constant memory usage
Cons: Can't be cached, needs a lot of CPU at each generation
Pros: Intermediate result can be cached, compressed, you can choose the compression level, widely used
Cons: Compression ratio lower than for Tgz/Tbz
We will focus on Tar and Zip generation, Tgz and Tbz are too CPU expensive for an "on the fly" archive generation.
<?php
require_once "File/Archive.php";
// $files is an array of path to the files that must be added to the archive
File_Archive::extract(
$files,
File_Archive::toArchive(
'myGallery.tar',
File_Archive::toOutput()
)
);
?>
The main advantages of the Zip generation is that it is not very expensive (due to the ability to cache the result), and widely used. I think 2 viable options are to generate uncompressed Zip archives (since you don't reduce a lot the size of picture and video files by compressing them) or to generate compressed Zip archive using a cache system.
On the fly creation of an uncompressed ZIP archive
<?php
require_once "File/Archive.php";
File_Archive::setOption('zipCompressionLevel', 0);
// $files is an array of path to the files that must be added to the archive
File_Archive::extract(
$files,
File_Archive::toArchive(
'myGallery.zip',
File_Archive::toOutput()
)
);
?>
On the fly creation of a compressed ZIP archive with a cache
<?php
require_once "File/Archive.php";
require_once "Cache/Lite.php";
// See the documentation of cache lite for the meaning of the $options array
// fileNameProtection must be left to the default true value
// automaticSerialization is not required and should be left to false
$options = array('cacheDir' => 'tmp');
File_Archive::setOption('cache', new Cache_Lite($options));
File_Archive::setOption('zipCompressionLevel', 9);
// $files is an array of path to the files that must be added to the archive
File_Archive::extract(
$files,
File_Archive::toArchive(
'myGallery.zip',
File_Archive::toOutput()
)
);
?>
Since generating a zip or a tar archive is pretty much the same code, you can write a simple code that lets the user choose what format he wants. The following code is taken from a code I really use in my gallery.
Custom archive
<?php
$allowedFormats = array('tar', 'zip');
if (!in_array($_GET['type'], $allowedFormats)) {
die('Type ' . htmlspecialchars($_GET['type']) . ' is either unknown or not allowed');
}
require_once "File/Archive.php";
File_Archive::setOption('zipCompressionLevel', 0);
/**
* I skipped the generation of the $files array since it really
* depends on you gallery and what files the user requires
*/
File_Archive::extract(
$files,
File_Archive::toArchive(
'myGallery.' . $_GET['type'],
File_Archive::toOutput()
)
);
?>
A reader is an object that represents a list of files and directories. Those files can be generated dynamically or exist physically. For example, there is a reader class for a directory, or for each archive format handled by File_Archive, and they all have the same interface.
To create a reader, you will have to use the File_Archive factory. The important function is the read() function:
read
(
string
$url
,
string
$symbolic = null
,
integer
$uncompression = 0
,
integer
$directoryDepth = -0
)
In this function the URL will represent what you want to read.
Generation of sources
<?php
require_once "File/Archive.php";
/*
To read a directory, just give the directory name
By default, the directory and all the subdirectories
will be parsed (see $directoryDepth to change this)
*/
$source = File_Archive::read("Path/to/dir");
/*
To read from one single file,
simply provide the name of the file
*/
$source = File_Archive::read("Path/to/dir/file.txt");
/*
An archive will be considered as a directory if a slash follows
This example reads the directory and all the subdirectories of inner/dir
contained in archive Path/to/dir/archive.tar
This reads all the .txt files in the inner directory of the archive.tar
*/
$source = File_Archive::read("Path/to/dir/archive.tar/inner/*.txt");
/*
If you want to uncompress the archive (read all its content)
Note: if you ommit the trailing /, the archive will be treated as a single file
*/
$source = File_Archive::read("Path/to/dir/archive.tar/");
?>
The symbolic
attribute says how the files read will be
displayed for future use.
If $URL
is a directory,
$URL
will be replaced by
$symbolic
(or
''
if
$symbolic
is
null
).
So,
in our first example,
the files will be displayed as if the current directory was
'Path/to/dir'
:
Since by default $symbolic
is empty,
Path/to/dir
will be simply removed from the file.
You may want to put Path/to/dir
as
$symbolic
to keep the full path
Path/to/dir
.
If $URL
is a file,
then only the filename will be kept,
and
$symbolic
will be added to it.
So,
in our second example,
the source contains a file with symbolic name
file.txt
.
If a symbolic name foo
had been specified,
the source would contain foo/file.txt
.
The $uncompression
parameter indicate how many files will
be uncompressed while parsing the tree to files.
By default the files are not uncompressed.
So, if you do
File_Archive::read('archive.tar/inner/dir', 'inner/dir'),
and if archive.tar
contains a file called
archive.tar/inner/dir/file.tgz
, this second archive
will appear as a file and not as a directory.
It won't be uncompressed because $uncompression
is 0
.
If $uncompression
is set to 1
,
file.tgz
would appear as a directory,
but the files inside this archive would not be uncompressed.
If $uncompression
is set to -1
,
all the files would be uncompressed, regardless of the depth.
The compressed files that may appear in
$URL
are not taken into account by$uncompression
variable.
The $directoryDepth
parameter
gives a limit to the number of directory
read by the reader.
Using a multi reader, you can make several sources appear as one. You can create a multi reader using the File_Archive::readMulti() function.
Multi reader
<?php
//This reader contains the content of directory and archive.tar
$source = File_Archive::readMulti(
array(
File_Archive::read('directory'),
File_Archive::read('archive.tar/')
)
);
?>
Any reader provides the following interface:
function next()
Go to the next file in the source.
Returns false
when the end of the archive is reached.
function getFilename()
Returns the filename of the currently selected entry in the archive.
function getStat()
Returns the stat as the stat() function does.
This function may not return a complete array,
it may even return
array()
.
function getDataFilename()
For optimisation purposes:
if the source is a physical file,
this function returns the name of the file;
otherwise it returns
null
.
function getData($length = -1)
Reads some data from the source. This function will return a string which size is determined by the smallest of
$length
if $length
>= 0
the end of the file.
If the end of the file is reached,
the function will return
null
.
function skip($length)
Equivalent to getData($length), but does not return any data. Depending on the data reader, this function can be far more efficient than getData().
function close()
Should be called after having used the data reader (closes the file handles...). This function moves the object in the same state as it was before the first call to next(). After this call, you can iterate again on the data reader.
Listing the content of a data reader
<?php
$source->close(); //Move back to the begining of the source
while($source->next()) {
echo $source->getFilename() . "<br/>\n";
}
?>
All File_Archive functions that take a reader as an argument also accept strings and arrays. The strings will be automatically interpreted as a reader using File_Archive::read function. The arrays will be interpreted as a multi reader.
Since the readers are passed by reference, you will have to pass a variable and not the raw string or array.
It is thus possible to rewrite the previous example like that:
Multi reader
<?php
//This reader contains the content of directory and archive.tar
File_Archive::extract(
array(
'directory',
'archive.tar/'
),
File_Archive::toArchive('test.zip')
);
?>
A writer is an object that deals with data. Some writers transform data (this is the case of the archive writers), some save them to disk (for files writers), or to memory (for the memory writer)... They all implement the same interface.
You can transfer data from a reader to a writer using the File_Archive::extract function.
All the writers can be created thanks to the File_Archive factory, and more particularly the File_Archive::to* functions.
To create a writer that will generate an archive, use the toArchive() function:
toArchive
(
string
$filename
,
&$innerWriter
,
$type = null
,
$stat = array()
,
$autoClose = true
)
$filename
is the name of the generated archive
$innerWriter
is another writer in which the archive file will be written
$type
is one of Tar
,
Gzip
or Zip
and indicates the format
of compression.
If not specified, the type is determined thanks to the extension of
the filename.
$stat
is an optional array to indicate stats about the
archive (see the PHP stat function for the possible indexes).
$autoClose
indicates whether the inner writer will be
closed once the data are sent.
It may be useful not to close the writer if you want to append some more
data after writing.
In general, you won't need to keep the writer open, so you should just
keep the default value.
Generation of archive writers
<?php
require_once "File/Archive.php";
/* Writer to a tar file */
File_Archive::toArchive("archive.tar", $innerWriter);
/* Writer to a tar.gz file */
File_Archive::toArchive("archive.tgz", $innerWriter);
/* Writer to a zip file */
File_Archive::toArchive("archive.zip", $innerWriter);
?>
A writer can write the files to physical files. To create such a writer, call File_Archive::toFiles();. If a directory does not exist, it will be automatically created.
Use files writer
<?php
require_once "File/Archive.php";
/* Copy a whole directory to another location */
File_Archive::extract(
File_Archive::read('Path/to/dir', 'new/directory');
File_Archive::toFiles()
);
/* Convert an archive to another format: tgz to zip */
File_Archive::extract(
File_Archive::read('archive.tgz/'),
File_Archive::toArchive(
'archive.zip',
File_Archive::toFiles()
)
);
?>
You can send emails as attachment using a mail writer available thanks to File_Archive::toMail function.
toMail
(
array
$to
,
array
$headers
,
string
$message
,
&$mail = null
)
This function relies on the PEAR Mail and Mail_Mime libraries, and the parameters are the same as the one of these classes:
$to
an array or a string with comma separated recipients
$headers
will be sent to
Mail_Mime and to $mail
:
an associative array of headers.
The header name is used as key and the header value as value.
$message
is the text version of the body of the mail.
You can provide an HTML version thanks to the
setHTMLBody() and addHTMLImage()
of the writer.
The signatures of these functions are the same as the ones of
Mail_Mime.
$mail
the way to send mail.
This is an object created with the Mail::factory()
function.
If null
, Mail::factory('mail')
will be used (and the email will be sent using the PHP mail function).
<?php
require_once "File/Archive.php";
/* Send the files in the current directory (no recursion) as attachment */
File_Archive::extract(
File_Archive::read('Path/to/dir', '', 0, 0),
File_Archive::toMail(
$to, // recipients
array(
'Subject' => 'Path/to/dir directory',
'From' => 'address@of.expeditor'
),
'Find all the files attached' // body
)
);
?>
To send files to the remote user (i.e. write data to the standard output), you need a special writer. You can build one calling function File_Archive::toOutput().
This writer will automatically send a header forcing the download of the file.
If you don't want that, call File_Archive::toOutput(false).
Using a multi writer, you can write the data to two or more different locations in parallel.
A typical use is to send the file to the user at the same time as you write it to a file.
It can also be used to generate archives in different formats.
You can create a multi writer using File_Archive::toMulti($dest1, $dest2).
Multi writer
<?php
//Send a directory to the user and to a file
File_Archive::extract(
File_Archive::read('directory'),
File_Archive::toArchive(
'multi.zip',
File_Archive::toMulti(
File_Archive::toOutput(),
File_Archive::toFiles()
)
)
);
?>
It is also possible to write data directly to a writer, without using a reader. To do so, you can use the following interface implemented by any writer:
function newFile($URL, $stat)
Create a new file in the writer.
$URL
is the name of the file,
$stat
is an array of statistics about the data
(see the PHP stat() function for more information).
The stat array may not contain all the information. The only index that must be present is index 7 (size of the data).
function writeData($data)
Append the specified data to the writer. A call to newFile() must have been done previously.
function close()
Close the writer, eventually flush the data, write the footer... This function must be called before the end of the file, otherwise some data may not be treated by the writer.
Dynamic creation of a zip file
<?php
require_once "File/Archive.php";
$dest = File_Archive::toArchive("foo.zip", File_Archive::toFiles());
$dest->newFile("even.txt");
for($i=0; $i<100; $i++)
$dest->writeData((2*$i)."\n");
$dest->newFile("odd.txt");
for($i=0; $i<100; $i++)
$dest->writeData((2*$i+1)."\n");
$dest->close();
?>
If you do not specify the stat array in the newFile() function, the majority of the archives will have to buffer the data until the end of the file is reached (this is because the size of the file is usually needed to be able to write the header).
This may be a memory problem if you want to generate really large files.
All File_Archive functions that take a writer as an argument also accept strings and arrays. The strings will be automatically interpreted as a writer using File_Archive::appender() function. The arrays will be interpreted as a multi writer.
Since the writers are passed by reference, you will have to pass a variable and not the raw string or array.
It is thus possible to rewrite the previous example like that:
Multi writer
<?php
//Send a directory to the user and to a file
File_Archive::extract(
$src = 'directory',
File_Archive::toArchive(
'multi.zip',
array(
File_Archive::toOutput(),
File_Archive::toFiles()
)
)
);
?>
File_Archive introduces the concept of filters to be able to select the files from a source. A filter is a particular reader that you can create with the File_Archive::filter() function. This function requires you to give a predicate. You can build this predicate using the File_Archive::pred* functions.
The standard logic predicates are:
predTrue(): always evaluates to true
predFalse(): always evaluates to false
predAnd($p1, $p2, ...): evaluates to
$p1 && $p2 && ...
predOr($p1, $p2, ...): evaluates to
$p1 || $p2 || ...
predNot($p): evaluates to !$p
Some other predicats will help you filtering the files:
predMinSize($size):
keep only the files
which size is
>= $size
(in bytes).
predMinTime($time): keep only the files that have been modified after $time (unix timestamp).
predMaxDepth($depth):
keep only the files that
have a public name with less than $depth
directories.
predExtension($list): keep only the files with a given
extension.
$list
is an array or a comma separated string of
allowed extensions.
predEreg($ereg): keep only the files that have a public name that matches the given regular expression.
predEregi($ereg): same as predEreg(), but the test is case insensitive.
predMIME($mimes): Select files with a certain MIME type. You can pass an array of types or a string, and use wildcards.
Filter examples
<?php
//Extract all the files that contain an 'a' in their path
// or filename from a tar archive
File_Archive::extract(
File_Archive::filter(
File_Archive::predEreg('a'),
File_Archive::read(
'archive.tar/',
'folder'
)
),
File_Archive::toFiles()
);
//Compress a directory to a zip file, including only the files
//smaller than 1MB that have changed since last hour
File_Archive::extract(
File_Archive::filter(
File_Archive::predAnd(
File_Archive::predNot(
File_Archive::predMinSize(1024 * 1024)
),
File_Archive::predMinTime(time()-3600)
),
File_Archive::read('directory')
),
File_Archive::toArchive(
'directory.zip',
File_Archive::toFiles()
)
);
?>
File_Archive version 1.3 introduces some new functions to edit existing archives. These functions will allow you to remove or append files to an existing archive.
Since for File_Archive, the file system is just another reader / writer, those modifications can be done on "real" archives (real files), or on nested archives (an archive inside another archive).
To remove files from an archive, you'll use one of the following functions from File_Archive class:
remove
(
&$pred
, $URL
)
Removes all the files that follow a given predicate from file
$URL
.
Note that the URL is the same as in the
File_Archive::read() function.
You can use nested archives.
removeFromSource
(
&$pred
, &$source
, $URL = null
)
Same as remove, but use $source
instead of the default
file system reader.
If no URL is specified, $source
must be an archive,
and the files will be removed from here.
removeDuplicates
(
$URL
)
This function will remove all the doublons from the archive at
$URL
; only the most recent file will be kept.
If the modification date is not specified for a file, it will be considered infinitely old.
If two files have the same modification date, the one that has highest position in the archive (usually the one that was added to the archive at last) will be kept.
removeDuplicatesFromSource
(
&$source
, $URL = null
)
Same as removeDuplicate(), but use
$source
instead of the default file system reader.
If no URL is specified, the duplicates will be removed from
$source
itself.
Note that those functions will not recursively uncompress archives.
Remove Jpg, gif and Bmp from a zip archive
<?php
require_once "File/Archive.php";
File_Archive::remove(
File_Archive::predExtension(
array('jpg', 'jpeg', 'bmp', 'gif')
),
'archive.zip'
);
?>
Remove image files from a nested archive
<?php
require_once 'File/Archive.php';
File_Archive::remove(
File_Archive::predMIME(
array('image/*')
),
'archive.zip/data.tgz'
);
?>
To append files to an archive, you'll use one of the following functions from File_Archive class:
appender
(
$URL
, $unique = null
, $type = null
, $stat = array()
)
Allows to append to the archive specified by the URL.
If unique is set to true
, the eventual duplicates
created by the insertion of new files will be automatically removed.
If set to null
, the default value
(that you can change using
File_Archive::setOption('appendRemoveDuplicates', true/false))
is used (false
by default).
If the archive does not exist, it will be created using the type specified in parameter (or looking at the extension in the URL if the type is not specified), and the stat array.
appenderFromSource
(
&$source
, $URL = null
, $unique = null
, $type = null
, $stat = array()
)
Same as appender, but using $source
instead of the
default file system reader.
Both of these functions return a writer that you can use as in the previous examples, using the extract function
Add a folder to an archive
<?php
require_once "File/Archive.php";
File_Archive::extract(
File_Archive::read('folder'),
File_Archive::appender('archive.zip')
);
?>
Note that if you use a string instead of a writer in a function, the string will be converted using File_Archive::appender(). Thus the previous example could be rewritten to:
Add a folder to an archive
<?php
require_once "File/Archive.php";
File_Archive::extract(
$src = 'folder',
$dest= 'archive.zip'
);
?>
File_Archive 1.4 introduce the possibility to use a cache to store intermediate result of a zip compression. It uses the Cache_Lite PEAR package to do so.
A zip file is made of compressed files, one after the others. So if you generate an archive that contains files A, B and C and then another archive that contains A and C, you will compress twice the files A and C. The use of the cache will allow to save the compressed version of files A, B and C on the first compression, to use them again in the second compression.
The cache can be (and should be) used if you dynamically create some zip archive that contains frequently the same files. For example, you may want to allow the user to select some images, videos or other files from your gallery and allow them to download a compressed zip archive that contains these files.
If you do so without cache, your server will answer very slowly if a lot of users ask the files. With the cache, the files will be compressed only once.
On my machine (a thinkpad T42P with default factory equipment), generating a 200MB zip archive takes around 30s of CPU without the cache, 32s of CPU with an empty cache and 2s of CPU if all the files to compress are already in cache.
The cache is a Cache_Lite object.
So you must have installed the package.
Then all you have to do is use the
File_Archive::setOption() function with the
cache
parameter.
Set up the cache
<?php
require_once "File/Archive.php";
require_once "Cache/Lite.php";
//Create the cache object
$cache = new Cache_Lite(
array(
//See the doc of Cache_Lite for its constructor parameters
)
);
//Ask File_Archive to use the cache object we just created
File_Archive::setOption('cache', $cache);
//And then create your archives as usual
//Generate a file called archive.zip in the working folder
File_Archive::extract(
'folderToCompress',
'archive.zip'
);
//Send an archive to the user
File_Archive::extract(
'folderToCompress',
File_Archive::toArchive(
'archive.zip',
File_Archive::toOutput()
)
);
?>
Microsoft Cabinet file extraction using either cabextract or expand
<?php
require_once 'File/Cabinet.php';
// optional step: specify the command static property
File_Cabinet::$command = '/usr/local/bin/cabextract';
$cabinet = new File_Cabinet('cabinet.cab');
// Extract the contents of 1 file
$file_contents = $cabinet->extract('a_file.txt');
// Extract the contents of multiple files (returns an array)
$file_contents = $cabinet->extract(array('a_file.txt', 'another_file.txt'));
// Extract using a glob (returns a concatenated string)
$file_contents = $cabinet->extract('*.txt');
// Extract to a directory
$cabinet->extract('a_file.txt', '/path/to/extraction/dir');
?>
Package for reading and modifying DICOM files
File_DICOM allows reading and modifying of DICOM files. DICOM stands for Digital Imaging and COmmunications in Medicine, and is a standard for creating, storing and transfering digital images (X-rays, tomography) and related information used in medicine. This package in particular does not support the exchange/transfer of DICOM data, nor any network related functionality. More information on the DICOM standard can be found at the NEMA site.
Please be aware that any use of the information produced by this package for diagnosing purposes is strongly discouraged by the author. See here for more information.
File_DICOM can be used to accomplish two things, to retrieve data from a file (including image data), and to set new values for that data (allowing to write the modified file).
Let's see how we could show some relevant data from a DICOM file and export its image data at the same time.
Showing data
<?php
require_once('File/DICOM.php');
$dicom = new File_DICOM();
$res = $dicom->parse("test.dcm");
// check for errors
if (PEAR::isError($res)) {
die("Error: ".$res->getMessage()."\n");
}
// show a few attributes of the DICOM file using group and element index
echo 'StudyDate : '.$dicom->getValue(0x0008, 0x0020)."\n";
echo 'Image Date : '.$dicom->getValue(0x0008, 0x0023)."\n";
echo 'Image Type : '.$dicom->getValue(0x0008, 0x0008)."\n";
echo 'Study Time : '.$dicom->getValue(0x0008, 0x0030)."\n";
echo 'Institution Name : '.$dicom->getValue(0x0008, 0x0080)."\n";
echo 'Manufacturer : '.$dicom->getValue(0x0008, 0x0070)."\n";
echo 'Manufacturer Model Name : '.$dicom->getValue(0x0008, 0x1090)."\n";
// or using element name
echo 'Patient Name : '.$dicom->getValue('PatientName')."\n";
echo 'Patient Age : '.$dicom->getValue('PatientAge')."\n";
// dump a PGM image from the file data
$res = $dicom->dumpImage('test.pgm');
if (PEAR::isError($res)) {
die("Error: ".$res->getMessage()."\n");
}
?>
void constructor File_DICOM::File_DICOM (
)
It creates a File_DICOM object.
This function can not be called statically.
mixed File_DICOM::parse (
string $infile
)
Parse a DICOM file and get all of its header members.
$infile
The DICOM file to parse
returns true on success, PEAR_Error on failure
This function can not be called statically.
mixed File_DICOM::write (
string $outfile = ''
)
This package is not documented yet.
$outfile
The name of the file to write. If not given it assumes the name of the file parsed. If no file was parsed and no name is given returns a PEAR_Error
returns true on success, PEAR_Error on failure
This function can not be called statically.
mixed File_DICOM::getValue (
mixed $group_or_name
, integer $element = null
)
Gets the value for a DICOM element of a given group from the parsed DICOM file
$group_or_name
The group the DICOM element belongs to (integer), or its name (string)
$element
The identifier for the DICOM element (unique inside a group). Optional
returns The value for the DICOM element on success, PEAR_Error on failure
This function can not be called statically.
Using getValue()
<?php
require_once('File/DICOM.php');
$dicom_file = new File_DICOM();
$res = $dicom_file->parse('test.dcm');
// check for errors
if (PEAR::isError($res)) {
die($res->getMessage());
}
echo 'this value: '.$dicom_file->getValue('PatientName')."\n";
echo 'Should be the same as this value: '.$dicom_file->getValue(0x0010, 0x0010)."\n";
?>
void File_DICOM::setValue (
integer $gp
, integer $el
, mixed $value
)
Sets the value for a DICOM element. Only works with strings now.
$gp
The group the DICOM element belongs to
$el
The identifier for the DICOM element (unique inside a group)
$value
This function can not be called statically.
mixed File_DICOM::dumpImage (
string $filename
)
Use with Caution!!. For a 8.5MB DICOM file on a P4 it takes 28 seconds to dump it's image.
$filename
The file where to save the image
returns true on success, PEAR_Error on failure.
This function can not be called statically.
File_Fortune: Interface to fortune cookie databases
The fortune program is a small but important part of *nix culture, and this package aims to provide support for its "fortune cookie" databases to PHP programmers.
File_Fortune
File_Fortune_Exception
<?php
require_once 'File/Fortune.php';
// Grab from a single fortune file:
$fortunes = new File_Fortune('/path/to/fortune/file');
echo $fortunes->getRandom();
// Grab from a directory of fortune files:
$fortunes = new File_Fortune('/path/to/fortune/files/');
echo $fortunes->getRandom();
?>
<?php
require_once 'File/Fortune.php';
$fortunes = new File_Fortune('/path/to/fortune/file');
$cached = $fortunes->getAll();
// or all fortunes in all files:
$fortunes->setDirectory('/path/to/fortunes/');
$cached = $fortunes->getAll();
?>
<?php
require_once 'File/Fortune.php';
$fortunes = new File_Fortune('/path/to/fortune/file');
$count = count($fortunes);
// or all fortunes in all files:
$fortunes->setDirectory('/path/to/fortunes/');
$count = count($fortunes);
?>
<?php
require_once 'File/Fortune.php';
$fortunes = new File_Fortune('/path/to/fortune/file');
foreach ($fortunes as $fortune) {
echo $fortune;
}
?>
Note: this will raise exceptions if a directory or multiple files have been set.
<?php
require_once 'File/Fortune.php';
$fortunes = new File_Fortune('/path/to/fortune/file');
// Delete a fortune:
unset($fortunes[2]); // deletes fortune denoted at index 2
// Update a fortune:
$fortune[2] = "I never liked this fortune"; // update fortune at index 2
?>
Note: this will raise exceptions if a directory or multiple files have been set.
<?php
require_once 'File/Fortune.php';
$fortunes = new File_Fortune('/path/to/fortune/file');
$fortunes->add('Shiny, new fortune!');
?>
Note: this will raise exceptions if a directory or multiple files have been set.
<?php
require_once 'File/Fortune.php';
$newFortunes = array(
'Fortune 1',
'Fortune 2',
'Fortune 3'
);
$fortunes = new File_Fortune('/path/to/fortune/file');
$fortunes->create($newFortunes);
?>
Note: this will raise exceptions if a directory or multiple files have been set.
File_Fortune
File_Fortune::__construct
(
string|array
$file
= null
,
string
$headerFile
= null
)
Optionally pass a filename or directory name to set the fortune file or directory, and, if passing a fortune file name, optionally pass the name of the header file.
$file
Fortune file name, or name of a directory containing fortune files.
If passing an array, array of fortune files names.
$headerFile
Optional location of binary header file to associate with file passed to $file; see setHeader() for more information.
throws no exceptions thrown
This function can not be called statically.
File_Fortune
File_Fortune::add
(
string
$fortune
)
This package is not documented yet.
$fortune
Fortune string to add to fortune file. Fortune will be appended to fortune file.
If multiple files or a directory have been set, you cannot manipulate the fortune list, and this method will raise an exception.
throws File_Fortune_Exception
This function can not be called statically.
void
File_Fortune::create
(
$fortunes
,
string
$file
= null
)
This package is not documented yet.
$fortunes
Array of fortunes to use to seed the new fortune file.
$file
Optional file name to use when creating new fortune file. If not provided, attempts to use the file set via setFile()
throws File_Fortune_Exception
This function can not be called statically.
void
File_Fortune::delete
(
int
$index
)
delete() may be used to delete a fortune at a given index. However, the easier usage is to simply use unset with array notation:
<?php
unset($fortunes[$index]);
If multiple files or a directory have been set, you cannot manipulate the fortune list, and this method will raise an exception.
$index
throws File_Fortune_Exception
This function can not be called statically.
array
File_Fortune::getAll
(
)
getAll() can be used to pull the entire fortune database into an array. Typically this is a bad idea as fortune files are often very large. If you wish to do some processing with each fortune, use the File_Fortune object as an iterator:
<?php
foreach ($fortunes as $fortune) {
// do something with the fortune
}
?>
Note: you can use getAll() when a directory or multiple files have been set; in such a context, it will return all fortunes in all files.
throws no exceptions thrown
This function can not be called statically.
string
File_Fortune::getDirectory
(
)
getDirectory() returns the currently registered fortune file directory, if any.
throws no exceptions thrown
This function can not be called statically.
string
File_Fortune::getFile
(
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
array
File_Fortune::getFiles
(
)
Retrieves a list of all currently set fortune files, as set either explicitly by setFiles() or implicitly by setDirectory().
throws no exceptions thrown
This function can not be called statically.
string
File_Fortune::getHeaderFile
(
)
Header files are explained in the setHeaderFile() documentation. This method returns the current header file name, if any.
throws no exceptions thrown
This function can not be called statically.
string
File_Fortune::getRandom
(
)
getRandom() pulls a random fortune. If a fortune file has been explicitly specified, the fortune will be pulled from that file; if a directory has been specified, a random fortune file will first be selected.
throws no exceptions thrown
This function can not be called statically.
void
File_Fortune::save
(
)
In most cases, it will not be necessary to save changes; __destruct() will save changes when the object goes out of scope. However, if you wish to manually ensure that changes are set, you may call save().
If multiple files or a directory have been set, you cannot manipulate the fortune list, and this method will raise an exception.
throws File_Fortune_Exception
This function can not be called statically.
File_Fortune
File_Fortune::setDirectory
(
string
$directory
)
This package is not documented yet.
$directory
throws no exceptions thrown
This function can not be called statically.
File_Fortune
File_Fortune::setFile
(
string
$file
,
string
$headerFile
= null
)
setFile() may be used to explicitly set a fortune file to use or create.
$file
$headerFile
throws no exceptions thrown
This function can not be called statically.
File_Fortune
File_Fortune::setFiles
(
)
setFiles() may be used to define a list of files from which to pull fortunes. You may pass either a string single argument, an array single argument, or multiple string arguments. As examples:
<?php
// single file:
$fortunes->setFiles('/path/to/fortunefile');
// array of files:
$fortunes->setFiles(array('/path/to/fortunefile', '/another/fortunefile', '/more/fortunes'));
// multiple individual files:
$fortunes->setFiles('/path/to/fortunefile', '/another/fortunefile', '/more/fortunes');
?>
throws no exceptions thrown
This function can not be called statically.
File_Fortune
File_Fortune::setHeaderFile
(
string
$headerFile
)
Fortune files consist of an ASCII file containing fortunes separated by a delimiter, and a binary header file that contains meta information such as the delimter used, number of fortunes, minimum and maximum length of fortunes contained in the file, and the offsets where each fortune exist in the file.
In most cases, this file is simply the name of the fortune file plus the extension 'dat', and File_Fortune will automatically detect this. If this is not the case, however, you may explicitly set the header file name using this method.
$headerFile
throws no exceptions thrown
This function can not be called statically.
void
File_Fortune::update
(
int
$index
,
string
$fortune
)
update() may be used to update fortunes. However, the recommended practice is to use array access:
<?php
$fortunes[2] = 'Updated fortunes are fun!';
If multiple files or a directory have been set, you cannot manipulate the fortune list, and this method will raise an exception.
$index
$fortune
throws File_Fortune_Exception
This function can not be called statically.
This package reads and writes fstab files, or other files sharing their format, such as /proc/mounts on Linux systems.
This package allows you to read, manipulate, and write fstab-format files, such as /etc/fstab, /etc/mtab, and /proc/mounts.
Getting the filesystem type of the root device.
<?php
require_once 'File/Fstab.php';
$fstab =& new File_Fstab();
$root =& $fstab->getEntryForPath('/');
echo "Root FS type: " . $root->fsType . "\n";
?>
Determine if a user may mount the CD-ROM
The user
option in your fstab determines whether
users may mount a given device or not.
<?php
require_once 'File/Fstab.php';
$fstab =& new File_Fstab();
$cd =& $fstab->getEntryForDevice('/dev/cdrom');
if ($cd->hasOption('user')) {
echo "Users may mount the CD-ROM\n";
} else {
echo "Users may not mount the CD-ROM\n";
}
?>
The File_Fstab_Entry class represents all the information available about a particular entry in a Fstab file.
The entry has a number of properties which represent the information in the fstab file.
$device
, $uuid
,
and $label
are mutually exclusive;
only one of the three may be set.
$device
.
dump
.
fsck
when the system boots.
You may want to read fstab(5)
for more information about
what these fields mean.
There are a number of ways of finding a specific entry from the fstab. You may find based on device, mountpoint, filesystem label, or UUID.
To find by device, you want to use the getEntryForDevice() function. The single argument this function accepts is the path to the block device for an entry.
Get entry by device
<?php
require_once 'File/Fstab.php';
$fstab =& new File_Fstab();
$dev =& $fstab->getEntryForDevice('/dev/hda1');
if (PEAR::isError($dev)) {
die($dev->getMessage());
}
?>
You may want to find a device based on the path it is mounted on;
for example, you may want to get the entry for /cdrom
,
without caring if the CD device is /dev/hdb
,
/dev/cdrom
, or some other device. To do this, use
the getEntryForPath() function.
Get entry by path
<?php
require_once 'File/Fstab.php';
$fstab =& new File_Fstab();
$dev =& $fstab->getEntryForPath('/cdrom');
if (PEAR::isError($dev)) {
die($dev->getMessage());
}
?>
Some systems use a filesystem UUID to specify the
device to mount. A UUID may look like this:
b46ad2ee-01f3-4041-96ca-91d35d059417
. The
getEntryForUUID() function handles this.
Get entry by UUID
<?php
require_once 'File/Fstab.php';
$fstab =& new File_Fstab();
$dev =& $fstab->getEntryForUUID('b46ad2ee-01f3-4041-96ca-91d35d059417');
if (PEAR::isError($dev)) {
die($dev->getMessage());
}
?>
Some filesystems allow you to specify a textual label to a filesystem.
For example, you may label your root device rootdev
,
the device you mount on /home
could be named
homedirs
and so forth. File_Fstab
supports getting entries based on the device label. This is accomplished
by using the getEntryForLabel() function.
Get entry by label
<?php
require_once 'File/Fstab.php';
$fstab =& new File_Fstab();
$dev =& $fstab->getEntryForLabel('homedirs');
if (PEAR::isError($dev)) {
die($dev->getMessage());
}
?>
In addition to reading fstab files, you may modify them as well.
Add an entry for a floppy disk
<?php
require_once 'File/Fstab.php';
$fstab =& new File_Fstab();
$floppy =& new File_Fstab_Entry();
$floppy->fsType = 'vfat';
$floppy->device = '/dev/fd0';
$floppy->mountPoint = '/floppy';
$fstab->addEntry($floppy);
?>
After modifying a fstab file, you will want to save your changes. The save() function does this.
Comments from the loaded file are not preserved when saving, and whitespace may change. This has no effect on the functionality of the fstab file, but you may lose helpful comments.
Save to the same file
This will save your changes back to the file you loaded, overwriting the old file.
<?php
require_once 'File/Fstab.php';
$fstab =& new File_Fstab();
$floppy =& new File_Fstab_Entry();
$floppy->fsType = 'vfat';
$floppy->device = '/dev/fd0';
$floppy->mountPoint = '/floppy';
$fstab->addEntry($floppy);
$res = $fstab->save();
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
Save to a different file
This will save your changes to a different file than the one you originally loaded.
<?php
require_once 'File/Fstab.php';
$fstab =& new File_Fstab();
$floppy =& new File_Fstab_Entry();
$floppy->fsType = 'vfat';
$floppy->device = '/dev/fd0';
$floppy->mountPoint = '/floppy';
$fstab->addEntry($floppy);
$res = $fstab->save('/tmp/fstab.new');
if (PEAR::isError($res)) {
die($res->getMessage());
}
?>
This package creates, reads, and modifies Machine Readable Cataloging (MARC) formatted files.
File_MARC allows you to read Machine Readable Cataloging (MARC) data in MARC 21 format. File_MARCXML, which is bundled with File_MARC, allows you to read MARCXML formatted data.
Your input source can be a PHP stream (File_MARC::SOURCE_FILE) or a string (File_MARC::SOURCE_STRING). The source location and source types are the first and second arguments to the File_MARC and File_MARCXML constructors.
Reading MARC 21 data from a file
In the following example, MARC21 data has been stored on disk in a file
named journals.mrc
. To read the MARCX21 data, we
call the constructor for File_MARC. We do not need
to tell File_MARC that journals.mrc
is a filename or
stream pointing to the MARC content, because the value of the second
parameter of the constructor for File_MARC defaults
to File_MARC::SOURCE_FILE.
<?php
require 'File/MARC.php';
// Retrieve a set of MARC records from a file
$journals = new File_MARC('journals.mrc');
// Iterate through the retrieved records
while ($record = $journals->next()) {
// Pretty print each record
print $record;
print "\n";
}
?>
Reading MARCXML data from a string
In the following example, MARCXML data has been returned from a call to
a Web service and has therefore been stored in a PHP variable,
$xml_data
, as a string. To read the MARCXML data, we
call the constructor for File_MARCXML. To tell
File_MARCXML that $xml_data
is a string, we specify
File_MARC::SOURCE_STRING as the second parameter of the
constructor.
<?php
require 'File/MARCXML.php';
// Retrieve a set of MARCXML records from a string
$journals = new File_MARCXML($xml_data, File_MARC::SOURCE_STRING);
// Iterate through the retrieved records
while ($record = $journals->next()) {
// Pretty print each record
print $record;
print "\n";
}
?>
A File_MARC object consists of a leader and an iterable set of File_MARC_Record objects representing MARC records. Each of these, in turn, consists of an iterable set of File_MARC_Data_Field or File_MARC_Control_Field objects representing MARC fields. A File_MARC_Data_Field consists of a set of iterable File_MARC_Subfield objects representing MARC subfields.
Printing the elements of a record
<?php
require 'File/MARC.php';
// Retrieve a set of MARC records
$bibrecords = new File_MARC('catdump.mrc', File_MARC::SOURCE_FILE);
// Iterate through the retrieved records
while ($record = $bibrecords->next()) {
// Print the leader
print $record->getLeader();
$subjects = $record->getFields('650');
if ($subjects) {
// Retrieve just the first 24_ field
print $record->getField('24.', true);
print "\n";
// Now print all of the retrieved subjects
foreach ($subjects as $field) {
print $field;
print "\n";
}
print "\n";
}
}
?>
All of this means that File_MARC makes it easy
to read in a set of MARC records and iterate through the contents to retrieve
specific fields and subfields.
File_MARC offers
convenience methods for retrieving specific fields without forcing you to
iterate through the fields. getField
returns
the first field that matches the field name, while
getFields
returns an array of all of the fields
that match the specified field name. Both of these methods accept an
optional boolean parameter that specifies whether your match string should
be treated as a Perl Compatible Regular Expression.
Retrieving all 650 fields from a record
<?php
require 'File/MARC.php';
// Retrieve a set of MARC records from a z39 result string
$bibrecords = new File_MARC($z39_result, File_MARC::SOURCE_STRING);
// Iterate through the retrieved records
while ($record = $bibrecords->next()) {
// Retrieve an array of all of the 650 fields
$subjects = $record->getFields('650');
if ($subjects) {
// Retrieve just the first 24_ field
print $record->getField('24.', true);
print "\n";
// Now print all of the retrieved subjects
foreach ($subjects as $field) {
print $field;
print "\n";
}
print "\n";
}
}
?>
When you iterate over a File_MARC_Data_Field object using foreach(), the MARC tag for the given field is returned as the key for the element and the set of subfields is returned as the value of the element.
Similarly, when you iterate over a File_MARC_Subfield object using foreach(), the code for the given subfield is returned as the key of the element and the value of the given subfield is returned as the value of the element.
Iterating over fields and subfields in a MARC record
In the following example, we iterate through a set of 650 fields to print out the subject headings contained in the subfields for each field.
<?php
require 'File/MARC.php';
// Retrieve a set of MARC records from a z39 result string
$bibrecords = new File_MARC($z39_result, File_MARC::SOURCE_STRING);
// Go through each record
while ($record = $bibrecords->next()) {
// Iterate through the fields
foreach ($record->getFields() as $tag => $subfields) {
// Skip everything except for 650 fields
if ($tag == '650') {
print "Subject:";
foreach ($subfields->getSubfields() as $code => $value) {
print " $value";
}
print "\n";
}
}
}
?>
Data fields, represented by the File_MARC_Data_Field class, offer a getIndicator() function to enable you to retrieve the value of an indicator.
Retrieving an indicator
In the following example, we retrieve the title (245) field of a MARC record and check the second indicator for the field to determine whether there are non-filing indicators that we should ignore when sorting the contents of the field.
<?php
require 'File/MARC.php';
$titleField = $record->getField('245');
$nonfiling = $titleField->getIndicator(2);
if ($nonfiling) {
// Sort using the subset of the $a subfield
$title = substr($titleField->getSubfield('a'), $nonfiling);
} else {
// Sort using the entire contents of the $a subfield
$title = $titleField->getSubfield('a');
}
?>
The File_MARC_Record class enables you to write Machine Readable Cataloging (MARC) data in MARC 21 format, in a human-readable string format, and (with some restrictions) in MARCXML format.
To return a record in MARC 21 format, call the toRaw() method on the File_MARC_Record object.
Writing MARC 21 data to a file
In the following example, we have created one or more MARC records
represented by File_MARC_Record objects
stored in the $records
array. To write this
data to a file in MARC 21 format, we simply open the file in binary
mode and write the contents of the records in the array to the file
by calling the toRaw() method on each record
in turn.
<?php
require 'File/MARC.php';
// convert_metadata_to_marc() is a fictional method
// that returns an array of File_MARC_Record objects
$records = convert_metadata_to_marc();
// Open a file for binary write access
$marc21_file = fopen("records.mrc", "wb");
// Iterate through the records
while ($record = $records->next()) {
// Write each record to the file in MARC 21 format
fwrite($marc21_file, $record->toRaw());
}
// Close the file
fclose($marc21_file);
?>
To return a human-readable version of a MARC 21 or MARCXML record, call the __toString() method on the File_MARC_Record object. Note that you call the __toString() method implicitly when you call the print() function on a File_MARC_Record object.
Returning a human-readable representation of MARC
In the following example, we print the contents of each MARC record in a human-readable format and also explicitly call the __toString() method so that we can write the human-readable contents to a file. Notice that it does not matter whether the source format is MARC or MARCXML, the methods we call to format the data for output are the same.
<?php
require 'File/MARCXML.php';
// Retrieve a set of MARCXML records from a string
$journals = new File_MARCXML($xml_data, File_MARC::SOURCE_STRING);
// Open a file for binary write access
$marc21_file = fopen("records.mrc", "wb");
// Iterate through the retrieved records
while ($record = $journals->next()) {
// Pretty print each record
print $record;
print "\n";
// Write the pretty-printed record to file
fwrite($marc21_file, $record->__toString() . "\n");
}
// Close the file
fclose($marc21_file);
?>
To return a record in MARCXML format, call the toXML() method on the File_MARC_Record object.
Significant restrictions on the toXML() method
Most significantly, PHP offers no means of converting from the MARC8 encoding that most legacy MARC records have been encoded in to a valid XML encoding such as UTF-8. MARC libraries in other languages have worked around this basic lack of infrastructure by creating their own character encoding conversion libraries. At this time, the author of File_MARC does not have the capacity to build the same support as a PEAR package but would welcome any assistance. Better still would be the addition of ANSEL and MARC8 encoding support to the iconv and ICU toolkits that are used to supply encoding conversion by most open-source projects and languages.
The toXML() method currently produces a single, complete, valid XML MARCXML document for a single File_MARC_Record object. You cannot simply concatenate the results of calling toXML() on two File_MARC_Record objects, because that will produce invalid an invalid XML document. At this time, it is up to the developer to extract the
record
node from each MARCXML document and concatenate them inside acollection
root element if they want to create a MARCXML document that contains more than a single record.
Writing MARCXML data to a file
In the following example, we have created a MARC record
represented by a File_MARC_Record object
stored in the $record
variable. To write this
data to a file in MARCXML format, we simply open the file in binary
mode and write the record to the file by calling the
toXML() method on the record object.
<?php
require 'File/MARC.php';
// Create a MARC record
$record = create_a_marc_record();
// Open a file for binary write access
$marcxml_file = fopen("records.mrc", "wb");
// Write the record to the file in MARCXML format
fwrite($marcxml_file, $record->toXML());
// Close the file
fclose($marcxml_file);
?>
Package to manage passwd-style files
The new File_Passwd package provides facilities to manage many different kinds of password files.
Unix, CVS, SMB, AuthUserFile, AuthDigestFile and custom formatted passwd files.
passwd-style means, the file has a format like this:
user<delimiter>password
The delimiting character is usually the colon.
With the new File_Passwd_Custom class you can choose your delimiting character, but be aware that any column of your passwd file MUST NOT contain the delimiter!
The File_Passwd class provides a factory for all special purpose classes, static authentication and common encryption methods.
Name | Value | Description |
---|---|---|
FILE_PASSWD_DES | "des" | DES encryption |
FILE_PASSWD_MD5 | "md5" | MD5 encryption |
FILE_PASSWD_SHA | "sha" | SHA encryption |
FILE_PASSWD_NT | "nt" | NT hash |
FILE_PASSWD_LM | "lm" | LM hash |
FILE_PASSWD_PLAIN | "plain" | no encryption |
Name | Value | Description |
---|---|---|
FILE_PASSWD_E_UNDEFINED | 0 | undefined - some seldom occuring errors |
FILE_PASSWD_E_INVALID_FORMAT | 1 | passwd file has invalid format |
FILE_PASSWD_E_INVALID_PROPERTY | 2 | an invalid (additional) property was supplied |
FILE_PASSWD_E_INVALID_CHARS | 3 | parameter contains illegal chracters (usually only alphanumerics, the dash and underline are allowed) |
FILE_PASSWD_E_INVALID_ENC_MODE | 4 | an invalid encryption mode was supplied (depending on the class actually used) |
FILE_PASSWD_E_EXISTS_ALREADY | 5 | an entry (user, group, etc) to add exists already |
FILE_PASSWD_E_EXISTS_NOT | 6 | an entry (user, group, etc) to delete/change doesn't exist |
FILE_PASSWD_E_USER_NOT_IN_GROUP | 7 | the specified user is not in this certain group |
FILE_PASSWD_E_USER_NOT_IN_REALM | 8 | the specified user is not in this certain realm |
FILE_PASSWD_E_PARAM_MUST_BE_ARRAY | 9 | the supplied param must be of type array |
FILE_PASSWD_E_METHOD_NOT_IMPLEMENTED | 10 | requested method was not implemented yet |
FILE_PASSWD_E_DIR_NOT_CREATED | 11 | a certain directory couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | 12 | passwd file couldn't be opened |
FILE_PASSWD_E_FILE_NOT_LOCKED | 13 | passwd file couldn't be locked |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | 14 | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | 15 | passwd file couldn't be closed |
string File_Passwd::apiVersion (
)
Returns API version of the File_Passwd package.
Returns string API version (currently 1.0.0).
This function can be called statically.
object &File_Passwd::factory
(
string $class
)
Load the desired worker class (extension).
$class
the desired extension of File_Passwd
Returns object File_Passwd extension or PEAR_Error on failure.
This function should be called statically.
mixed File_Passwd::staticAuth (
string $type
, string $file
, string $user
, string $pass
, mixed $opt = ''
)
Static user autentication.
Though this approach should be reasonable fast, it is NOT with APR compatible MD5 encryption used for htpasswd style password files encrypted in MD5.
Generating one MD5 password takes about 0.25 seconds!
Depending on $type
,
$opt
should be:
$type
Unix, Cvs, Smb, Authbasic or Authdigest
$file
path to passwd file
$user
the user to authenticate
$pass
the plaintext password
$opt
Returns TRUE if authenticated, FALSE if not, or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_NOT_EXISTS | passwd file doesn't exist |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in read mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked shared |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked (only if auth fails) |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed /only if auth fails) |
FILE_PASSWD_E_UNDEFINED | if class/file couldn't be loaded |
FILE_PASSWD_E_INVALID_ENC_MODE | supplied encryption mode is not supported |
FILE_PASSWD_E_USER_NOT_IN_REALM | user doesn't exist in this realm (only File_Passwd_Authdigest) |
This function should be called statically.
Base class for worker class extensions.
Class | Summary |
---|---|
File_Passwd_Authbasic | Manipulate AuthUserFiles as used for HTTP Basic Authentication. |
File_Passwd_Authdigest | Manipulate AuthDigestFiles as used for HTTP Digest Authentication. |
File_Passwd_Cvs | Manipulate CVS pserver passwd files. |
File_Passwd_Smb | Manipulate SMB server passwd files. |
File_Passwd_Unix | Manipulate standard Unix passwd files. |
File_Passwd_Custom | Manipulate custom formatted passwd files. |
Method Name | Summary |
---|---|
File_Passwd_Common::delUser() | Delete a certain user |
File_Passwd_Common::getFile() | Get path of passwd file |
File_Passwd_Common::listUser() | List user |
File_Passwd_Common::load() | Loads the file |
File_Passwd_Common::parse() | Parse the content of the file |
File_Passwd_Common::save() | Apply changes and rewrite passwd file |
File_Passwd_Common::setFile() | Set path to passwd file |
File_Passwd_Common::userExists() | Check if a certain user already exists |
File_Passwd_Common::_auth() | Base method for File_Passwd::staticAuth() |
File_Passwd_Common::_close() | Closes a prior opened and locked file handle |
File_Passwd_Common::_open() | Opens a file, locks it exclusively and returns the filehandle |
File_Passwd_Common::_save() | Save the modified content to the passwd file |
mixed^File_Passwd_Common::delUser (
string $user
)
Delete a certain user.
$user
username
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exist |
This function can not be called statically.
mixed File_Passwd_Common::listUser (
string $user = ''
)
List one user's properties or all users.
$user
the user to list or all users if empty
Returns array of user(s) or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_NOT_EXISTS | suer doesn't exist |
This function can not be called statically.
bool File_Passwd_Common::userExists (
string $user
)
Check if a certain user already exists.
$user
the name of the user to check if already exists
Returns boolean whether user already exists.
This function can not be called statically.
string File_Passwd_Common::getFile (
)
Get the path of the passwd file.
Returns string path of passwd file.
This function can not be called statically.
void File_Passwd_Common::setFile (
string $file
)
Set path to passwd file.
$file
path to passwd file
This function can not be called statically.
mixed File_Passwd_Common::load (
)
Loads the passwd file and calls the parse() method of the extending child class.
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_DIR_NOT_CREATED | the directory in which the passwd file should reside couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in read mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked shared |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed |
FILE_PASSWD_E_INVALID_FORMAT | passwd file has invalid format |
This function can not be called statically.
object File_Passwd_Common::parse (
)
This is kinda abstract method which only returns a PEAR_Error, so it is to be overwritten in the extending child class.
You must overwrite this method in your File_Passwd_* class.
(package developer related)
Returns object PEAR_Error FILE_PASSWD_E_METHOD_NOT_IMPLEMENTED.
This function can not be called statically.
object File_Passwd_Common::save (
)
This is kinda abstract method which only returns a PEAR_Error, so it is to be overwritten in the extending child class.
You must overwrite this method in your File_Passwd_* class.
(package developer related)
Returns object PEAR_Error FILE_PASSWD_E_METHOD_NOT_IMPLEMENTED.
This function can not be called statically.
mixed File_Passwd_Common::_auth (
string $file
, string $id
)
Base method for File_Passwd_*::staticAuth()
(package developer related)
$file
path to passwd file
$id
user_id to search for
Returns string line of passwd file containing $id, FALSE if $id wasn't found, or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | passwd file doesn't exist |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in read mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked shared |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked (only if auth fails) |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed (only if auth fails) |
This function can not be called statically.
mixed &File_Passwd_Common::_open (
string $mode
, mixed $file = null
)
Opens a file, locks it exclusively and returns the filehandle.
(package developer related)
$mode
the mode to open the file with
$file
path to passwd file
Returns resource file handle or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_DIR_NOT_CREATED | the directory in which the passwd file should reside couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in the desired mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked |
This function can not be called statically.
mixed File_Passwd_Common::_close (
resource &$file_handle
)
Closes a prior with File_Passwd_Common::_open() opened and locked file handle.
(package developer related)
&$file_handle
the file handle to operate on
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed |
This function can not be called statically.
mixed File_Passwd_Common::_save (
string $content
)
Save content to file.
(package developer related)
$content
file content
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_DIR_NOT_CREATED | the directory in which the passwd file should reside couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in write mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked exclusively |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed |
This function can not be called statically.
object File_Passwd_Common::__construct (
string $file = 'passwd'
)
Implemented for Zend Engine 2 compatibility.
(package developer related)
$file
path to passwd file
Returns object File_Passwd_* - new instance of an File_Passwd_* object.
This function can not be called statically.
Manipulate custom formatted passwd files. (inherited methods)
mixed File_Passwd_Custom::staticAuth (
string $file
, string $user
, string $pass
, array $opts
)
Static user authentication.
$file
path to passwd file
$user
user to authenticate
$pass
plaintext password
$opts
A two element array containing the encryption function to use and the delimiting character: e.g. array('md5', '|')
Returns TRUE if authenticated, FALSE if not or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_NOT_EXISTS | passwd file doesn't exist |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in read mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked shared |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked (only if auth fails) |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed (only if auth fails) |
This function should be called statically.
object &new File_Passwd_custom (
string $file = 'passwd'
)
Initialize a new File_Passwd_Custom object with the specified path to passwd file.
$file
path to passwd file
Returns object File_Passwd_Custom.
This function can not be called statically.
mixed File_Passwd_Custom::parse (
)
Parse the custom passwd file. (package developer related)
This usually happens in File_Passwd_Custom::load() .
Returns TRUE on success, PEAR_Error on failure.
Returns PEAR_Error FILE_PASSWD_E_INVALID_FORMAT, if passwd file has illegal format.
This function can not be called statically.
mixed File_Passwd_Custom::save (
)
Apply changes and rewrite passwd file.
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_DIR_NOT_CREATED | directory in which the passwd file should reside couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in write mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked exclusively |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed |
This function can not be called statically.
mixed File_Passwd_Custom::addUser (
string $user
, string $pass
, array $extra = array()
)
The username must start with an alphabetical character and must NOT contain any other characters than alphanumerics, the underline and dash.
The username MUST NOT contain the custom delimiter!
If you use the 'name map' you should also use these naming in the supplied extra array, because your values would get mixed up if they are in the wrong order, which is always true if you DON'T use the 'name map'!
So be warned and USE the 'name map'!
$user
the name of the user to add
$pass
the password of the user to add
$extra
extra properties of user to add
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_ALREADY_EXISTS | user already exists |
FILE_PASSWD_E_INVALID_CHARS | username contains illegal characters |
FILE_PASSWD_E_INVALID_CHARS | any of the extra proporties contains the delimiter |
FILE_PASSWD_E_INVALID_ENC_MODE | actual encryption mode is not supported |
FILE_PASSWD_E_UNDEFINED | if passwd file is shadowed |
This function can not be called statically.
mixed File_Passwd_Custom::modUser (
string $user
, array $properties = array()
)
You shouldn't modify the password of the user with this method, use File_Passwd_Custom::changePasswd() instead.
You should use this method only if the 'name map' is used, too.
$user
the user to modify
$properties
an associative array of properties to modify
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exists |
FILE_PASSWD_E_INVALID_CHARS | any of the extra properties contains the delimiter |
This function can not be called statically.
mixed File_Passwd_Custom::changePasswd (
string $user
, string $pass
)
Change the password of a certain user.
$user
the user whose password should be changed
$pass
the new plaintext password
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exist |
FILE_PASSWD_INVALID_ENC_MODE | actual encryption mode is not supported |
This function can not be called statically.
mixed File_Passwd_Custom::verifyPasswd (
string $user
, string $pass
)
Verify the password of a certain user.
$user
the user whose password should be verified
$pass
the password to verify
Returns TRUE if passwords equal, FALSE if they don't or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exist |
FILE_PASSWD_E_INVALID_ENC_MODE | actual encryption mode isn't supported |
This function can not be called statically.
boolean File_Passwd_Custom::useMap (
boolean $bool = null
)
Whether to use the 'name map' of the extra properties or not.
You first must supply a 'name map' to use it.
$bool
whether to use the 'name map' or not
Returns boolean TRUE if you set a value, or the actual value if called without param.
This function can not be called statically.
array File_Passwd_Custom::getMap (
)
Get the 'name map' which is used for the extra properties of the user.
Returns array 'name map'.
This function can not be called statically.
mixed File_Passwd_Custom::setMap (
mixed $map = array()
)
Set the 'name map' to use with the extra properties of the user.
This map is used for naming the associative array of the extra properties.
$map
the 'name map'
Returns TRUE on success, PEAR_Error on failure.
Returns PEAR_Error FILE_PASSWD_E_PARAM_MUST_BE_ARRAY, if the supplied 'name map' was not of type array.
This function can not be called statically.
Manipulate standard Unix passwd files. (inherited methods)
mixed File_Passwd_Unix::staticAuth (
string $file
, string $user
, string $pass
, string $mode
)
Static user authentication.
$file
path to passwd file
$user
user to authenticate
$pass
plaintext password
$mode
des or md5
Returns TRUE if authenticated, FALSE if not or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_NOT_EXISTS | passwd file doesn't exist |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in read mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked shared |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked (only if auth fails) |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed (only if auth fails) |
This function should be called statically.
object &new File_Passwd_Unix (
string $file = 'passwd'
)
Initialize a new File_Passwd_Unix object with the specified path to passwd file.
$file
path to passwd file
Returns object File_Passwd_Unix.
This function can not be called statically.
mixed File_Passwd_Unix::parse (
)
Parse the unix passwd file. (package developer related)
This usually happens in File_Passwd_Unix::load() .
Returns TRUE on success, PEAR_Error on failure.
Returns PEAR_Error FILE_PASSWD_E_INVALID_FORMAT, if passwd file has illegal format.
This function can not be called statically.
mixed File_Passwd_Unix::save (
)
Apply changes and rewrite passwd file.
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_DIR_NOT_CREATED | directory in which the passwd file should reside couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in write mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked exclusively |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed |
This function can not be called statically.
mixed File_Passwd_Unix::addUser (
string $user
, string $pass
, array $extra = array()
)
The username must start with an alphabetical character and must NOT contain any other characters than alphanumerics, the underline and dash.
If you use the 'name map' you should also use these naming in the supplied extra array, because your values would get mixed up if they are in the wrong order, which is always true if you DON'T use the 'name map'!
So be warned and USE the 'name map'!
If the passwd file is shadowed, the user will be added though, but with an 'x' as password, and a PEAR_Error will be returned, too.
$user
the name of the user to add
$pass
the password of the user to add
$extra
extra properties of user to add
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_ALREADY_EXISTS | user already exists |
FILE_PASSWD_E_INVALID_CHARS | username contains illegal characters |
FILE_PASSWD_E_INVALID_CHARS | any of the extra proporties contains a colon |
FILE_PASSWD_E_INVALID_ENC_MODE | actual encryption mode is not supported |
FILE_PASSWD_E_UNDEFINED | if passwd file is shadowed |
This function can not be called statically.
mixed File_Passwd_Unix::modUser (
string $user
, array $properties = array()
)
You shouldn't modify the password of the user with this method, use File_Passwd_Unix::changePasswd() instead.
You should use this method only if the 'name map' is used, too.
$user
the user to modify
$properties
an associative array of properties to modify
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exists |
FILE_PASSWD_E_INVALID_CHARS | any of the extra properties contains a colon |
This function can not be called statically.
mixed File_Passwd_Unix::changePasswd (
string $user
, string $pass
)
Change the password of a certain user.
$user
the user whose password should be changed
$pass
the new plaintext password
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exist |
FILE_PASSWD_INVALID_ENC_MODE | actual encryption mode is not supported |
FILE_PASSWD_UNDEFINED | if passwd file is shadowed |
This function can not be called statically.
mixed File_Passwd_Unix::verifyPasswd (
string $user
, string $pass
)
Verify the password of a certain user.
$user
the user whose password should be verified
$pass
the password to verify
Returns TRUE if passwords equal, FALSE if they don't or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exist |
FILE_PASSWD_E_INVALID_ENC_MODE | actual encryption mode isn't spported |
This function can not be called statically.
boolean File_Passwd_Unix::useMap (
boolean $bool = null
)
Whether to use the 'name map' of the extra properties or not.
Default Unix passwd files look like:
user:password:user_id:group_id:gecos:home_dir:shell
The default 'name map' for properties except user and password looks like:
If you want to change the naming of the standard map use File_Passwd_Unix::setMap().
$bool
whether to use the 'name map' or not
Returns boolean TRUE if you set a value, or the actual value if called without param.
This function can not be called statically.
array File_Passwd_Unix::getMap (
)
Get the 'name map' which is used for the extra properties of the user.
Returns array 'name map'.
This function can not be called statically.
mixed File_Passwd_Unix::setMap (
mixed $map = array()
)
Set the 'name map' to use with the extra properties of the user.
This map is used for naming the associative array of the extra properties.
$map
the 'name map'
Returns TRUE on success, PEAR_Error on failure.
Returns PEAR_Error FILE_PASSWD_E_PARAM_MUST_BE_ARRAY, if the supplied 'name map' was not of type array.
This function can not be called statically.
string File_Passwd_Unix::getMode (
)
Get actual encryption mode (des|md5).
Returns string actual encryption mode.
This function can not be called statically.
mixed File_Passwd_Unix::setMode (
string $mode
)
Set encryption mode to use.
Supported encryption modes are 'des' and 'md5'.
You can use the constants FILE_PASSWD_MD5 and FILE_PASSWD_DES for this purpose.
$mode
encryption mode to use
Returns TRUE on success, PEAR_Error on failure.
Returns PEAR_Error FILE_PASSWD_E_INVALID_ENC_MODE, if supplied encryption mode is not supported.
This function can not be called statically.
array File_Passwd_Unix::listModes (
)
List the supported encryption modes.
<pre>
array
+ md5
+ des
</pre>
Returns array supported encryption modes.
This function can not be called statically.
boolean File_Passwd_Unix::isShadowed (
)
Check if the passwords of this passwd file are shadowed in another file.
Returns boolean whether passwords of this passwd file are shadowed in another file.
This function can not be called statically.
mixed File_Passwd_Unix::generatePassword (
string $pass
, string $mode = 'md5'
, string $salt = null
)
Generate a "Un*x" style password.
The encryption mode can be of any type File_Passwd provides, although FILE_PASSWD_MD5 and FILE_PASSWD_DES are the most common.
$pass
the plaintext password to encrypt
$mode
the encryption mode to use
$salt
the salt to use for encryption (usually empty)
Returns string encrypted password, or PEAR_Error FILE_PASSWD_E_INVALID_ENC_MODE if encryption mode is not supported.
File_Passwd_Unix::generatePassword()
<?php
require_once 'File/Passwd/Unix.php';
$pass = File_Passwd_Unix::generatePassword('secret', FILE_PASSWD_MD5);
?>
This function should be called statically.
Manipulate CVS pserver passwd files. (inherited methods)
mixed File_Passwd_Cvs::staticAuth (
string $file
, string $user
, string $pass
)
Static user authentication.
$file
path to passwd file
$user
user to authenticate
$pass
plaintext password
Returns TRUE if authenticated, FALSE if not or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_NOT_EXISTS | passwd file doesn't exist |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in read mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked shared |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked (only if auth fails) |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed /only if auth fails) |
This function should be called statically.
object &new File_Passwd_Cvs (
mixed $file = 'passwd'
)
Initialize a new File_Passwd_Cvs object with the given path to passwd file.
$file
path to passwd file
Returns object File_Passwd_Cvs.
This function can not be called statically.
mixed File_Passwd_Cvs::parse (
)
Parse the CVS passwd file. (package developer related)
This usually happens in File_Passwd_Cvs::load() .
Returns TRUE on success, PEAR_Error on failure.
Returns PEAR_Error FILE_PASSWD_E_INVALID_FORMAT, if passwd file has invalid format.
This function can not be called statically.
mixed File_Passwd_Cvs::save (
)
Apply changes and rewrite CVS passwd file.
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_DIR_NOT_CREATED | directory in which the passwd file should reside couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in write mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked exclusively |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed |
This function can not be called statically.
mixed File_Passwd_Cvs::addUser (
string $user
, string $pass
, string $system_user = ''
)
The username must start with an alphabetical character and must NOT contain any other characters than alphanumerics, the underline and dash.
$user
the name of the user to add
$pass
the password of the user tot add
$system_user
the systemuser this user maps to
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_ALREADY | user already exists |
FILE_PASSWD_E_INVALID_CHARS | user or system_user contain illegal characters |
This function can not be called statically.
mixed File_Passwd_Cvs::changeSysUser (
mixed $user
, mixed $system
)
Change the corresponding system user of a certain cvs user.
$user
the user to change the system user for
$system
the new system user name
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exist |
FILE_PASSWD_E_INVALID_CHARS | system user contains illegal characters |
This function can not be called statically.
mixed File_Passwd_Cvs::changePasswd (
mixed $user
, mixed $pass
)
Change the password of a certain user.
$user
the user whose password should be changed
$pass
the new plaintext password
$return.success-pearerror;
Returns PEAR_Error FILE_PASSWD_E_EXISTS_ALREADY, if user already exists.
This function can not be called statically.
mixed File_Passwd_Cvs::verifyPasswd (
string $user
, string $pass
)
Verify the password of a certain user.
$user
user whose password should be verified
$pass
the plaintext password that should be verified
Returns TRUE if passwords equal, FALSE if the don't or PEAR_Error on failure.
Returns PEAR_Error FILE_PASSWD_E_EXISTS_NOT, if user doesn't exist.
This function can not be called statically.
string File_Passwd_Cvs::generatePassword (
string $pass
, string $salt = null
)
Generate a "CVS" pserver style password.
$pass
the plaintext password to encrypt
$salt
the salt to use for encryption (usually empty)
Returns string encrypted password.
File_Passwd_Cvs::generatePassword()
<?php
require_once 'File/Passwd/Cvs.php';
$pass = File_Passwd_Cvs::generatePassword('secret');
?>
This function should be called statically.
Manipulate SMB server passwd files. (inherited methods)
mixed File_Passwd_Smb::staticAuth (
string $file
, string $user
, string $pass
, string $mode
)
Static user authentication.
$file
path to passwd file
$user
user to authenticate
$pass
plaintext password
$mode
encryption mode ('nt'|'lm') NTHASH or LMHASH
Returns TRUE if authenticated, FALSE if not or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_NOT_EXISTS | passwd file doesn't exist |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in read mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked shared |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked (only if auth fails) |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed /only if auth fails) |
FILE_PASSWD_E_INVALID_ENC_MODE | supplied encryption mode was invalid |
This function should be called statically.
object &new File_Passwd_Smb (
string $file = 'smbpasswd'
)
Initialize a new File_Passwd_Smb object with the given path to passwd file.
$file
path to SMB passwd file
Returns object File_Passwd_Smb.
This function can not be called statically.
mixed File_Passwd_Smb::parse (
)
Parse the SMB passwd file. (package developer related)
This usually happens in File_Passwd_Smb::load() .
Returns TRUE on success, PEAR_Error on failure.
Returns PEAR_Error FILE_PASSWD_E_INVALID_FORMAT, if passwd file has invalid format.
This function can not be called statically.
mixed File_Passwd_Smb::save (
)
Apply changes and rewrite SMB passwd file.
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_DIR_NOT_CREATED | directory in which the passwd file should reside couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in write mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked exclusively |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed |
This function can not be called statically.
mixed File_Passwd_Smb::addUser (
string $user
, string $pass
, array $params
, boolean $isMachine = false
)
Add an SMB user/machine account.
$user
the user/machine to add
$pass
the new plaintext password
$params
additional properties of account:
$isMachine
whether to add an machine account
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_ALREADY | account already exists |
FILE_PASSWD_E_INVALID_CHARS | user/machine name contains illegal characters |
This function can not be called statically.
mixed File_Passwd_Smb::modUser (
string $user
, array $params
)
Modify a certain user.
You shouldn't modify the password with this method, use File_Passwd_Smb::changePasswd() instead.
$user
the user to modify
$params
an associative array of properties to change
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | account doesn't exist |
FILE_PASSWD_E_INVALID_PROPERTY | any supplied property was invalid |
This function can not be called statically.
mixed File_Passwd_Smb::changePasswd (
string $user
, string $pass
)
Change the passwd of a certain user.
$user
the user whose passwd should be changed
$pass
the new plaintext passwd
Returns TRUE on success, PEAR_Error on failure.
Returns PEAR_Error FILE_PASSWD_E_EXISTS_NOT, if user doesn't exist.
This function can not be called statically.
mixed File_Passwd_Smb::verifyPasswd (
string $user
, string $pass
)
Verifies an account with the given plaintext password.
$user
username
$pass
the plaintext password
Returns TRUE if passwds equal, FALSE if they don't or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exist |
FILE_PASSWD_E_UNDEFINED | if account is disabled |
This function can not be called statically.
mixed File_Passwd_Smb::verifyEncryptedPasswd (
string $user
, string $nthash
, string $lmhash = ''
)
Verify the encrypted password of an user/machine.
We prefer NT-Hash instead of weak LAN-Manager-Hash.
$user
username
$nthash
NT-Hash in hex
$lmhash
LAN-Manager-Hash in hex
Returns TRUE if passwds equal, FALSE if they don't or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exist |
FILE_PASSWD_E_UNDEFINED | if account is disabled |
This function can not be called statically.
string File_Passwd_Smb::generatePassword (
string $pass
, string $mode = 'nt'
)
Generate a "Samba" server style password.
The encryption mode can either be FILE_PASSWD_NT or FILE_PASSWD_LM.
$pass
the plaintext password to encrypt
$mode
the encryption mode to use
Returns string encrypted password.
File_Passwd_Smb::generatePassword()
<?php
require_once 'File/Passwd/Smb.php';
$pass = File_Passwd_Smb::generatePassword('secret', FILE_PASSWD_LM);
?>
This function should be called statically.
See also Crypt_CHAP_MSv1::ntPasswordHash() , Crypt_CHAP_MSv1::lmPasswordHash() .
Manipulate AuthUserFiles. (inherited methods)
mixed File_Passwd_Authbasic::staticAuth (
string $file
, string $user
, string $pass
, string $mode
)
Static user authentication.
$file
path to passwd file
$user
user to authenticate
$pass
plaintext password
$mode
des, sha or md5
Returns TRUE if authenticated, FALSE if not or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_NOT_EXISTS | passwd file doesn't exist |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in read mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked shared |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked (only if auth fails) |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed /only if auth fails) |
This function should be called statically.
object &new File_Passwd_Authbasic
(
string $file = '.htpasswd'
)
Initialise a new File_Passwd_Authbasic object with the given path to the passwd file.
$file
path to AuthUserFile
Returns object File_Passwd_Authbasic.
This function can not be called statically.
mixed File_Passwd_Authbasic::parse (
)
Parse the AuthUserFile. (package developer related)
This usually happens in File_Passwd_Authbasic::load() .
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_INVALID_FORMAT | passwd file has invalid format |
This function can not be called statically.
mixed File_Passwd_Authbasic::save (
)
Apply changes and rewrite AuthUserFile.
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_DIR_NOT_CREATED | directory in which the passwd file should reside couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in write mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked exclusively |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed |
This function can not be called statically.
mixed
File_Passwd_Authbasic::addUser
(
string $user
, string $pass
)
The username must start with an alphabetical character and must NOT contain any other characters than alphanumerics, the underline and dash.
$user
username
$pass
plaintext password
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_ALREADY | the user to add already exists |
FILE_PASSWD_E_INVALID_CHARS | the username to add contains illegal characters |
This function can not be called statically.
mixed
File_Passwd_Authbasic::changePasswd
(
string $user
, string $pass
)
Change the password of a certain user.
$user
the user whose password should be changed
$pass
the new plaintext password
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | the user to delete doesn't exist |
This function can not be called statically.
mixed File_Passwd_Authbasic::verifyPasswd (
string $user
, string $pass
)
Verify the password of a certain user.
$user
the user whose password should be verified
$pass
the plaintext password to verify
Returns TRUE if passwords equal, FALSE if they don't, or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | user doesn't exist |
FILE_PASSWD_E_INVALID_ENC_MODE | invalid encryption mode was supplied |
This function can not be called statically.
string
File_Passwd_Authbasic::getMode
(
)
Get the actual encryption mode.
string - actual encryption mode
This function can not be called statically.
mixed File_Passwd_Authbasic::setMode (
string $mode
)
You can choose one of md5, sha or des.
ATTN: DES encryption not available on Win32!
Returns a PEAR_Error if a specific encryption mode is not supported.
$mode
the encryption mode to use
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
array
File_Passwd_Authbasic::listModes (
)
Returns an array of supported encryption modes.
<pre>
array
+ md5
+ sha
+ des
</pre>
ATTN: DES encryption not available on Win32!
array - supported encryption modes.
This function can not be called statically.
mixed File_Passwd_Authbasic::generatePassword (
string $pass
, string $mode = 'des'
, string $salt = null
)
Generate a password usable for "AuthBasic" authentication.
The encryption mode can either be FILE_PASSWD_DES, FILE_PASSWD_SHA or FILE_PASSWD_MD5.
$pass
the plaintext password to encrypt
$mode
the encryption mode to use
$salt
the salt to use for encryption (usually empty)
Returns string encrypted password, or PEAR_Error FILE_PASSWD_E_INVALID_ENC_MODE if encryption mode is not supported.
File_Passwd_Authbasic::generatePassword()
<?php
require_once 'File/Passwd/Authbasic.php';
$pass = File_Passwd_Authbasic::generatePassword('secret', FILE_PASSWD_MD5);
?>
This function should be called statically.
Manipulate AuthDigestFiles. (inherited methods)
mixed File_Passwd_Authdigest::staticAuth (
string $file
, string $user
, string $pass
, string $realm
)
Static user authentication.
$file
path to passwd file
$user
user to authenticate
$pass
plaintext password
$realm
the realm the user should be in
Returns TRUE if authenticated, FALSE if not or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_NOT_EXISTS | passwd file doesn't exist |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in read mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked shared |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked (only if auth fails) |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed (only if auth fails) |
This function should be called statically.
object &new File_Passwd_Authdigest (
string $file = '.htdigest'
)
Initialize a new object of File_Passwd_Authdigest with the specified path to the AuthDigestFile.
$file
path to AuthDigestFile
Returns object File_Passwd_Authdigest.
This function can not be called statically.
mixed File_Passwd_Authdigest::parse (
)
Parse the AuthDigestFile. (package developer related)
This usually happens in File_Passwd_Authdigest::load() .
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_INVALID_FORMAT | AuthDigestFile has invalid format |
This function can not be called statically.
mixed File_Passwd_Authdigest::save (
)
Apply changes and rewrite AuthDigestFile.
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_DIR_NOT_CREATED | directory in which the passwd file should reside couldn't be created |
FILE_PASSWD_E_FILE_NOT_OPENED | passwd file couldn't be opened in write mode |
FILE_PASSWD_E_FILE_NOT_LOCKED | passwd file couldn't be locked exclusively |
FILE_PASSWD_E_FILE_NOT_UNLOCKED | passwd file couldn't be unlocked |
FILE_PASSWD_E_FILE_NOT_CLOSED | passwd file couldn't be closed |
This function can not be called statically.
mixed File_Passwd_Authdigest::addUser (
string $user
, string $realm
, string $pass
)
Add an user to the AuthDigestFile.
$user and $realm must start with an alphabetical charachter and must NOT contain any other characters than alphanumerics, the underline and dash.
$user
the user to add
$realm
the realm the user should be in
$pass
the plaintext password
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_ALREADY | user already exists in the supplied realm |
FILE_PASSWD_E_INVLAID_CHARS | user or realm contains illegal characters |
This function can not be called statically.
mixed File_Passwd_Authdigest::changePasswd (
string $user
, string $realm
, string $pass
)
Change the password of a certain user in a specific realm.
This method in fact adds the user whith the new password after deleting the user.
$user
the user whose password should be changed
$realm
the realm the user is in
$pass
the new plaintext password
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_USER_NOT_IN_REALM | user doesn't exist in the supplied realm |
FILE_PASSWD_E_INVALID_CHARS | user or realm contains illegal characters |
This function can not be called statically.
mixed File_Passwd_Authdigest::verifyPasswd (
string $user
, string $realm
, string $pass
)
Verify the password of an user in a certain realm.
$user
the user whose password should be verified
$realm
the realm the user is in
$pass
the plaintext password to verify
Retruns TRUE if passwords equal, FALSE if they don't, or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_USER_NOT_IN_REALM | the specified user doesn't exist in the supplied realm |
This function can not be called statically.
mixed File_Passwd_Authdigest::delUserInRealm (
string $user
, string $inRealm
)
Delete a certain user in a specific realm.
$user
the user to remove
$inRealm
the realm the user should be in
Returns TRUE on success, PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_USER_NOT_IN_REALM | user doesn't exist in the supplied realm |
This function can not be called statically.
array File_Passwd_Authdigest::listUserInRealm (
string $inRealm = ''
)
List all user af either one specific or all realms.
$inRealm
the realm to list users of
Returns array:
This function can not be called statically.
boolean File_Passwd_Authdigest::userInRealm (
string $user
, string $realm
)
Ckeck if a certain user is in a specific realm.
$user
the user to check
$realm
the realm the user shuold be in
Returns TRUE if user is in realm, FALSE if not or PEAR_Error on failure.
Error Code | Summary |
---|---|
FILE_PASSWD_E_EXISTS_NOT | specified realm doesn't exist |
This function can not be called statically.
string File_Passwd_Authdigest::generatePassword (
string $user
, string $realm
, string $pass
)
Generate a password usable for "AuthDigest" authentication.
$user
the username
$realm
the realm the user is in
$pass
the plaintext password
Returns string encrypted password.
File_Passwd_Authdigest::generatePassword()
<?php
require_once 'File/Passwd/Authdigest.php';
$pass = File_Passwd_Authdigest::generatePassword('mike', 'restricted', 'secret');
?>
This function should be called statically.
This package offers methods for reading and writing IDv1-information tags of MP3 files.
Id.php
Name | Value | Meaning |
---|---|---|
PEAR_MP3_ID_FNO | 1 | Error code: file not found |
PEAR_MP3_ID_NOMP3 | 4 | Error code: file is not a MP3 file |
PEAR_MP3_ID_RE | 2 | Error code: read error |
PEAR_MP3_ID_TNF | 3 | Error code: Tag not found |
void MP3_Id::MP3_Id (
boolean $study
= false
)
Constructor
$study
Enables an in-deep look in the file (increase execution time!)
This function can not be called statically.
void MP3_Id::copy (
string $from
)
Sets the ID3 tags to the same as the tags in
$from
$from
Name of the variable storing an other instance of MP3_Id
This function can not be called statically.
array MP3_Id::genres (
)
Returns a list of genre numbers and names
array
- list of genres
This function can not be called statically.
mixed MP3_Id::getGenre (
integer $genreno
)
Return the name of a genre number, if no genre number is specified the genre number found in the file will be used.
$genreno
Number of the genre
mixed FALSE, if no genre found, else string
This function can not be called statically.
integer MP3_Id::getGenreNo (
string $genre
, integer $default = 0xff
)
Get the number of a genre.
$genre
Name of the genre
$default
The value to return in case of genre not found
This function can not be called statically.
mixed MP3_Id::getTag (
string $name
, mixed $default
)
Get the value of a tag
$name
the name of the tag to get
$default
returned, if the tag not exists
mixed The value of the field
This function can not be called statically.
void MP3_Id::read (
string $file
)
Reads the given file and parses it.
$file
the name of the file to parse
Error code | Error message | Reason | Solution |
---|---|---|---|
PEAR_MP3_ID_FNO | "Unable to open $file" | The provide file name could not be opened | The file is currently write protected or doesn't exists. |
PEAR_MP3_ID_RE | "Unable to see to end - 128 of $file" or "Unable to see to end of $file" | A read error occured | The file is too short or empty - not a MP3 file or a corrupted one. |
This function can not be called statically.
void MP3_Id::remove (
boolean $id3v1
= true
, boolean $id3v2
= true
)
Removes all tags from a file.
$id3v1
TRUE to remove tags
$id3v2
unused
Error code | Error message | Reason | Solution |
---|---|---|---|
PEAR_MP3_ID_FNO | "Unable to open $file" | The provide file name could not be opened | The file is currently write protected or doesn't exists. |
PEAR_MP3_ID_RE | "Unable to see to end - 128 of $file" or "Unable to see to end of $file" | A read error occured | The file is too short or empty - not a MP3 file or a corrupted one. |
This function can not be called statically.
void MP3_Id::setTag (
mixed $name
, mixed $value
)
Sets a field
Tag | Meaning |
---|---|
name | Title of the content |
artists | Name of band or artist |
album | Name of the album |
year | publishing year of the album or song |
comment | song comment |
track | the number of the track |
genre | genre of the song |
genreno | Number of the genre |
$name
Name of the tag to set or hash with the key as fieldname
$value
the value to set
This function can not be called statically.
void MP3_Id::study (
void
)
Does extra work to get the MPEG frame info.
Error code | Error message | Reason | Solution |
---|---|---|---|
PEAR_MP3_ID_NOMP3 | "No mpeg frame found" | The file have no MP3 file structure. | The file is not a MP3 file. |
This function can not be called statically.
void MP3_Id::write (
boolean $v1
= true
)
Updates the tags on the file.
$v1
if TRUE update/create an Version 1-tag on the file
Error code | Error message | Reason | Solution |
---|---|---|---|
PEAR_MP3_ID_FNO | "Unable to open $file" | The provide file name could not be opened | The file is currently write protected or doesn't exists. |
PEAR_MP3_ID_RE | "Unable to see to end - 128 of $file" or "Unable to see to end of $file" | A read error occured | The file is too short or empty - not a MP3 file or a corrupted one. |
This function can not be called statically.
The OpenDocument package lets you create office files according to the OASIS OpenDocument specification. As of version 0.1.2, only text documents can be created.
Creating an OpenDocument text document is really simple.
<?php
/**
* This script demonstrates how to create a new text document by
* adding a headline and a paragraph containing a link.
*/
require_once 'OpenDocument.php';
$doc = new OpenDocument();
$doc->createHeading('Welcome to OpenDocument!', 1);
$p = $doc->createParagraph('This script serves as a demonstration of PEAR\'s
OpenDocument package. You are invited to explore the capabilities of this
package and to read the API documentation available at ');
$p->createHyperlink('pear.php.net', 'http://pear.php.net/package/OpenDocument');
$p->createTextElement('. Have fun!');
$doc->save('my-first-document.odt');
?>
After creating an OpenDocument instance, there are several
methods
available to create actual content; all of them are prefixed with
create
.
All of those methods return the created object. This object has already been inserted at the end of the document.
The newly created element may have some create*()
methods
of its own - a
paragraph for example
allows you to add
links
and
additional text
elements.
When you are done, call OpenDocument::save() with the filename as only parameter to store the document on disk.
Until version 0.1.2, only relative filenames are allowed.
Package for generating Excel spreadsheets
Spreadsheet_Excel_Writer is a tool for creating Excel files without the need for COM components. The files generated by the current version of Spreadsheet_Excel_Writer correspond to the Excel 5 (BIFF5) format, so all functionality until that version of Excel (but not beyond) should be available.
The most common use for Spreadsheet_Excel_Writer will be spitting out large (or not so large) amounts of information in the form of a spreadsheet, which is easy to manipulate with a fairly ubiquitous spreadsheet program such as Excel (or OpenOffice).
So let's cut to the chase and see how this is done:
Typical usage
<?php
require_once 'Spreadsheet/Excel/Writer.php';
// Creating a workbook
$workbook = new Spreadsheet_Excel_Writer();
// sending HTTP headers
$workbook->send('test.xls');
// Creating a worksheet
$worksheet =& $workbook->addWorksheet('My first worksheet');
// The actual data
$worksheet->write(0, 0, 'Name');
$worksheet->write(0, 1, 'Age');
$worksheet->write(1, 0, 'John Smith');
$worksheet->write(1, 1, 30);
$worksheet->write(2, 0, 'Johann Schmidt');
$worksheet->write(2, 1, 31);
$worksheet->write(3, 0, 'Juan Herrera');
$worksheet->write(3, 1, 32);
// Let's send the file
$workbook->close();
?>
The first thing you should notice, is that we created a workbook before any worksheets. All worksheets are contained within a workbook, and a workbook may contain several worksheets.
Another important thing, which you should have in mind when programming with Spreadsheet_Excel_Writer, is that ampersand sign (&) that appears when we created our worksheet. That ampersand means we are referencing a Worksheet object instead of copying it. If you don't know what that means, don't worry, all you have to remember is to always use ampersands when calling addWorksheet() for creating a worksheet, or addFormat() for creating a format.
You may have noticed also the following line:
What that means is that we are sending our spreadsheet to a browser. But what if we just want to save the spreadsheet in our machine? Well, you just have to omit that line and give a valid file path to the workbook constructor.
For example, if we wanted to save the same spreadsheet we created in our first example to a file named 'test.xls', we would do it like so:
Saving to a regular file
<?php
require_once 'Spreadsheet/Excel/Writer.php';
// We give the path to our file here
$workbook = new Spreadsheet_Excel_Writer('test.xls');
$worksheet =& $workbook->addWorksheet('My first worksheet');
$worksheet->write(0, 0, 'Name');
$worksheet->write(0, 1, 'Age');
$worksheet->write(1, 0, 'John Smith');
$worksheet->write(1, 1, 30);
$worksheet->write(2, 0, 'Johann Schmidt');
$worksheet->write(2, 1, 31);
$worksheet->write(3, 0, 'Juan Herrera');
$worksheet->write(3, 1, 32);
// We still need to explicitly close the workbook
$workbook->close();
?>
If you would like to learn about formatting (fonts, cell color, text alignment, etc...) with Spreadsheet_Excel_Writer, you can check the formatting tutorial here.
A format is an object of type Spreadsheet_Excel_Writer_Format. This format can be applied to cells inside a spreadsheet so that these cells inherit the properties of the format (text alignment, background color, border colors, etc...).
Formats can't be created directly by a new call. You have to create a format using the addFormat() method from a Workbook, which associates your Format with this Workbook (you can't use the Format with another Workbook).
Let's see how addFormat() is used:
addFormat usage
<?php
require_once 'Spreadsheet/Excel/Writer.php';
// Creating a workbook
$workbook = new Spreadsheet_Excel_Writer();
// Creating the format
$format_bold =& $workbook->addFormat();
$format_bold->setBold();
?>
There, we just created a bold format. Notice the ampersand sign (&) that appears when we created our format. If you don't create your format like that it will appear as if all the format's properties you set are ignored.
Well, we just created our first format, but we didn't use it. Not very smart. So let's do something useful with a format.
Let's say you want to make your regular data filled spreadsheet. Only this time, when you proudly present your beautiful creation to your boss, the thing you most dread happens:
Pointy haired boss - Mmmmhhh, seems OK.
You - Yes, I added those totals as you requested.
Pointy haired boss - Mmmmhhh, you know, there's going to be a lot of customers using this spreadsheet...
You - So...
Pointy haired boss - Mmmmhhh, what do you think of changing the style for those headers there?
You - ...
Of course it won't be just those headers: "why don't we center this title here?", "Could you merge those cells over there?", "what do you think of using the company's colors for those titles?".
There are a number of ways for dealing with this situation, but in this tutorial we will stick to the one which will keep your job.
So let's begin work on the spreadsheet for DotCom.com.
First example
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$format_bold =& $workbook->addFormat();
$format_bold->setBold();
// We need a worksheet in which to put our data
$worksheet =& $workbook->addWorksheet();
// This is our title
$worksheet->write(0, 0, "Profits for Dotcom.Com", $format_bold);
// And now the data
$worksheet->write(0, 0, 0);
?>
There. Now all of those VC's out there are going to be calling like crazy asking for an oportunity to invest on DotCom.com. Wait a minute. These are not regular VC's we are talking about. These are very selective guys who wouldn't trust their money to the first start-up they happen to see on the internet. I know! Let's put the company's colors in there!
Second example
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$format_bold =& $workbook->addFormat();
$format_bold->setBold();
$format_title =& $workbook->addFormat();
$format_title->setBold();
$format_title->setColor('yellow');
$format_title->setPattern(1);
$format_title->setFgColor('blue');
$worksheet =& $workbook->addWorksheet();
$worksheet->write(0, 0, "Quarterly Profits for Dotcom.Com", $format_title);
// While we are at it, why not throw some more numbers around
$worksheet->write(1, 0, "Quarter", $format_bold);
$worksheet->write(1, 1, "Profit", $format_bold);
$worksheet->write(2, 0, "Q1");
$worksheet->write(2, 1, 0);
$worksheet->write(3, 0, "Q2");
$worksheet->write(3, 1, 0);
$workbook->send('test.xls');
$workbook->close();
?>
If you just tested the previous example you might have noticed that the title would need several cells to be seen correctly, but the format we applied only works for the first cell. So our title does not look very nice.
What can we do to fix that? Well, you could tell your boss that the title looks ok to you, and that he really needs to visit an ophthalmologist. Or you could use cell merging in order to make the title spread over several cells.
For this you have to use the setAlign() method with 'merge' as argument, and create some empty cells so the title can 'use' them as a sort of background (there will be a better way to do this in a future version of Spreadsheet_Excel_Writer).
Applying merging to our example script, we would have this:
Merging cells
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$format_bold =& $workbook->addFormat();
$format_bold->setBold();
$format_title =& $workbook->addFormat();
$format_title->setBold();
$format_title->setColor('yellow');
$format_title->setPattern(1);
$format_title->setFgColor('blue');
// let's merge
$format_title->setAlign('merge');
$worksheet =& $workbook->addWorksheet();
$worksheet->write(0, 0, "Quarterly Profits for Dotcom.Com", $format_title);
// Couple of empty cells to make it look better
$worksheet->write(0, 1, "", $format_title);
$worksheet->write(0, 2, "", $format_title);
$worksheet->write(1, 0, "Quarter", $format_bold);
$worksheet->write(1, 1, "Profit", $format_bold);
$worksheet->write(2, 0, "Q1");
$worksheet->write(2, 1, 0);
$worksheet->write(3, 0, "Q2");
$worksheet->write(3, 1, 0);
$workbook->send('test.xls');
$workbook->close();
?>
mixed Workbook::close (
)
Calls finalization methods for the workbook. This method should always be the last one to be called on every workbook.
returns true on success, PEAR_Error on failure
This function can not be called statically.
object reference Workbook::&addWorksheet (
string $name=''
)
Add a new worksheet to the Excel workbook. If no name is given the name
of the worksheet will be Sheeti
with
i
in [1..].
string $name
-
the optional name of the worksheet.
Can not be longer than 31 characters.
returns a reference to a worksheet object on success, PEAR_Error on failure
This function can not be called statically.
Using &addWorksheet()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer('test.xls');
$worksheet =& $workbook->addWorksheet('My first worksheet');
if (PEAR::isError($worksheet)) {
die($worksheet->getMessage());
}
$workbook->close();
?>
object reference Workbook::&addFormat (
array $properties=array()
)
Add a new format to the Excel workbook. Also, pass any properties to the Format constructor. Valid properties are:
Align
Bold
Bottom
Top
Left
Right
Border
BorderColor
BottomColor
TopColor
RightColor
LeftColor
FgColor
BgColor
Color
Pattern
Underline
TextRotation
Size
NumFormat
Script
array $properties
-
array with properties for initializing the format.
object reference
- to an Excel Format
This function can not be called statically.
Using &addFormat()
<?php
require_once "Spreadsheet/Excel/Writer.php";
$workbook = new Spreadsheet_Excel_Writer();
$format =& $workbook->addFormat(array('Size' => 10,
'Align' => 'center',
'Color' => 'white',
'Pattern' => 1,
'FgColor' => 'magenta'));
$worksheet =& $workbook->addWorksheet();
$worksheet->writeString(1, 0, "Magenta Hello", $format);
$workbook->close();
?>
void Workbook::setCountry (
integer $code
)
This package is not documented yet.
$code
Is the international calling country code for the chosen country.
throws no exceptions thrown
This function can not be called statically.
boolean Workbook::&setTempDir (
string $dir
)
Sets the temp dir used for storing the OLE file. Use this method if you don't have the right to write in the default temporary dir.
string $dir
-
The dir to be used as temp dir
boolean
- TRUE if given dir is valid, FALSE otherwise
This function can not be called statically.
Using &setTempDir()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer('test.xls');
$worksheet =& $workbook->addWorksheet('My first worksheet');
$workbook->setTempDir('/home/xnoguer/temp');
$worksheet->write(0, 0, "did this work?");
$workbook->close();
?>
void Workbook::setVersion (
integer $version
)
This method exists just to access experimental functionality from BIFF8. It will be deprecated ! Only possible value is 8 (Excel 97/2000). For any other value it fails silently.
$version
The BIFF version
This function can not be called statically.
integer Workbook::setCustomColor (
integer $index
,
integer $red
,
integer $green
,
integer $blue
)
Change the RGB components of the elements in the colour palette. The new color, defined by the given RGB components, will "overwrite" the color previously defined for the given index.
integer $index
-
colour index
integer $red
-
red RGB value [0-255]
integer $green
-
green RGB value [0-255]
integer $blue
-
blue RGB value [0-255]
integer
- The palette index for the custom color
This function can not be called statically.
Using setCustomColor()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// "regular" green
$format_regular_green =& $workbook->addFormat();
$format_regular_green->setFgColor('green');
// "special" green
$format_special_green =& $workbook->addFormat();
$format_special_green->setFgColor(11);
// our green (overwriting color on index 12)
$workbook->setCustomColor(12, 10, 200, 10);
$format_our_green =& $workbook->addFormat();
$format_our_green->setFgColor(12);
$worksheet->setColumn(0, 0, 30);
$worksheet->write(0, 0, "Regular green", $format_regular_green);
$worksheet->write(1, 0, "Special green (index 11)", $format_special_green);
$worksheet->write(2, 0, "Our green", $format_our_green);
$workbook->send('greens.xls');
$workbook->close();
?>
array Workbook::worksheets (
)
Returns an array of the worksheet objects in a workbook
throws no exceptions thrown
This function can not be called statically.
string Worksheet::getName (
)
Retrieve the worksheet name. This is usefull when creating worksheets without a name.
string
- The worksheet's name
This function can not be called statically.
Using getName()
<?php
?>
void Worksheet::setInputEncoding (
string $encoding
)
It allows writing for different charsets by setting the worksheet's "current" charset. It has been tested for UTF-8, ISO-8859-7. It requires iconv for any charset other than UTF-16LE.
string $encoding
-
The encoding. It suports all encodings supported by php's iconv()
function.
This function can not be called statically.
Using setInputEncoding()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer('test.xls');
$workbook->setVersion(8);
$worksheet =& $workbook->addWorksheet('International worksheet');
$russian = "\xD0\xBF\xD0\xBE\xD0\xBA\xD0\xB0";
$greek = "\342\345\353\355\341";
$worksheet->setInputEncoding('ISO-8859-7');
$worksheet->write(0, 0, $greek);
$worksheet->setInputEncoding('utf-8');
$worksheet->write(1, 0, $russian);
$workbook->close();
?>
void Worksheet::select (
)
Set this worksheet as a selected worksheet, i.e. the worksheet has its tab highlighted.
This function can not be called statically.
Using select()
<?php
?>
void Worksheet::activate (
)
Set this worksheet as the active worksheet, i.e. the worksheet that is displayed when the workbook is opened. Also set it as selected.
This function can not be called statically.
Using activate()
<?php
?>
void Worksheet::setFirstSheet (
)
Set this worksheet as the first visible sheet. This is necessary when there are a large number of worksheets and the activated worksheet is not visible on the screen.
This function can not be called statically.
Using setFirstSheet()
<?php
?>
void Worksheet::protect (
string $password
)
Set the worksheet protection flag to prevent accidental modification and to hide formulas if the locked and hidden format properties have been set.
string $password
-
The password to use for protecting the sheet.
This function can not be called statically.
Using protect()
<?php
?>
void Worksheet::setColumn (
integer $firstcol
,
integer $lastcol
,
float $width
,
mixed $format=0
,
integer $hidden=0
)
Set the width of a single column or a range of columns.
integer $firstcol
-
first column on the range
integer $lastcol
-
last column on the range
float $width
-
width to set
mixed $format
-
The optional XF format to apply to the columns
integer $hidden
-
The optional hidden atribute
This function can not be called statically.
Using setColumn()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
$radius = 20;
$worksheet->setColumn(0,$radius*2,1);
// Face
for ($i = 0; $i < 360; $i++)
{
$worksheet->write(floor(sin((2*pi()*$i)/360)*$radius) + $radius + 1, floor(cos((2*pi()*$i)/360)*$radius) + $radius + 1, "x");
}
// Eyes (maybe use a format instead?)
$worksheet->writeURL(floor($radius*0.8), floor($radius*0.8), "0");
$worksheet->writeURL(floor($radius*0.8), floor($radius*1.2), "0");
// Smile
for ($i = 65; $i < 115; $i++)
{
$worksheet->write(floor(sin((2*pi()*$i)/360)*$radius*1.3) + floor($radius*0.2), floor(cos((2*pi()*$i)/360)*$radius*1.3) + $radius + 1, "x");
}
// hide gridlines so they don't mess with our Excel art.
$worksheet->hideGridLines();
$workbook->send('face.xls');
$workbook->close();
?>
mixed Worksheet::writeCol (
integer $row
, integer $col
, array $val
, mixed $format
= null
)
This package is not documented yet.
$row
The first row (uppermost row) we are writing to
$col
The col we are writing to
$val
The array of values to write
$format
The optional format to apply to the cell
returns PEAR_Error on failure
throws no exceptions thrown
This function can not be called statically.
mixed Worksheet::writeRow (
integer $row
, integer $col
, array $val
, mixed $format
= null
)
This package is not documented yet.
$row
The row we are writing to
$col
The first col (leftmost col) we are writing to
$val
The array of values to write
$format
The optional format to apply to the cell
returns PEAR_Error on failure
throws no exceptions thrown
This function can not be called statically.
void Worksheet::setSelection (
integer $first_row
,
integer $first_column
,
integer $last_row
,
integer $last_column
)
Set which cell or cells are selected in a worksheet
integer $first_row
-
first row in the selected quadrant
integer $first_column
-
first column in the selected quadrant
integer $last_row
-
last row in the selected quadrant
integer $last_column
-
last column in the selected quadrant
This function can not be called statically.
Using setSelection()
<?php
?>
void Worksheet::freezePanes (
array $panes
)
Set panes and mark them as frozen. One can use this method to mark certain regions in the worksheet so that they are "frozen" in the sense that when scrolling through the worksheet these regions are not affected by the scrolling and remain where they are on the screen. This is the same functionality as provided by Microsoft Excel through the -> menu command.
array $panes
-
This is the only parameter received and is composed of the following:
0 => Vertical split position,
1 => Horizontal split position
2 => Top row visible
3 => Leftmost column visible
4 => Active pane
This function can not be called statically.
Using freezePanes()
<?php
$worksheet =& $workbook->addWorksheet("Some Worksheet");
/* ... */
/* This freezes the first six rows of the worksheet: */
$worksheet->freezePanes(array(6, 0));
/* To freeze the first column, one must use the following syntax: */
$worksheet->freezePanes(array(0, 1));
?>
If one needs to further specify the scrolling region, the following syntax can be used:
<?php
/* Freeze the first six rows and start the scrollable region at row nine: */
$worksheet->freezePanes(array(6, 0, 9, 0));
?>
void Worksheet::thawPanes (
array $panes
)
Set panes and mark them as unfrozen.
array $panes
-
This is the only parameter received and is composed of the following:
0 => Vertical split position,
1 => Horizontal split position
2 => Top row visible
3 => Leftmost column visible
4 => Active pane
This function can not be called statically.
Using thawPanes()
<?php
?>
void Worksheet::hideScreenGridlines (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void Worksheet::setPortrait (
)
Set the page orientation as portrait.
This function can not be called statically.
Using setPortrait()
<?php
?>
void Worksheet::setLandscape (
)
Set the page orientation as landscape.
This function can not be called statically.
Using setLandscape()
<?php
?>
void Worksheet::setPaper (
integer $size=0
)
Set the paper type. Ex. 1 = US Letter, 9 = A4
integer $size
-
The type of paper size to use
This function can not be called statically.
Using setPaper()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
// Creating a workbook
$workbook = new Spreadsheet_Excel_Writer();
// sending HTTP headers
$workbook->send('test.xls');
// Creating a worksheet
$worksheet =& $workbook->addWorksheet('My first worksheet');
//set paper size
$worksheet->setPaper(9);
// The actual data
$worksheet->write(0, 0, 'Name');
$worksheet->write(0, 1, 'Age');
$worksheet->write(1, 0, 'John Smith');
$worksheet->write(1, 1, 30);
$worksheet->write(2, 0, 'Johann Schmidt');
$worksheet->write(2, 1, 31);
$worksheet->write(3, 0, 'Juan Herrera');
$worksheet->write(3, 1, 32);
// Let's send the file
$workbook->close();
?>
void Worksheet::setHeader (
string $string
,
float $margin=0.5
)
Set the page header caption and optional margin.
string $string
-
The header text
float $margin
-
optional head margin in inches.
This function can not be called statically.
Using setHeader()
<?php
?>
void Worksheet::setMerge (
integer $first_row
, integer $first_col
, integer $last_row
, integer $last_col
)
This package is not documented yet.
$first_row
First row of the area to merge
$first_col
First column of the area to merge
$last_row
Last row of the area to merge
$last_col
Last column of the area to merge
throws no exceptions thrown
This function can not be called statically.
Using setMerge()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet = $workbook->addWorksheet('SetMerge');
// Sets the color of a cell's content
$format = $workbook->addFormat();
$format->setFgColor(10);
$worksheet->write(0, 0, 'Cell Start', $format);
// Merge cells from row 0, col 0 to row 2, col 2
$worksheet->setMerge(0, 0, 2, 2);
$workbook->send('SetMerge.xls');
$workbook->close();
?>
void Worksheet::centerHorizontally (
integer $center=1
)
Center the page horizontally.
integer $center
-
the optional value for centering. Defaults to 1 (center).
This function can not be called statically.
Using centerHorizontally()
<?php
?>
void Worksheet::centerVertically (
integer $center=1
)
Center the page vertically.
integer $center
-
the optional value for centering. Defaults to 1 (center).
This function can not be called statically.
Using centerVertically()
<?php
?>
void Worksheet::setMargins (
float $margin
)
Set all the page margins to the same value in inches.
float $margin
-
The margin to set in inches
This function can not be called statically.
Using setMargins()
<?php
?>
void Worksheet::setMargins_LR (
float $margin
)
Set the left and right margins to the same value in inches.
float $margin
-
The margin to set in inches
This function can not be called statically.
Using setMargins_LR()
<?php
?>
void Worksheet::setMargins_TB (
float $margin
)
Set the top and bottom margins to the same value in inches.
float $margin
-
The margin to set in inches
This function can not be called statically.
Using setMargins_TB()
<?php
?>
void Worksheet::setMarginLeft (
float $margin=0.75
)
Set the left margin in inches.
float $margin
-
The margin to set in inches
This function can not be called statically.
Using setMarginLeft()
<?php
?>
void Worksheet::setMarginRight (
float $margin=0.75
)
Set the right margin in inches.
float $margin
-
The margin to set in inches
This function can not be called statically.
Using setMarginRight()
<?php
?>
void Worksheet::setMarginTop (
float $margin=1
)
Set the top margin in inches.
float $margin
-
The margin to set in inches
This function can not be called statically.
Using setMarginTop()
<?php
?>
void Worksheet::setMarginBottom (
float $margin=1
)
Set the bottom margin in inches.
float $margin
-
The margin to set in inches
This function can not be called statically.
Using setMarginBottom()
<?php
?>
void Worksheet::repeatRows (
integer $first_row
,
integer $last_row=NULL
)
Set the rows to repeat at the top of each printed page.
integer $first_row
-
First row to repeat
integer $last_row
-
Last row to repeat. Optional.
This function can not be called statically.
Using repeatRows()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer('test.xls');
$worksheet =& $workbook->addWorkSheet();
$worksheet->setColumn(0,0,100);
$worksheet->write(0,0,"99 Bottles Of Beer On The Wall- The Complete Lyrics");
// repeat only the first row
$worksheet->repeatRows(0);
for ($i = 99; $i > 0; $i--)
{
if ($i > 1) {
$next = $i - 1;
}
else {
$next = "no more";
}
$worksheet->write(100 - $i,0,"$i Bottles of beer on the wall, $i bottles of beer, ".
"take one down, pass it around, ".
"$next bottles of beer on the wall.");
}
$workbook->close();
?>
void Worksheet::repeatColumns (
integer $first_col
,
integer $last_col=NULL
)
Set the columns to repeat at the left hand side of each printed page.
integer $first_col
-
First column to repeat
integer $last_col
-
Last column to repeat. Optional.
This function can not be called statically.
Using repeatColumns()
<?php
?>
void Worksheet::printArea (
integer $first_row
,
integer $first_col
,
integer $last_row
,
integer $last_col
)
Set the area of each worksheet that will be printed.
integer $first_row
-
First row of the area to print
integer $first_col
-
First column of the area to print
integer $last_row
-
Last row of the area to print
integer $last_col
-
Last column of the area to print
This function can not be called statically.
Using printArea()
<?php
?>
void Worksheet::hideGridlines (
)
Set the option to hide gridlines on the printed page.
This function can not be called statically.
Using hideGridlines()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
$radius = 20;
$worksheet->setColumn(0,$radius*2,1);
// Face
for ($i = 0; $i < 360; $i++)
{
$worksheet->write(floor(sin((2*pi()*$i)/360)*$radius) + $radius + 1, floor(cos((2*pi()*$i)/360)*$radius) + $radius + 1, "x");
}
// Eyes (maybe use a format instead?)
$worksheet->writeURL(floor($radius*0.8), floor($radius*0.8), "0");
$worksheet->writeURL(floor($radius*0.8), floor($radius*1.2), "0");
// Smile
for ($i = 65; $i < 115; $i++)
{
$worksheet->write(floor(sin((2*pi()*$i)/360)*$radius*1.3) + floor($radius*0.2), floor(cos((2*pi()*$i)/360)*$radius*1.3) + $radius + 1, "x");
}
// hide gridlines so they don't mess with our Excel art.
$worksheet->hideGridLines();
$workbook->send('face.xls');
$workbook->close();
?>
void Worksheet::printRowColHeaders (
integer $print=1
)
Set the option to print the row and column headers on the printed page.
integer $print
-
Whether to print the headers or not. Defaults to 1 (print).
This function can not be called statically.
Using printRowColHeaders()
<?php
?>
void Worksheet::fitToPages (
integer $width
,
integer $height
)
Store the vertical and horizontal number of pages that will define the maximum area printed. It doesn't seem to work with OpenOffice.
integer $width
-
Maximun width of printed area in pages
integer $height
-
Maximun heigth of printed area in pages
This function can not be called statically.
Using fitToPages()
<?php
?>
void Worksheet::setHPagebreaks (
array $breaks
)
Store the horizontal page breaks on a worksheet (for printing). The breaks represent the row after which the break is inserted.
array $breaks
-
Array containing the horizontal page breaks
This function can not be called statically.
Using setHPagebreaks()
<?php
?>
void Worksheet::setVPagebreaks (
array $breaks
)
Store the vertical page breaks on a worksheet (for printing). The breaks represent the column after which the break is inserted.
array $breaks
-
Array containing the vertical page breaks
This function can not be called statically.
Using setVPagebreaks()
<?php
?>
void Worksheet::setZoom (
integer $scale=100
)
Set the worksheet zoom factor.
integer $scale
-
The zoom factor
This function can not be called statically.
Using setZoom()
<?php
?>
void Worksheet::setPrintScale (
integer $scale=100
)
Set the scale factor for the printed page. It turns off the "fit to page" option
integer $scale
-
The optional scale factor. Defaults to 100
This function can not be called statically.
Using setPrintScale()
<?php
?>
void Worksheet::write (
integer $row
,
integer $col
,
mixed $token
,
mixed $format=0
)
Map to the appropriate write method acording to the token recieved.
integer $row
-
The row of the cell we are writing to
integer $col
-
The column of the cell we are writing to
mixed $token
-
What we are writing
mixed $format
-
The optional format to apply to the cell
This function can not be called statically.
Using write()
<?php
?>
void Worksheet::writeNumber (
integer $row
,
integer $col
,
float $num
,
mixed $format=0
)
Write a double to the specified row and column (zero indexed). An integer can be written as a double. Excel will display an integer. $format is optional. Returns 0 : normal termination -2 : row or column out of range
integer $row
-
Zero indexed row
integer $col
-
Zero indexed column
float $num
-
The number to write
mixed $format
-
The optional XF format
This function can not be called statically.
Using writeNumber()
<?php
?>
void Worksheet::writeString (
integer $row
,
integer $col
,
string $str
,
mixed $format=0
)
Write a string to the specified row and column (zero indexed). NOTE: there is an Excel 5 defined limit of 255 characters. $format is optional. Returns 0 : normal termination -1 : insufficient number of arguments -2 : row or column out of range -3 : long string truncated to 255 chars
integer $row
-
Zero indexed row
integer $col
-
Zero indexed column
string $str
-
The string to write
mixed $format
-
The XF format for the cell
This function can not be called statically.
Using writeString()
<?php
?>
void Worksheet::writeNote (
integer $row
,
integer $col
,
string $note
)
Writes a note associated with the cell given by the row and column. NOTE records don't have a length limit.
integer $row
-
Zero indexed row
integer $col
-
Zero indexed column
string $note
-
The note to write
This function can not be called statically.
Using writeNote()
<?php
?>
void Worksheet::writeBlank (
integer $row
,
integer $col
,
mixed $format
)
Write a blank cell to the specified row and column (zero indexed). A blank cell is used to specify formatting without adding a string or a number. A blank cell without a format serves no purpose. Therefore, we don't write a BLANK record unless a format is specified. This is mainly an optimisation for the write_row() and write_col() methods. Returns 0 : normal termination (including no format) -1 : insufficient number of arguments -2 : row or column out of range
integer $row
-
Zero indexed row
integer $col
-
Zero indexed column
mixed $format
-
The XF format
This function can not be called statically.
Using writeBlank()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// we can set set all properties on instantiation
$upper_right_side_brick =& $workbook->addFormat(array('right' => 5, 'top' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
// or set all properties one by one
$upper_left_side_brick =& $workbook->addFormat();
$upper_left_side_brick->setLeft(5);
$upper_left_side_brick->setTop(5);
$upper_left_side_brick->setSize(15);
$upper_left_side_brick->setPattern(1);
$upper_left_side_brick->setBorderColor('blue');
$upper_left_side_brick->setFgColor('red');
$lower_right_side_brick =& $workbook->addFormat(array('right' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$lower_left_side_brick =& $workbook->addFormat(array('left' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$worksheet->setColumn(0, 20, 6);
// Sky
$sky =& $workbook->addFormat(array('fgcolor' => 'cyan', 'pattern' => 1, 'size' => 15));
for ($i = 0; $i <= 10; $i++)
{
for ($j = 0; $j < 20; $j++) {
$worksheet->writeBlank($i, $j, $sky);
}
}
// Cloud
$cloud =& $workbook->addFormat(array('fgcolor' => 'white', 'pattern' => 1, 'size' => 15));
$worksheet->writeBlank(5, 7, $cloud);
$worksheet->writeBlank(4, 8, $cloud);
$worksheet->writeBlank(5, 8, $cloud);
$worksheet->writeBlank(6, 8, $cloud);
$worksheet->writeBlank(4, 9, $cloud);
$worksheet->writeBlank(5, 9, $cloud);
$worksheet->writeBlank(5, 10, $cloud);
// Bricks
for ($j = 0; $j < 20; $j++)
{
for ($i = 5; $i <= 11; $i++)
{
if (($i + $j)%2 == 1) // right side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_right_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_right_side_brick);
}
else // left side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_left_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_left_side_brick);
}
}
}
// hide gridlines so they don't mess with our Excel art.
$worksheet->hideGridLines();
$workbook->send('bricks.xls');
$workbook->close();
?>
integer Worksheet::writeFormula (
integer $row
,
integer $col
,
string $formula
,
mixed $format=0
)
Write a formula to the specified row and column (zero indexed). In case of error it will write the error message (instead of the formula) in the corresponding row and column.
integer $row
-
Zero indexed row
integer $col
-
Zero indexed column
string $formula
-
The formula text string
mixed $format
-
The optional XF format
integer
- 0 for normal termination, -1 for an error in
the formula, -2 for row or column out of range.
This function can not be called statically.
Formulas must start with an equal sign ('=').
Arguments given to an Excel function should be separated by comas (','), not by semicolons (';').
Using writeFormula()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer('formula.xls');
$worksheet =& $workbook->addWorksheet();
$worksheet->write(0, 0, 2);
$worksheet->write(0, 1, "and");
$worksheet->write(0, 2, 2);
$worksheet->write(0, 3, "makes");
$worksheet->writeFormula(0, 4, "=SUM(A1,C1)");
$workbook->close();
?>
void Worksheet::writeUrl (
integer $row
,
integer $col
,
string $url
,
string $string=''
,
mixed $format=0
)
Write a hyperlink. This is comprised of two elements: the visible label and the invisible link. The visible label is the same as the link unless an alternative string is specified. The label is written using the writeString() method. Therefore the 255 characters string limit applies. $string and $format are optional and their order is interchangeable. The hyperlink can be to a http, ftp, mail, internal sheet, or external directory url. Returns 0 : normal termination -1 : insufficient number of arguments -2 : row or column out of range -3 : long string truncated to 255 chars
integer $row
-
Row
integer $col
-
Column
string $url
-
URL string
string $string
-
Alternative label
mixed $format
-
The cell format
This function can not be called statically.
Using writeUrl()
<?php
?>
void Worksheet::setRow (
integer $row
,
integer $height
,
mixed $format=0
)
This method is used to set the height and XF format for a row. Writes the BIFF record ROW.
integer $row
-
The row to set
integer $height
-
Height we are giving to the row.
Use NULL to set XF without setting height
mixed $format
-
XF format we are giving to the row
This function can not be called statically.
Using setRow()
<?php
?>
void Worksheet::mergeCells (
integer $first_row
,
integer $first_col
,
integer $last_row
,
integer $last_col
)
This is an Excel97/2000 method. It is required to perform more complicated merging than the normal setAlign('merge'). It merges the area given by its arguments.
integer $first_row
-
First row of the area to merge
integer $first_col
-
First column of the area to merge
integer $last_row
-
Last row of the area to merge
integer $last_col
-
Last column of the area to merge
This function can not be called statically.
Using mergeCells()
<?php
?>
void Worksheet::insertBitmap (
integer $row
,
integer $col
,
string $bitmap
,
integer $x=0
,
integer $y=0
,
integer $scale_x=1
,
integer $scale_y=1
)
Insert a 24bit bitmap image in a worksheet. The main record required is IMDATA but it must be proceeded by a OBJ record to define its position.
integer $row
-
The row we are going to insert the bitmap into
integer $col
-
The column we are going to insert the bitmap into
string $bitmap
-
The bitmap filename
integer $x
-
The horizontal position (offset) of the image inside the cell.
integer $y
-
The vertical position (offset) of the image inside the cell.
integer $scale_x
-
The horizontal scale
integer $scale_y
-
The vertical scale
This function can not be called statically.
Using insertBitmap()
<?php
?>
void Worksheet::setOutline (
bool $visible=true
,
bool $symbols_below=true
,
bool $symbols_right=true
,
bool $auto_style=false
)
This method sets the properties for outlining and grouping. The defaults correspond to Excel's defaults.
bool $visible
-
bool $symbols_below
-
bool $symbols_right
-
bool $auto_style
-
This function can not be called statically.
Using setOutline()
<?php
?>
object The Spreadsheet_Excel_Writer::Spreadsheet_Excel_Writer (
string $filename=''
)
The constructor. It just creates a Workbook
string $filename
-
The optional filename for the Workbook.
object The
- Workbook created
This function can not be called statically.
Using Spreadsheet_Excel_Writer()
<?php
?>
void Spreadsheet_Excel_Writer::send (
string $filename
)
Send HTTP headers with the correct content-type (application/vnd.ms-excel), cache control and filename for the file.
string $filename
-
The filename to use for HTTP headers
This function can be called statically, but it is easiest when called from the workbook object.
Using send()
<?php
$workbook->send('filename.xls');
?>
string Spreadsheet_Excel_Writer::rowcolToCell (
integer $row
,
integer $col
)
Utility function for writing formulas. Converts a cell's coordinates to the A1 format.
integer $row
-
Row for the cell to convert (0-indexed).
integer $col
-
Column for the cell to convert (0-indexed).
returns The cell identifier in A1 format on success, PEAR_Error on failure
Using rowcolToCell()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer('rowcol.xls');
$worksheet1 =& $workbook->addWorksheet("rowcol");
$first = 1;
$last = 10;
for ($i = $first; $i <= $last; $i++) {
$worksheet1->write($i, 1, $i);
}
$cell1 = Spreadsheet_Excel_Writer::rowcolToCell($first, 1);
$cell2 = Spreadsheet_Excel_Writer::rowcolToCell($last, 1);
$worksheet1->write($last + 1, 0, "Total =");
$worksheet1->writeFormula($last + 1, 1, "=SUM($cell1:$cell2)");
$workbook->close();
?>
void Format::setAlign (
string $location
)
Set cell alignment.
string
$location
-
alignment for the cell
Horizontal Alignments (pick one):
left
, center
,
right
, fill
,
justify
, merge
,
equal_space
.
Vertical Alignments (pick one):
top
, vcenter
,
bottom
, vjustify
,
vequal_space
.
To implement a combination of Horizontal and Vertical Alignments, call this method two times.
This function can not be called statically.
Using setAlign()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// put text at the top
$format_top =& $workbook->addFormat();
$format_top->setAlign('top');
$format_top->setTextWrap(1);
// center the text horizontally
$format_center =& $workbook->addFormat();
$format_center->setAlign('center');
// put text at the top and center it horizontally
$format_top_center =& $workbook->addFormat();
$format_top_center->setAlign('top');
$format_top_center->setAlign('center');
$worksheet->write(0, 0, 'On top of the world!',
$format_top);
$worksheet->write(1, 0, 'c', $format_center);
$worksheet->write(2, 0, 'tc', $format_top_center);
$workbook->send('align.xls');
$workbook->close();
?>
void Format::setVAlign (
string $location
)
Set cell alignment. This is an alternative to setAlign()
string
$location
-
alignment for the cell
top
, vcenter
,
bottom
, vjustify
,
vequal_space
.
This function can not be called statically.
Using setVAlign()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// justify
$format_justify =& $workbook->addFormat('vAlign' => 'vjustify');
// center the text vertically
$format_center =& $workbook->addFormat('vAlign' => 'vcenter');
// justify and center vertically
$format_justify_center =& $workbook->addFormat();
$format_justify_center->setVAlign('vjustify');
$format_justify_center->setVAlign('vcenter');
$worksheet->write(0, 0, 'justify', $format_justify);
$worksheet->write(1, 0, 'center', $format_center);
$worksheet->write(2, 0, 'jc', $format_justify_center);
$workbook->send('align.xls');
$workbook->close();
?>
void Format::setHAlign (
string $location
)
Set cell alignment. This is an alternative to setAlign()
string
$location
-
alignment for the cell
left
, center
,
right
, fill
,
justify
, merge
,
equal_space
.
This function can not be called statically.
Using setHAlign()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// put text at the top
$format_top =& $workbook->addFormat();
$format_top->setHAlign('top');
$format_top->setTextWrap(1);
// center the text horizontally
$format_center =& $workbook->addFormat();
$format_center->setHAlign('center');
// put text at the top and center it horizontally
$format_top_center =& $workbook->addFormat();
$format_top_center->setHAlign('top');
$format_top_center->setHAlign('center');
$worksheet->write(0, 0, 'On top of the world!',
$format_top);
$worksheet->write(1, 0, 'c', $format_center);
$worksheet->write(2, 0, 'tc', $format_top_center);
$workbook->send('align.xls');
$workbook->close();
?>
void Format::setMerge (
)
This is an alias for the unintuitive setAlign('merge')
This function can not be called statically.
void Format::setLocked (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void Format::setUnLocked (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void Format::setBold (
integer $weight=1
)
Sets the boldness of the text. Bold has a range 100..1000. 0 (400) is normal. 1 (700) is bold.
integer $weight
-
Weight for the text, 0 maps to 400 (normal text)
1 maps to 700 (bold text). Valid range is: 100-1000
It's Optional, default is 1 (bold).
This function can not be called statically.
Using setBold()
<?php
?>
void Format::setBottom (
integer $style
)
Sets the width for the bottom border of the cell
integer $style
-
style of the cell border. 1 => thin, 2 => thick.
This function can not be called statically.
Using setBottom()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// we can set set all properties on instantiation
$upper_right_side_brick =& $workbook->addFormat(array('right' => 5, 'top' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
// or set all properties one by one
$upper_left_side_brick =& $workbook->addFormat();
$upper_left_side_brick->setLeft(5);
$upper_left_side_brick->setTop(5);
$upper_left_side_brick->setBottom(1);
$upper_left_side_brick->setSize(15);
$upper_left_side_brick->setPattern(1);
$upper_left_side_brick->setBorderColor('blue');
$upper_left_side_brick->setFgColor('red');
$lower_right_side_brick =& $workbook->addFormat(array('right' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$lower_left_side_brick =& $workbook->addFormat(array('left' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$worksheet->setColumn(0, 20, 6);
// Sky
$sky =& $workbook->addFormat(array('fgcolor' => 'cyan', 'pattern' => 1, 'size' => 15));
for ($i = 0; $i <= 10; $i++)
{
for ($j = 0; $j < 20; $j++) {
$worksheet->writeBlank($i, $j, $sky);
}
}
// Cloud
$cloud =& $workbook->addFormat(array('fgcolor' => 'white', 'pattern' => 1, 'size' => 15));
$worksheet->writeBlank(5, 7, $cloud);
$worksheet->writeBlank(4, 8, $cloud);
$worksheet->writeBlank(5, 8, $cloud);
$worksheet->writeBlank(6, 8, $cloud);
$worksheet->writeBlank(4, 9, $cloud);
$worksheet->writeBlank(5, 9, $cloud);
$worksheet->writeBlank(5, 10, $cloud);
// Bricks
for ($j = 0; $j < 20; $j++)
{
for ($i = 5; $i <= 11; $i++)
{
if (($i + $j)%2 == 1) // right side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_right_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_right_side_brick);
}
else // left side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_left_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_left_side_brick);
}
}
}
// hide gridlines so they don't mess with our Excel art.
$worksheet->hideGridLines();
$workbook->send('bricks.xls');
$workbook->close();
?>
void Format::setTop (
integer $style
)
Sets the width for the top border of the cell
integer $style
-
style of the cell top border. 1 => thin, 2 => thick.
This function can not be called statically.
Using setTop()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// we can set set all properties on instantiation
$upper_right_side_brick =& $workbook->addFormat(array('right' => 5, 'top' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
// or set all properties one by one
$upper_left_side_brick =& $workbook->addFormat();
$upper_left_side_brick->setLeft(5);
$upper_left_side_brick->setTop(5);
$upper_left_side_brick->setSize(15);
$upper_left_side_brick->setPattern(1);
$upper_left_side_brick->setBorderColor('blue');
$upper_left_side_brick->setFgColor('red');
$lower_right_side_brick =& $workbook->addFormat(array('right' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$lower_left_side_brick =& $workbook->addFormat(array('left' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$worksheet->setColumn(0, 20, 6);
// Sky
$sky =& $workbook->addFormat(array('fgcolor' => 'cyan', 'pattern' => 1, 'size' => 15));
for ($i = 0; $i <= 10; $i++)
{
for ($j = 0; $j < 20; $j++) {
$worksheet->writeBlank($i, $j, $sky);
}
}
// Cloud
$cloud =& $workbook->addFormat(array('fgcolor' => 'white', 'pattern' => 1, 'size' => 15));
$worksheet->writeBlank(5, 7, $cloud);
$worksheet->writeBlank(4, 8, $cloud);
$worksheet->writeBlank(5, 8, $cloud);
$worksheet->writeBlank(6, 8, $cloud);
$worksheet->writeBlank(4, 9, $cloud);
$worksheet->writeBlank(5, 9, $cloud);
$worksheet->writeBlank(5, 10, $cloud);
// Bricks
for ($j = 0; $j < 20; $j++)
{
for ($i = 5; $i <= 11; $i++)
{
if (($i + $j)%2 == 1) // right side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_right_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_right_side_brick);
}
else // left side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_left_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_left_side_brick);
}
}
}
// hide gridlines so they don't mess with our Excel art.
$worksheet->hideGridLines();
$workbook->send('bricks.xls');
$workbook->close();
?>
void Format::setLeft (
integer $style
)
Sets the width for the left border of the cell
integer $style
-
style of the cell left border. 1 => thin, 2 => thick.
This function can not be called statically.
Using setLeft()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// we can set set all properties on instantiation
$upper_right_side_brick =& $workbook->addFormat(array('right' => 5, 'top' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
// or set all properties one by one
$upper_left_side_brick =& $workbook->addFormat();
$upper_left_side_brick->setLeft(5);
$upper_left_side_brick->setTop(5);
$upper_left_side_brick->setSize(15);
$upper_left_side_brick->setPattern(1);
$upper_left_side_brick->setBorderColor('blue');
$upper_left_side_brick->setFgColor('red');
$lower_right_side_brick =& $workbook->addFormat(array('right' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$lower_left_side_brick =& $workbook->addFormat(array('left' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$worksheet->setColumn(0, 20, 6);
// Sky
$sky =& $workbook->addFormat(array('fgcolor' => 'cyan', 'pattern' => 1, 'size' => 15));
for ($i = 0; $i <= 10; $i++)
{
for ($j = 0; $j < 20; $j++) {
$worksheet->writeBlank($i, $j, $sky);
}
}
// Cloud
$cloud =& $workbook->addFormat(array('fgcolor' => 'white', 'pattern' => 1, 'size' => 15));
$worksheet->writeBlank(5, 7, $cloud);
$worksheet->writeBlank(4, 8, $cloud);
$worksheet->writeBlank(5, 8, $cloud);
$worksheet->writeBlank(6, 8, $cloud);
$worksheet->writeBlank(4, 9, $cloud);
$worksheet->writeBlank(5, 9, $cloud);
$worksheet->writeBlank(5, 10, $cloud);
// Bricks
for ($j = 0; $j < 20; $j++)
{
for ($i = 5; $i <= 11; $i++)
{
if (($i + $j)%2 == 1) // right side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_right_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_right_side_brick);
}
else // left side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_left_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_left_side_brick);
}
}
}
// hide gridlines so they don't mess with our Excel art.
$worksheet->hideGridLines();
$workbook->send('bricks.xls');
$workbook->close();
?>
void Format::setRight (
integer $style
)
Sets the width for the right border of the cell
integer $style
-
style of the cell right border. 1 => thin, 2 => thick.
This function can not be called statically.
Using setRight()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// we can set set all properties on instantiation
$upper_right_side_brick =& $workbook->addFormat(array('right' => 5, 'top' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
// or set all properties one by one
$upper_left_side_brick =& $workbook->addFormat();
$upper_left_side_brick->setLeft(5);
$upper_left_side_brick->setTop(5);
$upper_left_side_brick->setSize(15);
$upper_left_side_brick->setPattern(1);
$upper_left_side_brick->setBorderColor('blue');
$upper_left_side_brick->setFgColor('red');
$lower_right_side_brick =& $workbook->addFormat(array('right' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$lower_left_side_brick =& $workbook->addFormat(array('left' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$worksheet->setColumn(0, 20, 6);
// Sky
$sky =& $workbook->addFormat(array('fgcolor' => 'cyan', 'pattern' => 1, 'size' => 15));
for ($i = 0; $i <= 10; $i++)
{
for ($j = 0; $j < 20; $j++) {
$worksheet->writeBlank($i, $j, $sky);
}
}
// Cloud
$cloud =& $workbook->addFormat(array('fgcolor' => 'white', 'pattern' => 1, 'size' => 15));
$worksheet->writeBlank(5, 7, $cloud);
$worksheet->writeBlank(4, 8, $cloud);
$worksheet->writeBlank(5, 8, $cloud);
$worksheet->writeBlank(6, 8, $cloud);
$worksheet->writeBlank(4, 9, $cloud);
$worksheet->writeBlank(5, 9, $cloud);
$worksheet->writeBlank(5, 10, $cloud);
// Bricks
for ($j = 0; $j < 20; $j++)
{
for ($i = 5; $i <= 11; $i++)
{
if (($i + $j)%2 == 1) // right side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_right_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_right_side_brick);
}
else // left side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_left_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_left_side_brick);
}
}
}
// hide gridlines so they don't mess with our Excel art.
$worksheet->hideGridLines();
$workbook->send('bricks.xls');
$workbook->close();
?>
void Format::setBorder (
integer $style
)
Set cells borders to the same style
integer $style
-
style to apply for all cell borders. 1 => thin, 2 => thick.
This function can not be called statically.
Using setBorder()
<?php
?>
void Format::setBorderColor (
mixed $color
)
Sets all the cell's borders to the same color
mixed $color
-
The color we are setting. Either a string (like 'blue'),
or an integer (range is [8...63]).
Please see the "Using colors" section of the manual for more information.
This function can not be called statically.
Using setBorderColor()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// we can set set all properties on instantiation
$upper_right_side_brick =& $workbook->addFormat(array('right' => 5, 'top' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
// or set all properties one by one
$upper_left_side_brick =& $workbook->addFormat();
$upper_left_side_brick->setLeft(5);
$upper_left_side_brick->setTop(5);
$upper_left_side_brick->setSize(15);
$upper_left_side_brick->setPattern(1);
$upper_left_side_brick->setBorderColor('blue');
$upper_left_side_brick->setFgColor('red');
$lower_right_side_brick =& $workbook->addFormat(array('right' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$lower_left_side_brick =& $workbook->addFormat(array('left' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$worksheet->setColumn(0, 20, 6);
// Sky
$sky =& $workbook->addFormat(array('fgcolor' => 'cyan', 'pattern' => 1, 'size' => 15));
for ($i = 0; $i <= 10; $i++)
{
for ($j = 0; $j < 20; $j++) {
$worksheet->writeBlank($i, $j, $sky);
}
}
// Cloud
$cloud =& $workbook->addFormat(array('fgcolor' => 'white', 'pattern' => 1, 'size' => 15));
$worksheet->writeBlank(5, 7, $cloud);
$worksheet->writeBlank(4, 8, $cloud);
$worksheet->writeBlank(5, 8, $cloud);
$worksheet->writeBlank(6, 8, $cloud);
$worksheet->writeBlank(4, 9, $cloud);
$worksheet->writeBlank(5, 9, $cloud);
$worksheet->writeBlank(5, 10, $cloud);
// Bricks
for ($j = 0; $j < 20; $j++)
{
for ($i = 5; $i <= 11; $i++)
{
if (($i + $j)%2 == 1) // right side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_right_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_right_side_brick);
}
else // left side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_left_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_left_side_brick);
}
}
}
// hide gridlines so they don't mess with our Excel art.
$worksheet->hideGridLines();
$workbook->send('bricks.xls');
$workbook->close();
?>
void Format::setBottomColor (
mixed $color
)
Sets the cell's bottom border color
mixed $color
-
either a string (like 'blue'), or an integer (range is [8...63]).
Please see the "Using colors" section of the manual for more information.
This function can not be called statically.
Using setBottomColor()
<?php
?>
void Format::setTopColor (
mixed $color
)
Sets the cell's top border color
mixed $color
-
either a string (like 'blue'), or an integer (range is [8...63]).
Please see the "Using colors" section of the manual for more information.
This function can not be called statically.
Using setTopColor()
<?php
?>
void Format::setLeftColor (
mixed $color
)
Sets the cell's left border color
mixed $color
-
either a string (like 'blue'), or an integer (range is [8...63]).
Please see the "Using colors" section of the manual for more information.
This function can not be called statically.
Using setLeftColor()
<?php
?>
void Format::setRightColor (
mixed $color
)
Sets the cell's right border color
mixed $color
-
either a string (like 'blue'), or an integer (range is [8...63]).
Please see the "Using colors" section of the manual for more information.
This function can not be called statically.
Using setRightColor()
<?php
?>
void Format::setFgColor (
mixed $color
)
Sets the cell's "foreground color".
The term "foreground color" is misleading. Here, "foreground" means the top layer of a cell's background. To set the color of a cell's contents, use the setColor() method.
The color actually seen may depend on the pattern and background color being used.
The example entitled "How background and foreground colors interact with patterns" is very helpful.
mixed $color
-
either a string (like 'blue'), or an integer (range is [8...63]).
See the "Using colors" section, below, for more information.
The following colors can be defined by name:
black
, white
,
red
, green
,
blue
, yellow
,
magenta
and cyan
.
To learn what the other indexed colors look like, read Color Palette and the 56 Excel ColorIndex Colors. Beware that the color indexes listed there are displaced by 1 with respect to those used by Spreadsheet_Excel_Writer.
If the predifined colors don't meet your requirements, use the setCustomColor() method.
This function can not be called statically.
Using setFgColor()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// "regular" green
$format_regular_green =& $workbook->addFormat();
$format_regular_green->setFgColor('green');
// "special" green
$format_special_green =& $workbook->addFormat();
$format_special_green->setFgColor(11);
// our green (overwriting color on index 12)
$workbook->setCustomColor(12, 10, 200, 10);
$format_our_green =& $workbook->addFormat();
$format_our_green->setFgColor(12);
$worksheet->setColumn(0, 0, 30);
$worksheet->write(0, 0, "Regular green", $format_regular_green);
$worksheet->write(1, 0, "Special green (index 11)", $format_special_green);
$worksheet->write(2, 0, "Our green", $format_our_green);
$workbook->send('setFgColor.xls');
$workbook->close();
?>
void Format::setBgColor (
mixed $color
)
Sets the cell's "background color".
The term "background color" is misleading. Here, "background" means the bottom layer of a cell's background.
This method only comes into play when creating patterns via the setPattern() method. In general, chances are you are more interested in using the setFgColor() method.
The example entitled "How background and foreground colors interact with patterns" is very helpful.
mixed $color
-
either a string (like 'blue'), or an integer (range is [8...63]).
Please see the "Using colors" section of the manual for more information.
This function can not be called statically.
Using setBgColor()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet('testing bg color');
$worksheet->setRow(1, 30);
$format6 =& $workbook->addFormat();
$format6->setBgColor('green');
$format6->setPattern(6);
$worksheet->write(1, 1, 'the bg', $format6);
$workbook->send('setBgColor.xls');
$workbook->close();
?>
void Format::setColor (
mixed $color
)
Sets the color of a cell's content
mixed $color
-
either a string (like 'blue'), or an integer (range is [8...63]).
Please see the "Using colors" section of the manual for more information.
This function can not be called statically.
Using setColor()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet = &$workbook->addWorksheet('SetColor');
for ($inc =0; $inc<64; $inc++) {
// Sets the color of a cell's content
$format = $workbook->addFormat();
$format->setColor($inc);
$worksheet->write($inc, 0, 'Color (index '.$inc.')', $format);
}
$workbook->send('setColor.xls');
$workbook->close();
?>
void Format::setPattern (
integer $arg=1
)
Sets the fill pattern attribute of a cell. Use in combination with the setBgColor() and setFgColor() methods.
The example entitled "How background and foreground colors interact with patterns" is very helpful.
integer $arg
-
Optional. Defaults to 1. Meaningful values are: 0-18.
0 = no background.
1 = solid "background" using the color set by
setFgColor().
This function can not be called statically.
How background and foreground colors interact with patterns
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet('testing colors and patterns');
$worksheet->setRow(1, 30);
$worksheet->setRow(2, 30);
$worksheet->setRow(3, 30);
// valid patterns are 0 to 18
for ($i = 0; $i <= 18; $i++)
{
// green in different patterns
$another_format1 =& $workbook->addFormat();
$another_format1->setBgColor('green');
$another_format1->setPattern($i);
$worksheet->write(1, $i, "pattern $i", $another_format1);
// red in different patterns
$another_format2 =& $workbook->addFormat();
$another_format2->setFgColor('red');
$another_format2->setPattern($i);
$worksheet->write(2, $i, "pattern $i", $another_format2);
// mixed red and green according to pattern
$another_format3 =& $workbook->addFormat();
$another_format3->setBgColor('green');
$another_format3->setFgColor('red');
$another_format3->setPattern($i);
$worksheet->write(3, $i, "pattern $i", $another_format3);
}
$workbook->send('setPattern.xls');
$workbook->close();
?>
void Format::setUnderline (
integer $underline
)
Sets the underline of the text
integer $underline
-
The value for underline. Possible values are
1 => underline, 2 => double underline.
This function can not be called statically.
Using setUnderline()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$format_underline =& $workbook->addFormat();
$format_underline->setUnderline(1);
$worksheet =& $workbook->addWorksheet();
$worksheet->write(0, 0, "This is underlined", $format_underline);
$format_underline->setUnderline(2);
$worksheet->write(1, 0, "This is twice underlined", $format_underline);
?>
void Format::setItalic (
)
Sets the font style as italic
This function can not be called statically.
Using setItalic()
<?php
?>
void Format::setSize (
integer $size
)
Sets the font size
integer $size
-
The font size (in pixels I think).
This function can not be called statically.
Using setSize()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// we can set set all properties on instantiation
$upper_right_side_brick =& $workbook->addFormat(array('right' => 5, 'top' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
// or set all properties one by one
$upper_left_side_brick =& $workbook->addFormat();
$upper_left_side_brick->setLeft(5);
$upper_left_side_brick->setTop(5);
$upper_left_side_brick->setSize(15);
$upper_left_side_brick->setPattern(1);
$upper_left_side_brick->setBorderColor('blue');
$upper_left_side_brick->setFgColor('red');
$lower_right_side_brick =& $workbook->addFormat(array('right' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$lower_left_side_brick =& $workbook->addFormat(array('left' => 5, 'bottom' => 5, 'size' => 15,
'pattern' => 1, 'bordercolor' => 'blue',
'fgcolor' => 'red'));
$worksheet->setColumn(0, 20, 6);
// Sky
$sky =& $workbook->addFormat(array('fgcolor' => 'cyan', 'pattern' => 1, 'size' => 15));
for ($i = 0; $i <= 10; $i++)
{
for ($j = 0; $j < 20; $j++) {
$worksheet->writeBlank($i, $j, $sky);
}
}
// Cloud
$cloud =& $workbook->addFormat(array('fgcolor' => 'white', 'pattern' => 1, 'size' => 15));
$worksheet->writeBlank(5, 7, $cloud);
$worksheet->writeBlank(4, 8, $cloud);
$worksheet->writeBlank(5, 8, $cloud);
$worksheet->writeBlank(6, 8, $cloud);
$worksheet->writeBlank(4, 9, $cloud);
$worksheet->writeBlank(5, 9, $cloud);
$worksheet->writeBlank(5, 10, $cloud);
// Bricks
for ($j = 0; $j < 20; $j++)
{
for ($i = 5; $i <= 11; $i++)
{
if (($i + $j)%2 == 1) // right side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_right_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_right_side_brick);
}
else // left side of brick
{
$worksheet->writeBlank(2*$i, $j, $upper_left_side_brick);
$worksheet->writeBlank(2*$i + 1, $j, $lower_left_side_brick);
}
}
}
// hide gridlines so they don't mess with our Excel art.
$worksheet->hideGridLines();
$workbook->send('bricks.xls');
$workbook->close();
?>
void Format::setTextWrap (
)
Sets text wrapping
This function can not be called statically.
Using setTextWrap()
<?php
?>
void Format::setTextRotation (
integer $angle
)
Sets the orientation of the text
integer $angle
-
The rotation angle for the text (clockwise). Possible
values are: 0, 90, 270 and -1 for stacking top-to-bottom.
This function can not be called statically.
Using setTextRotation()
<?php
?>
void Format::setNumFormat (
string $num_format
)
Sets the numeric format.
It can be date, time, currency, etc...
The following table lists possible values for $num_format
and the corresponding types that a numeric format expects as arguments.
0 | Decimal | The amount of zeros specifies the amount of digits that will be shown |
0.00 | Decimal | The amount of zeros after the decimal dot specifies the amount of decimal digits that will be shown |
#.## | Decimal | The amount of sharp signs after the decimal dot specifies the maximum amount of decimal digits that will be shown |
0% | Percent | The amount of zeros specifies the amount of digits that will be shown. |
0.000% | Percent | The amount of zeros after the decimal dot specifies the amount of decimal digits that will be shown. |
$#.#;[Red]($#.#) | Currency | Zeros and sharp signs have the same meaning as in other formats. |
??/?? | Fraction | The amount of question signs in the denominator determines its precision (maximum amount of digits in the denominator). |
# ??/?? | Fraction | A fraction with an integer part. Zeros and sharp signs are used for defining the integer part, and they have the same meaning as in other formats. |
0.00E+# | Scientific | In scientific notation base and exponent are formated according to the same rules applied to decimals. For scientific notation zeros and sharp signs appear to be equivalent. |
D-MMM-YY | Date | A date represented in the given notation. Month can be a one or two digits month, or a three letter month. Year can have 2 or 4 digits. The argument to be formated as a date is considered to be the number of days since December 30 1899 (Excel's day zero). For dates preceding day zero, negative numbers can be used. |
D/M/YYYY h:mm:ss | Date/Time | A date represented in the given notation. The argument to be formated as a date is considered to be the number of days since Excel's day zero. |
h:mm:ss AM/PM | Time | A time represented in the given notation. Be careful, the argument to be formated as a time has to be given in days. For example an argument of 0.5 would be presented as '12:00:00 PM'. |
The information here presented comes from OpenOffice.org's Documentation of the Microsoft Excel File Format (http://sc.openoffice.org/excelfileformat.pdf).
string $num_format
-
The numeric format.
This function can not be called statically.
Using setNumFormat()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
// We'll show dates with a three letter month and four digit year format
$date_format =& $workbook->addFormat();
$date_format->setNumFormat('D-MMM-YYYY');
// number of seconds in a day
$seconds_in_a_day = 86400;
// Unix timestamp to Excel date difference in seconds
$ut_to_ed_diff = $seconds_in_a_day * 25569;
// show Excel's day zero date
$worksheet->write(0, 0, "Excel's day zero");
$worksheet->write(0, 1, 0, $date_format);
// show today's date
$now = time();
$worksheet->write(1, 0, "Today's date:");
$worksheet->write(1, 1, ($now + $ut_to_ed_diff) / $seconds_in_a_day, $date_format);
$workbook->send('num_formatting.xls');
$workbook->close();
?>
void Format::setStrikeOut (
)
Sets font as strikeout.
This function can not be called statically.
Using setStrikeOut()
<?php
?>
void Format::setOutLine (
)
Sets outlining for a font.
This function can not be called statically.
Using setOutLine()
<?php
?>
void Format::setShadow (
)
Sets font as shadow.
This function can not be called statically.
Using setShadow()
<?php
?>
void Format::setScript (
integer $script
)
Sets the script type of the text
integer $script
-
The value for script type. Possible values are
1 => superscript, 2 => subscript.
This function can not be called statically.
Using setScript()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
// creating the formats
$format_superscript =& $workbook->addFormat();
$format_superscript->setScript(1);
$format_subscript =& $workbook->addFormat();
$format_subscript->setScript(2);
$worksheet =& $workbook->addWorksheet();
$worksheet->write(0, 0, "This is superscript", $format_superscript);
$worksheet->write(1, 0, "This is subscript", $format_subscript);
?>
void Format::setFontFamily (
string $font_family
)
Sets the font family.
string $font_family
-
The font family name. Possible values are: 'Times New Roman', 'Arial', 'Courier'.
This function can not be called statically.
Using setFontFamily()
<?php
require_once 'Spreadsheet/Excel/Writer.php';
$workbook = new Spreadsheet_Excel_Writer();
$worksheet =& $workbook->addWorksheet();
$format_times =& $workbook->addFormat();
$format_times->setFontFamily('Times New Roman');
$format_courier =& $workbook->addFormat();
$format_courier->setFontFamily('Courier');
$worksheet->write(0, 0, "Hello World, Arial (default)");
$worksheet->write(1, 0, "Hello World, Times New Roman", $format_times);
$worksheet->write(2, 0, "Hello World, Courier", $format_courier);
$workbook->send('fonts.xls');
$workbook->close();
?>
Provides Packages for working with files and file system
Archive_Tar provides an API for handling (compressed) Tar archives.
void Archive_Tar (
string $tarname
, mixed
$compress
= null
)
The constructor declares a new Archive_Tar object, identifying it by the name of the tar file.
string $tarname
-
the name of the tar archive to work with
mixed $compress
-
if TRUE, indicates that the archive is compressed (using gzip).
if NULL the archive is not compressed, if 'gz' or 'bz2', indicates
that the archive is compressed with gzip or bz2. For compatibility
reason the boolean value TRUE means 'gz'.
boolean add (
mixed $filelist
)
This method adds files and directories to an existing archive. If the archive does not exist, it attempts to create it. The files and directories listed are added at the end of the archive, even if a file with the same name is already archived.
mixed $filelist
- an array of filenames
and directory names, or a single string with names separated
by a single blank space.
For each directory added in the archive, the files and
sub-directories are also added.
boolean
- Returns TRUE on success, FALSE on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "Invalid file list" | The argument for the function is not correct formatted or build. | Check for typing mistakes in the argument |
This function can not be called statically.
Add files to a compressed archive
<?php
$tar_object = new Archive_Tar("tarname.tar.gz", true);
$v_list[0]="dev/file.txt";
$v_list[1]="dev/data/";
$v_list[2]="log/file.log";
$tar_object->add($v_list);
?>
boolean addModify (
mixed $filelist
, string
$add_dir
, string
$remove_dir = ''
)
This methods add files and directories listed in
filelist
at the end of the existing archive.
If the archive does not exists it attempts to create it. If a file or directory is already in the archive it will only be added at the end of the archive. There is no update of the existing archived file or directory. However while extracting the archive, the last file will replace the first one. This results in a none optimization of the archive size. If a file or directory does not exists, it is ignored.
mixed $filelist
-
an array of filenames and directory
names, or a single string with names separated by a single blank space.
string $add_dir
-
a string which contains a path to be
added to the memorized path of each element in the list.
string $remove_dir
-
a string which contains a path to be
removed from the memorized path of each element in the list, when
relevant.
The path indicated in add_dir
will be added
at the beginning of the memorized path of each file/directory
listed. However it can be set to empty ''. The adding of a
path is done after the removing of path. The path
add/remove ability enables the user to prepare an archive
for extraction in a different path than the original path.
boolean
- Returns TRUE on success, FALSE on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "Invalid file list" | The argument for the function is not correctly formatted or build. | Check for typing mistakes in the argument |
NULL |
"Unable to open in write mode file name "
|
The file permissions for an existing file do not allow writing or the file is locked. | Check permissions and possible competive programs using the file. |
NULL | "Invalid file list" | Archive is empty or corrupted | |
NULL |
"File filename does not exist"
|
A file you want to add to the archive does not exist. | Check for typing mistakes in the function argument. |
NULL |
"Directory dirname can not be read"
|
A directory or a file in it you want to add to the archive does not exists or the permissions for reading the directory does not allow access. | Check for typing mistakes in the function argument and permissions. |
NULL |
"Unable to open file filename in binary read mode"
|
The file to add to the archive could not be read. | Check for typing mistakes in the function argument and file permissions. |
This function can not be called statically.
Add files to a compressed archive in a new directory
<?php
$tar_object = new Archive_Tar("tarname.tar");
$v_list[0]="dev/file.txt";
$v_list[1]="dev/data/";
$v_list[2]="log/file.log";
$tar_object->addModify($v_list, "install");
// files are stored in the archive as :
// install/file.txt
// install/data
// install/data/file1.txt
// install/data/... all the files and sub-dirs of data/
// install/file.log
?>
Add files to a compressed archive moving to a new directory
<?php
$tar_object = new Archive_Tar("tarname.tar");
$v_list[0]="dev/file.txt";
$v_list[1]="dev/data/";
$v_list[2]="log/file.log";
$tar_object->addModify($v_list, "install", "dev");
// files are stored in the archive as :
// install/file.txt
// install/data
// install/data/file1.txt
// install/data/... all the files and sub-dirs of data/
// install/log/file.log
?>
Add files to a compressed archive moving to a new directory (especially for Windows)
<?php
$tar_object = new Archive_Tar("tarname.tar");
$v_list[0]="d:\\dev\\file.txt";
$v_list[1]="d:\\dev\\data\\";
$v_list[2]="d:\\log\\file.log";
$tar_object->addModify($v_list, "install/temp", "d:\\dev");
// files are stored in the archive as :
// install/temp/file.txt
// install/temp/data
// install/temp/data/file1.txt
// install/temp/data/... all the files and sub-dirs of data/
// install/temp/log/file.log
?>
On Windows system, Windows path format can be used.
However if the files are using a Windows path,
the $remove_dir
parameter must also be
in Windows path format.
The $add_dir
parameter can be in
Windows or Unix path format.
boolean create (
mixed $filelist
)
This method creates the archive file and adds the listed files or directories.
If a file with the same tar name exists and is writable, it is replaced by the new tar archive (it is not an 'add', but a 'create'). If a file exists and is write-protected or is a folder, the method raises a PEAR_Error.
mixed $filelist
-
an array of filenames and directory names, or a single
string with names separated by a single blank space.
For each directory added in the archive, the files and
sub-directories are also added.
boolean
- Returns TRUE on success, FALSE on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "Invalid file list" | The argument for the function is not correct formatted or build. | Check for typing mistakes in the argument |
This function can not be called statically.
Creating an archive
<?php
$tar_object = new Archive_Tar("myArchive.tar");
// print errors
$tar_object->setErrorHandling(PEAR_ERROR_PRINT);
// Archive content
$v_list[0]="file.txt";
// the slash is optional
$v_list[1]="data/";
$v_list[2]="file.log";
// create the archive
$tar_object->create($v_list);
?>
Creating a compressed archive, use a string as create() argument
<?php
$tar_object = new Archive_Tar("tarname.tgz", true);
$tar_object->setErrorHandling(PEAR_ERROR_PRINT);
$tar_object->create("file.txt data/ file.log");
?>
boolean createModify (
array $filelist
, string
$add_dir
, string
$remove_dir = ''
)
This method creates the archive file and adds the listed files or directories.
If the file already exists and is writable, it is replaced by the new tar. It is a 'create' and not a 'add'. If the file exists and is read-only or is a directory, it is not replaced.
mixed $filelist
-
an array of filenames and directory
names, or a single string with names separated by a single blank space.
string $add_dir
- contains a path to be
added to the memorized path of each element in the list.
string $remove_dir
- contains a path to be
removed from the memorized path of each element in the list, when
relevant. Default is an empty string.
boolean
- Returns TRUE on success, FALSE on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "Invalid file list" | The argument for the function is not correctly formatted or build. | Check for typing mistakes in the argument |
This function can not be called statically.
Create a new compressed archive in a new directory
<?php
$tar_object = new Archive_Tar("tarname.tgz", true);
$tar_object->setErrorHandling(PEAR_ERROR_PRINT);
$v_list[0]="dev/file.txt";
$v_list[1]="dev/data/";
$v_list[2]="log/file.log";
$tar_object->createModify($v_list, "install", "dev");
// files are stored in the archive as :
// install/file.txt
// install/data
// install/data/file1.txt
// install/data/... all the files and sub-dirs of data/
// install/log/file.log
?>
Create a new compressed archive in a new directory (especially for Windows)
<?php
$tar_object = new Archive_Tar("tarname.tgz", true);
$tar_object->setErrorHandling(PEAR_ERROR_PRINT);
$v_list[0]="c:\\dev\\file.txt";
$v_list[1]="c:\\dev\\data\\";
$v_list[2]="c:\\log\\file.log";
$tar_object->createModify($v_list, "install/temp", "c:\\dev");
// files are stored in the archive as :
// install/temp/file.txt
// install/temp/data
// install/temp/data/file1.txt
// install/temp/data/... all the files and sub-dirs of data/
// install/temp/log/file.log
?>
boolean extract (
string $path
)
Extracts the files from the archive into the given path.
While extracting a file: If the file already exists it is replaced without looking for last modification date. If the file already exists and is write protected, the extraction is aborted. If a directory with the same name already exists, the extraction is aborted.
However the result can be a partial extraction that may need to be manually cleaned.
string $path
-
the destination path to extract
boolean
- Returns TRUE on success, FALSE on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"
Unable to open in read mode archive
"
|
The file is exclusively locked by another application. | Check for other applications working on the file. This can not be caused by a competive processing the archive with Archive_Tar |
NULL |
"
Unable to open in write mode archive
"
|
The file is locked by another application. | Check for other applications working on the file. This maybe caused by a competive processing the archive with Archive_Tar |
NULL |
"
Invalid extract mode mode
"
|
Implementation error | Should not occur, please set up a bug report. |
NULL |
"
Directory name already exists as a file
"
|
A file is marked up as directory in the archive. | Maybe a corrupted archive. |
NULL |
"
File name already exists as a directory
"
|
A directoy is marked up as file in the archive. | Maybe a corrupted archive. |
NULL |
"
File name already exists and
is write protected.
"
|
The archive contains a file which already exists in the destination dir and can not be overwritten. | Extract the archive to an empty directory. |
NULL |
"
Unable to create path for name
"
|
One or more new nested directories could not be created in the destination directory. | Ensure that the destination directory and all nested directories have the required permissions. |
NULL |
"
Unable to create directory name
"
|
A directory could not be created in the destination directory. | Ensure that the destination directory has the required permissions. |
NULL |
"
Error while opening name in write binary mode
"
|
The file could not be created. | The file is possibly locked. |
NULL |
"
Extracted file filename does
not have the correct file size filesize
(size expected). Archive may be corrupted.
"
|
Read the message. | Read the message. |
This function can not be called statically.
Extract compressed archive
<?php
$tar = new Archive_Tar('archive.tar.gz', true);
$result = $tar->extract('/home/myFolder');
?>
boolean extractList (
array $filelist
, string
$path = ''
, string
$remove_path = ''
)
This method extracts only the files from the archive that are
indicated in the $filelist
. These files
are extracted in the current directory or in the directory
indicated by the optional $path
parameter.
string $filelist
- an array of filenames
and directory names, or a single string with names separated
by a single blank space.
string $path
- the path of the directory
where the files and/or directory need to by extracted.
string $remove_path
- part of the
memorized path that can be removed if present at the beginning
of the files or directories path.
boolean
- Returns TRUE on success, FALSE on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"
Unable to open in read mode archive
"
|
The file is exclusively locked by another application. | Check for other applications working on the file. This can not be caused by a competive processing the archive with Archive_Tar |
NULL |
"
Unable to open in write mode archive
"
|
The file is locked by another application. | Check for other applications working on the file. This maybe caused by a competive processing the archive with Archive_Tar |
NULL |
"
Invalid extractlist mode mode
"
|
Implementation error | Should not occur, please set up a bug report. |
NULL |
"
Directory name already exists as a file
"
|
A file is marked up as directory in the archive. | Maybe a corrupted archive. |
NULL |
"
File name already exists as a directory
"
|
A directoy is marked up as file in the archive. | Maybe a corrupted archive. |
NULL |
"
File name already exists and
is write protected.
"
|
The archive contains a file which already exists in the destination dir and can not be overwritten. | Extract the archive to an empty directory. |
NULL |
"
Unable to create path for name
"
|
One or more new nested directories could not be created in the destination directory. | Ensure the destination directory and all nested directories have the required permissions. |
NULL |
"
Unable to create directory name
"
|
A directory could not be created in the destination directory. | Ensure the destination directory has the required permissions. |
NULL |
"
Error while opening name in write binary mode
"
|
The file could not be created. | The file is maybe locked. |
NULL |
"
Extracted file filename does
not have the correct file size filesize
(size expected). Archive may be corrupted.
"
|
Read the message. | Read the message. |
This function can not be called statically.
Extract compressed archive
<?php
// tarname.tar with files :
// dev/data/file.txt
// dev/data/log.txt
// readme.txt
$tar_object = new Archive_Tar("tarname.tar");
$tar_object->extractList("dev/data/file.txt readme.txt", "install",
"dev");
// Files will be extracted there :
// install/data/file.txt
// install/readme.txt
?>
boolean extractModify (
string $path
, string
$remove_path
)
This method extracts all the content of the archive in the directory
indicated by path
. When relevant the memorized
path of the files or directories can be modified by removing the
remove_path
path at the beginning of the
file or directory path.
While extracting a file: If the file already exists it is replaced without looking for last modification date. If the file already exists and is write protected, the extraction is aborted. If a directory with the same name already exists, the extraction is aborted.
While extracting a directory, if a file with the same name already exists, the extraction is aborted. While extracting a file/directory if the destination directory exist and is write protected, or does not exist but can not be created, the extraction is aborted. If after extraction an extracted file does not show the correct stored file size, the extraction is aborted.
string $path
- the path of the directory
where the files and/or directories need to by extracted.
string $remove_path
- part of the memorized
path that can be removed if present at the beginning of the files
or directories path.
boolean
- Returns TRUE on success, FALSE on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"
Unable to open in read mode archive
"
|
The file is exclusive locked by another application. | Check for other applications working on the file. This can not be caused by a competive processing the archive with Archive_Tar |
NULL |
"
Unable to open in write mode archive
"
|
The file is locked by another application. | Check for other applications working on the file. This maybe caused by a competive processing the archive with Archive_Tar |
NULL |
"
Invalid extractmodify mode mode
"
|
Implementation error | Should not occur, please set up a bug report. |
NULL |
"
Directory name already exists as a file
"
|
A file is marked up as directory in the archive. | Maybe a corrupted archive. |
NULL |
"
File name already exists as a directory
"
|
A directoy is marked up as file in the archive. | Maybe a corrupted archive. |
NULL |
"
File name already exists and
is write protected.
"
|
The archive contains a file which already exists in the destination dir and can not be overwritten. | Extract the archive to an empty directory. |
NULL |
"
Unable to create path for name
"
|
One or more new nested directories could not be created in the destination directory. | Ensure the destination directory and all nested directories have the required rights. |
NULL |
"
Unable to create directory name
"
|
A directory could not be created in the destination directory. | Ensure the destination directory has the required rights. |
NULL |
"
Error while opening name in write binary mode
"
|
The file could not be created. | The file is maybe locked. |
NULL |
"
Extracted file filename does
not have the correct file size filesize
(size expected). Archive may be corrupted.
"
|
Read the message. | Read the message. |
This function can not be called statically.
Extract compressed archive into a new directory ignoring the old one
Extract compressed archive into a new directory ignoring the old one (especilly for Windows)
array listContent (
)
Lists the files and the directories of the archive.
array
-
each array entry represents a file or folder.
The array is not sorted, so the index shows the position
of the file or directory in the archive.
Each entry contains the following information:
$file['filename']
-
Name and path of the file/dir.
$file['mode']
-
File permissions (result of fileperms())
$file['uid']
-
user id
$file['gid']
-
group id
$file['size']
-
filesize
$file['mtime']
-
Last modification time (result of filemtime())
$file['typeflag']
-
empty for file, "5" for directory
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"
Unable to open in read mode archive
"
|
The file is exclusively locked by another application. | Check for other applications working on the file. This can not be caused by a competive processing the archive with Archive_Tar |
NULL |
"
Invalid listcontent mode mode
"
|
Implementation error | Should not occur, please set up a bug report. |
NULL |
"
Directory name already exists as a file
"
|
A file is marked up as directory in the archive. | Maybe a corrupted archive. |
NULL |
"
File name already exists as a directory
"
|
A directoy is marked up as file in the archive. | Maybe a corrupted archive. |
NULL |
"
File name already exists and
is write protected.
"
|
The archive contains a file which already exists in the destination dir and can not be overwritten. | Extract the archive to an empty directory. |
NULL |
"
Unable to create path for name
"
|
One or more new nested directories could not be created in the destination directory. | Ensure the destination directory and all nested directories have the required rights. |
NULL |
"
Unable to create directory name
"
|
A directory could not be created in the destination directory. | Ensure the destination directory has the required rights. |
NULL |
"
Error while opening name in write binary mode
"
|
The file could not be created. | The file is maybe locked. |
NULL |
"
Extracted file filename does
not have the correct file size filesize
(size expected). Archive may be corrupted.
"
|
Read the message. | Read the message. |
This function can not be called statically.
List archive content
<?php
$tar_object = new Archive_Tar("tarname.tar");
if (($v_list = $tar_object->listContent()) != 0) {
for ($i=0; $i<sizeof($v_list); $i++) {
echo "Filename :'".$v_list[$i]['filename']."'<br>";
echo " .size :'".$v_list[$i]['size']."'<br>";
echo " .mtime :'".$v_list[$i]['mtime']."' (".
date("l dS of F Y h:i:s A", $v_list[$i]['mtime']).")<br>";
echo " .mode :'".$v_list[$i]['mode']."'<br>";
echo " .uid :'".$v_list[$i]['uid']."'<br>";
echo " .gid :'".$v_list[$i]['gid']."'<br>";
echo " .typeflag :'".$v_list[$i]['typeflag']."'<br>";
}
}
?>
boolean extractInString (
string $path
)
This method extracts the file identified by path
from the archive and returns it
as a string.
It does not use temporary files.
string $path
- the path of the file
to extract
string
- the content of the extracted file
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"
Unable to open in read mode archive
"
|
The file is exclusive locked by another application. | Check for other applications working on the file. This can not be caused by a competive processing the archive with Archive_Tar |
NULL |
"
Invalid extractinstring mode mode
"
|
Implementation error | Should not occur, please set up a bug report. |
NULL |
"
Error while opening name in write binary mode
"
|
The file could not be created. | The file is maybe locked. |
NULL |
"
Extracted file filename does
not have the correct file size filesize
(size expected). Archive may be corrupted.
"
|
Read the message. | Read the message. |
This function can not be called statically.
Extract a file in a string
boolean addString (
string $filename
, string $content
)
This method adds the string
content
in the archive like a file with full filename
filename
.
If the archive does not exists it attempts to create it.
string $filename
-
the path and filename that will be associated with the added string
in the archive.
string $content
-
the string to add in the archive as a file.
boolean
- Returns TRUE on success, FALSE on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"Unable to open in write mode file name "
|
The file permissions for an existing file do not allow writing or the file is locked. | Check permissions and possible competive programs using the file. |
NULL |
"Unable to open file filename in binary read mode"
|
The file to add to the archive could not be read. | Check for typing mistakes in the function argument and file permissions. |
This function can not be called statically.
Add a string in a compressed archive
<?php
$tar_object = new Archive_Tar("tarname.tgz");
$content = "this file was generated from a string";
$tar_object->addString("data/readme.txt", $content);
// A file is created in the archive with name :
// data/readme.txt
?>
Common file and directory routines
File provides an easy interface to PHP's builtin file and directory functions, plus some functions to deal with paths.
Using File
<?php
require_once 'File.php';
$file = "/home/tal/example.txt";
//Echo the whole file
echo File::readAll($file);
//Now use a different approach
$fp = new File();
//Write a single line to the file, using a Macintosh EOL character and
//truncating the file before writing to it
$fp->writeLine($file, "This is a single line", FILE_MODE_WRITE, "\r");
//strip leading and trailing separators from the file path
echo $fp->stripLeadingSeparators($file);
echo $fp->stripTrailingSeparators($file);
?>
The default number of bytes to read from a file.
Read-only mode for opened files
Using this mode, opened files will be truncated first and then new data will be written to them.
Using this mode, new data will be appended to the end of opened files.
Shared (read) locking mode
Exclusive (write) locking mode
mixed File::buildpath (
array $parts
,
string $separatir
= DIRECTORY_SEPARATOR
)
deprecated
This function can be called statically.
Using File::write()
<?php
require_once 'File.php';
// deprecated Use File_Util::buildPath() instead.
?>
mixed File::close (
string $filename
,
string $mode
)
This function can be called statically.
Using File::close()
<?php
require_once 'File.php';
$e = File::close('test.txt', FILE_MODE_WRITE);
if (PEAR::isError($e)) {
echo 'Could not close file : ' . $e->getMessage();
} else {
echo "Successfully closed file test.txt\n";
}
?>
string File::gettempdir (
)
deprecated
This function can be called statically.
Using File::getTempDir()
<?php
require_once 'File.php';
// deprecated Use File_Util::tmpDir() instead
?>
string File::getTempFile (
string $dirname
= null
)
deprecated
This function can be called statically.
Using File::getTempFile()
<?php
require_once 'File.php';
// deprecated Use File_Util::tmpFile() instead
?>
bool File::isAbsolute (
string $path
)
This method checks whether the supplied path is an absolute path (eg. "/foo/bar" or "C:\foo\bar").
string $path
- the path the will be checked.
This method returns TRUE if the path is absolute, FALSE otherwise.
This function can be called statically.
Using File::isAbsolute()
<?php
require_once 'File.php';
if (File::isAbsolute("/usr/local") {
echo "Path is absolute";
} else {
echo "Path isn't absolute";
}
?>
This short example will output the string 'Path is absolute'.
mixed File::read (
string $filename
,
int $size = FILE_DEFAULT_READSIZE
,
mixed $lock
= false
)
File::read() reads a specific amount of bytes from a specified file and returns them to the user.
string $filename
- the file to read from
int $size
- the number of bytes to read from the
file (defaults to FILE_DEFAULT_READSIZE)
mixed $lock
- lock type to use, FALSE if none
mixed
- this function returns the requested bytes
from the file if there were no errors, FALSE if it reached EOF or
a PEAR_Error object if an error has occured during reading from file.
Error Code | Error Value | Meaning | Solution |
---|---|---|---|
NULL | "File does not exist: $filename " |
The file $filename does not exist. |
Check if the path that is passed to the function is correct. |
NULL | "Failed to open file: $filename " |
There are few possible things that might cause that error, usually it's caused by wrong permissions or bad sectors on the harddisk. | Check the permissions of the file (ls -l {file} on UNIX systems) and change them so the file is readable by PHP, check if the harddisk is working properly and has no bad sectors. |
This function can be called statically.
Using File::read()
<?php
require_once 'File.php';
//output 40 bytes of file foo.bar
echo File::read("/path/to/foo.bar", 40);
?>
mixed File::readAll (
string $filename
,
mixed $lock
= false
)
This function can be called statically.
Using File::readAll()
<?php
require_once 'File.php';
?>
mixed File::readCharChar (
string $filename
,
mixed $lock
= false
)
This function can be called statically.
Using File::readChar()
<?php
require_once 'File.php';
$e = File::readChar('test.txt');
if (PEAR::isError($e)) {
echo 'Could not read char from file : ' . $e->getMessage();
} else {
echo $e;
}
?>
mixed File::readLine (
string $filename
,
mixed $lock
= false
)
This function can be called statically.
Using File::readLine()
<?php
require_once 'File.php';
$e = File::readLine('test.txt');
if (PEAR::isError($e)) {
echo 'Could not read from file : ' . $e->getMessage();
} else {
echo $e;
}
?>
mixed File::rewind (
string $filename
,
string $mode
)
This function can be called statically.
Using File::rewind()
<?php
require_once 'File.php';
$e = File::rewind('test.txt', FILE_MODE_READ);
if (PEAR::isError($e)) {
echo 'Could not rewind the file : ' . $e->getMessage();
} else {
echo "File test.txt successfully rewound\n";
}
?>
string File::skipRoot (
string $path
)
This method strips the root directory from $path
.
string $path
- the path to be processed.
If the path is absolute, this method returns the processed path, otherwise, it returns the path untouched.
This function can be called statically.
Using File::skipRoot()
<?php
require_once 'File.php';
echo File::skipRoot("/home/foo/bar");
?>
This example prints out home/foo/bar
.
string File::stripLeadingSeparators (
string $path
,
string $separator
= DIRECTORY_SEPARATOR
)
This method removes the leading directory separator (like "/" on *nix) from a path name.
string $path
- the path name where the
leading separator should be removed from.
string $separator
- optional string that
defines the separator. This parameter defaults to the value of
the constant DIRECTORY_SEPARATOR that is
pre-defined by PHP.
This methods returns the given path name without a leading directory separator.
This function can be called statically.
Using File::stripLeadingSeparators()
<?php
require_once "File.php";
echo File::stripLeadingSeparators("/home/foo/lala/");
?>
This example will print home/foo/lala/
.
string File:stripTrailingSeparators (
string $path
,
string $separator
= DIRECTORY_SEPARATOR
)
This method removes the trailing directory separator (like "/" on *nix) from a path name.
string $path
- the path name where the
trailing separator should be removed from.
string $separator
- optional string that
defines the separator. This parameter defaults to the value of
the constant DIRECTORY_SEPARATOR that is
pre-defined by PHP.
This methods returns the given path name without a trailing directory separator.
This function can be called statically.
Using File:stripTrailingSeparators()
<?php
require_once "File.php";
echo File::stripTrailingSeparators("/home/foo/lala/");
?>
This example will print /home/foo/lala
.
mixed File::unlock (
string $filename
,
string $mode
)
This function can be called statically.
Using File::unlock()
<?php
require_once 'File.php';
$e = File::unlock('test.txt');
if (PEAR::isError($e)) {
echo 'Could not unlock the file : ' . $e->getMessage();
} else {
echo "Successfully unlocked the file test.txt\n";
}
?>
mixed File::write (
string $filename
,
string $char
,
string $mode = FILE_MODE_APPEND
,
mixed $lock
= false
)
This function can be called statically.
Using File::write()
<?php
require_once 'File.php';
$e = File::write('test.txt', 'this is a test line', FILE_MODE_WRITE);
if (PEAR::isError($e)) {
echo 'Could not write to file : ' . $e->getMessage();
} else {
echo "Successfully wrote to file test.txt\n";
}
?>
mixed File::writeChar (
string $filename
,
string $char
,
string $mode = FILE_MODE_APPEND
,
mixed $lock
= false
)
This function can be called statically.
Using File::writeChar()
<?php
require_once 'File.php';
$e = File::write('test.txt', 'a');
if (PEAR::isError($e)) {
echo 'Could not write to file : ' . $e->getMessage();
} else {
echo "Successfully wrote to file test.txt\n";
}
?>
mixed File::writeLine (
string $filename
,
string $line
,
string $mode = FILE_MODE_APPEND
,
string $crlf = "\n"
,
mixed $lock
= false
)
Writes a single line, appending a linefeed by default.
$filename
- Name of file to write to
$line
- Line of data to be written to file
$mode
- Write mode, can be either
FILE_MODE_WRITE
or FILE_MODE_APPEND
.
Defaults to appending.
$crlf
- Carriage return / line feed your system is using.
Defaults to LF (\n
), but can be set to anything.
On Unix, \n
is used, on
Windows \r\n
and Mac OS uses \r
.
$lock
- If the file shall be locked
PEAR_Error when an error occured, number of bytes written when all went well (crlf included).
Using File::writeLine()
<?php
require_once 'File.php';
$e = File::writeLine('test.txt', str_repeat("0123456789", 1000));
if (PEAR::isError($e)) {
echo 'Could not write to file : ' . $e->getMessage();
} else {
echo "Successfully wrote to file test.txt\n";
}
?>
Commonly needed functions to search for files and directories
All search functions use $pattern
parameter to
specify match for filenames. Format of the $pattern
depends on value of another parameter - $pattern_type
.
$pattern_type
is
'php'
, then the pattern is
case-sensitive string which follows the conventions of the
ereg_*-functions.
'perl'
pattern type it must follow the
preg_*-functions pattern format. It is recommended to use
'perl'
, because it is faster.
'shell'
mode is most simple and should
be familiar to everybody with basic computer skills. It is as easy as
windows approach, but has some additional concepts borrowed from the FAR
Manager software. The text of the following section was borrowed from
the FAR Manager documentation.
File masks are used to select single files and folders or groups of them. Masks may contain common valid file name symbols, wildcards ('*' and '?') and special expressions:
For example, files ftp.exe, fc.exe and f.ext may be selected using mask f*.ex?, mask *co* will select both color.ini and edit.com, mask [c-f,t]*.txt can select config.txt, demo.txt, faq.txt and tips.txt.
You may enter several file masks separated with commas or semicolons. For example, to select all the documents, you can specify *.doc,*.txt,*.wri in search pattern.
You may use exclude masks. An exclude mask is one or multiple file masks that must not be matched by the files matching the mask. The exclude mask is delimited from the main mask by the character '|'.
Usage examples of exclude masks:
*.cpp
All files with the extension cpp.*.*|*.bak,*.tmp
All files except for the files with extensions bak and tmp.*.*|
This mask has an error - the character | is entered, but the mask itself is not specified.*.*|*.bak|*.tmp
Also an error - the character | may not be contained in the mask more than once.|*.bak
The same as *|*.bakThe comma (or semicolon) is used for separating file masks from each other, and the '|' character separates include masks from exclude masks.
'shell' match mode is available from version 1.2.0 of File_Find
array
&File_Find::glob (
string $pattern
,
string $dirpath
,
string $pattern_type = 'php'
)
Search the directory to find matches for the specified pattern.
$pattern
- a string containing the pattern
to search the directory for.
$dirpath
- a string containing the
directory path to search.
$pattern_type
- a string containing the type
of pattern matching functions to use (can either be 'php', 'perl' or 'shell').
The format of the $pattern
depends on the
$pattern_type
-value. For more information see
search methods
array
-
an array contains all filenames and name of
subdirectories, which matches the pattern.
Or a PEAR_Error.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | " Cannot open directory " | The given directory could not be opend. | Check typing and directory permissions. This can not be caused by a competive processing the archive with Archive_Tar |
This function can be called statically.
Find all PHP files in current directory
<?php
include "File/Find.php";
$dir = ".";
$items = &File_Find::glob( '!.*\.php$!', $dir, 'perl' );
print_r($items);
?>
array
&File_Find::maptree (
string $directory
)
Map the directory tree given by the directory_path parameter.
string $directory
- contains the directory
path that you want to map
array
- a two element array, the first
element containing a list of all the directories, the second
element containing a list of all the files.
This function can be called statically.
Get the map of a directory
<?php
include "File/Find.php";
$dir = "File_Find";
list($directories, $files) = File_Find::maptree($dir);
echo "Directories ";
print_r($directories);
echo "Files ";
print_r($files);
?>
The above example will output something similar to:
Directories Array
(
[0] => File_Find
[1] => File_Find\dir2
[2] => File_Find\dir2\2
[3] => File_Find\dir2\1
[4] => File_Find\dir2\0
[5] => File_Find\dir
[6] => File_Find\dir\txtdir
[7] => File_Find\dir\dir3
[8] => File_Find\dir\dir2
)
Files Array
(
[0] => File_Find\dir2\2\1.txt
[1] => File_Find\dir2\1\1.txt
[2] => File_Find\dir2\0\1.txt
[3] => File_Find\dir\1.txt
[4] => File_Find\dir\2.txt
[5] => File_Find\dir\txtdir\5.txt
[6] => File_Find\dir\dir3\4.bak
[7] => File_Find\dir\dir3\4.txt
[8] => File_Find\dir\dir2\3.bak
[9] => File_Find\dir\dir2\3.txt
)
array
&File_Find::mapTreeMultiple (
string $directory
,
integer $maxrecursion = 0
,
integer $count = 0
)
Map the directory tree given by the
directory_path
parameter.
Depending on maxrecursion
you get the
content of the directory and the content of the subdirectories
too.
string $directory
- contains the directory
path that you want to map
integer $maxrecursion
- defines the
deep of recursive mapping of subdirectories
integer $count
- can be ignored -
internal parameter to track recursion level
array
-
a multidimensional array containing all subdirectories
and their files
This function can be called statically.
Get the content of a directory including the content of subdirectories
<?php
include "File/Find.php";
$file = File_Find::mapTreemultiple('/usr/', 1);
print_r($file);
?>
The above example will output something similar to:
Array
(
[0] => file1.tmp
[1] => file2.tmp
['bin'] => Array
(
[0] => readme.txt
)
)
array
&File_Find::search (
string $pattern
,
string $dirpath
,
string $pattern_type = 'php'
,
bool $fullpath = true
,
string $match = 'files'
)
Search the directory to find matches for the specified pattern.
$pattern
- a string containing the pattern
to search the directory for.
$dirpath
- a string containing the
directory path to search.
$pattern_type
- a string containing the type
of pattern matching functions to use (can either be 'php', 'perl' or 'shell').
The format of the $pattern
depends on the
$pattern_type
-value. For more information see
search methods
$fullpath
- whether the string should be
matched against the full path or only against the filename
$match
- can be either 'files', 'directories' or
'both' to specify the kind of list to return
array
-
an array containing all filenames
This function can be called statically.
Provides an API for managing SAMBA passwd-style files
SAMBA is a free implementation of CIFS/SMB. The password encryption on Unix and Windows is different, therefore SAMBA must have his own file where the passwords are encrypted either as NT-Hash and/or as LAN-Manager-Hash. LAN-Manager-Hashes are weak and shouldn't be used anymore, NT-Hashes are based on MD4, but no salt is used, therefore users with the same passwords have the same NT-Hash. In this file are also stored machine accounts, if the SAMBA server acts as PDC, such entries ends with $.
void
File_SMBPasswd::File_SMBPasswd (
string $file
)
Creates a new File_SMBPasswd object and bind it to the given file.
string $file
- SAMBA password file to read
mixed File_SMBPasswd::load (
)
Load the contents of smbpasswd file.
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Using File_SMBPasswd::load()
<?php
require_once('File/SMBPasswd.php');
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$status = $fh->load();
if (PEAR::isError($status)) {
// handle errors
} else {
// continue processing
}
?>
string File_SMBPasswd::getFile (
)
Get the value of file property. This property contains the filename of the current smbpasswd file.
string containing the filename.
This function can not be called statically.
array File_SMBPasswd::getAccounts (
)
Get the value of accounts property. This property contains all accounts of the current smbpasswd file.
array containing all accounts.
This function can not be called statically.
mixed File_SMBPasswd::addAccountEncrypted (
string $user
, int $userid
, string $lmhash = ''
, string $nthash = ''
, string $comment = ''
, string $flags = '[U ]'
)
Modifies an existing account. The passwords must be already encrypted.
string $user
- username to be added
int $userid
- userid of the user
string $lmhash
- LAN-Manager-Hash
string $nthash
- NT-Hash
string $comment
- comment
string $flags
- flags
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Note that the user to be added must already exist in the systems password file.
Using File_SMBPasswd::addAccountEncrypted()
<?php
require_once 'File/SMBPasswd.php';
// add account mbretter
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
$status = $fh->addAccountEncrypted(
'mbretter',
1005,
'75BA30198E6D1975AAD3B435B51404EE',
'FC156AF7EDCD6C0EDDE3337D427F4EAC',
'Michael Bretterklieber');
if (PEAR::isError($status)) {
// handle errors
} else {
$fh->save();
}
?>
mixed File_SMBPasswd::addAccount (
string $user
, int $userid
, string $pass = ''
, string $comment = ''
, string $flags = '[U ]'
)
This method works in the same way as File_SMBPasswd::addAccountEncrypted() , except the password has to be given as plaintext. The encryption is done internaly.
string $user
- username to be added
int $userid
- userid of the user
string $pass
- plaintext-password
string $comment
- comment
string $flags
- flags
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Note that the user to be added must already exist in the system password file.
Using File_SMBPasswd::addAccount()
<?php
require_once 'File/SMBPasswd.php';
// add user mbretter
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
$status = $fh->addAccount(
'mbretter',
1004,
'MyPw',
'Michael Bretterklieber');
if (PEAR::isError($status)) {
// handle errors
} else {
// continue processing
$fh->save();
}
?>
mixed File_SMBPasswd::addUser (
string $user
, int $userid
, string $pass = ''
, string $comment = ''
)
This method works in the same way as File_SMBPasswd::addAccount() , except the flags are forced representing a user-account.
string $user
- username to be added
int $userid
- userid of the user
string $pass
- plaintext-password
string $comment
- comment
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Note that the user to be added must already exist in the system password file.
Using File_SMBPasswd::addUser()
<?php
require_once 'File/SMBPasswd.php';
// add user mbretter
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
$status = $fh->addUser(
'mbretter',
1004,
'MyPw',
'Michael Bretterklieber');
if (PEAR::isError($status)) {
// handle errors
} else {
// continue processing
$fh->save();
}
?>
mixed File_SMBPasswd::addMachine (
string $machine
, int $userid
, string $comment = ''
)
This method works in the same way as File_SMBPasswd::addAccount() , except the flags are forced representing a machine-account, a $ is implicitely added to the machinename.
string $machine
- machinename to be added
int $userid
- userid of the user
string $comment
- comment
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Note that the machine to be added must already exist in the system password file.
Using File_SMBPasswd::addMachine()
<?php
require_once 'File/SMBPasswd.php';
// add user mbretter
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
$status = $fh->addMachine(
'mypc',
10004,
'My Turbo PC');
if (PEAR::isError($status)) {
// handle errors
} else {
// continue processing
$fh->save();
}
?>
mixed File_SMBPasswd::modAccountEncrypted (
string $user
, int $userid
, string $nthash = ''
, string $lmhash = ''
, string $comment = ''
, string $flags = ''
)
Modifies an existing account using pre-encrypted passwords.
string $user
- username to be added
int $userid
- userid of the user
string $nthash
- new NT-Hash
string $lmhash
- new LM-Hash
string $comment
- comment
string $flags
- flags
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Note that the user to be added must already exist in the system password file.
Using File_SMBPasswd::modAccountEncrypted()
<?php
require_once 'File/SMBPasswd.php';
// modify user mbretter
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
$status = $fh->modAccountEncrypted(
'mbretter',
1005,
'75BA30198E6D1975AAD3B435B51404EE',
'FC156AF7EDCD6C0EDDE3337D427F4EAC',
Michaela Bretterklieber');
if (PEAR::isError($status)) {
// handle errors
} else {
$fh->save();
}
?>
mixed File_SMBPasswd::modAccount (
string $user
, int $userid
, string $pass = ''
, string $comment = ''
, string $flags = ''
)
This method works in the same way as File_SMBPasswd::modAccountEncrypted() , except the password has to be given as plaintext. The encryption is done internaly.
string $user
- username to be modified
int $userid
- userid of the user
string $pass
- plaintext-password
string $comment
- comment
string $flags
- flags
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Using File_SMBPasswd::modAccount()
<?php
require_once 'File/SMBPasswd.php';
// modify user mbretter
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
$status = $fh->modAccount(
'mbretter',
1005,
'MyPwa',
'Michaela Bretterklieber');
if (PEAR::isError($status)) {
// handle errors
} else {
$fh->save();
}
?>
mixed File_SMBPasswd::modUser (
string $user
, int $userid
, string $pass = ''
, string $comment = ''
, string $flags = ''
)
This method modifies an existing user, using a plaintext-password.
string $user
- username to be added
int $userid
- userid of the user
string $pass
- plaintext-password
string $comment
- comment
string $flags
- flags
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Note that the user to be added must already exist in the system password file.
Using File_SMBPasswd::modUser()
<?php
require_once 'File/SMBPasswd.php';
// modify user mbretter
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
$status = $fh->modUser(
'mbretter',
1005,
'MyPwa',
'Michaela Bretterklieber');
if (PEAR::isError($status)) {
// handle errors
} else {
$fh->save();
}
?>
mixed File_SMBPasswd::delAccount (
string $name
)
This method deletes an existing account.
string $name
- account name to be deleted (username or machinename)
mixed
- Returns TRUE on success,
PEAR_Error on failure.
Using File_SMBPasswd::delAccount()
<?php
require_once 'File/SMBPasswd.php';
// delete user mbretter
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
$status = $fh->delAccount('mbretter');
if (PEAR::isError($status)) {
// handle errors
} else {
$fh->save();
}
?>
mixed File_SMBPasswd::delUser (
string $user
)
This method deletes an existing user.
string $user
- username to be deleted
mixed
- Returns TRUE on success,
PEAR_Error on failure.
Using File_SMBPasswd::delAccount()
<?php
require_once 'File/SMBPasswd.php';
// delete user mbretter
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
$status = $fh->delUser('mbretter');
if (PEAR::isError($status)) {
// handle errors
} else {
$fh->save();
}
?>
mixed File_SMBPasswd::verifyAccountEncrypted (
string $user
, string $nthash
, string $lmhash = ''
)
This method verifies the given username and passwords against the entry in the loaded smbpasswd file. The given passwords must already be a valid NT-Hash or LM-Hash, whereas the LM-Hash is optional.
string $user
- username to be verified
string $nthash
- the NT-Hash
string $lmhash
- the LM-Hash
mixed
- Returns TRUE on success, FALSE on failure.
Using File_SMBPasswd::verifyAccount()
<?php
require_once 'File/SMBPasswd.php';
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
if ($fh->verifyAccountEncrypted('mbretter', '75BA30198E6D1975AAD3B435B51404EE')) {
echo "Account is valid";
} else {
echo "Account is in-valid";
}
?>
mixed File_SMBPasswd::verifyAccount (
string $user
, string $pass
)
This method verifies the given username and plaintext-password against the entry in the loaded smbpasswd file.
string $user
- username to be verified
string $pass
- the plaintext password
mixed
- Returns TRUE on success, FALSE on failure.
Using File_SMBPasswd::verifyAccount()
<?php
require_once 'File/SMBPasswd.php';
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$fh->load();
if ($fh->verifyAccount('mbretter', 'MyPw')) {
echo "Account is valid";
} else {
echo "Account is in-valid";
}
?>
mixed File_SMBPasswd::lock (
)
lock the smbpasswd file.
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Using File_SMBPasswd::lock()
<?php
require_once('File/SMBPasswd.php');
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$status = $fh->lock();
if (PEAR::isError($status)) {
// handle errors
} else {
// continue processing
}
?>
mixed File_SMBPasswd::unlock (
)
unlock the smbpasswd file.
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Using File_SMBPasswd::unlock()
<?php
require_once('File/SMBPasswd.php');
$fh = new File_SMBPasswd('/usr/local/private/smbpasswd');
$status = $fh->unlock();
if (PEAR::isError($status)) {
// handle errors
} else {
// continue processing
}
?>
mixed File_SMBPasswds:save (
)
Saves the contents of File_SMBPasswd object as a corresponding smbpasswd file on the disc. Save implicitely locks the file.
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Using File_SMBPasswd::save()
<?php
require_once('File_SMBPasswd.php');
$f = new File_SMBPasswd('./smbpasswd');
$f->load();
$ret = $f->addAccount('sepp3', 12, 'MyPw');
if (PEAR::isError($ret)) {
echo $ret->getMessage();
exit;
}
$ret = $f->modAccount('sepp', '', 'MyPw');
if (PEAR::isError($ret)) {
echo $ret->getMessage();
exit;
}
$ret = $f->delAccount('karli');
if (PEAR::isError($ret)) {
echo $ret->getMessage();
exit;
}
$status = $f->save();
if (PEAR::isError($status)) {
// handle errors
} else {
// continue processing
}
?>
void File_SMBPasswd::printAccounts (
)
Print all accounts of the loaded smbpasswd file.
void
This function can not be called statically.
Using File_SMBPasswd::printAccounts()
<?php
require_once('File/SMBPasswd.php');
$f = new File_SMBPasswd('/usr/local/private/smbpasswd');
$f->load();
$f->printaccounts();
?>
Provides an API for replacing text in files
With SearchReplace, you can replace a text in as many as desired files by another.
Typical usage
<?php
include 'File/SearchReplace.php' ;
$files = array( "test1.txt",
"test2.txt",
"test3.txt" ) ;
$ignoreline = array( "#", ":") ;
$snr = new File_SearchReplace( "Yes", "No", $files, "/mail/", false,
$ignoreline) ;
$snr -> doSearch() ;
?>
The example replaces all occurences of "Yes"
with
"No"
in the given $files and in all files in the
directory "/mail/"
. If a line in a file starts
with one of the chars in $ignoreline
, possible
matches will be ignored.
You can do a new search without creating a new instance of the class.
Do a new search
<?php
...
// string to search
$snr -> setFind( "Er") ;
// string to find
$snr -> setReplace( "Sie") ;
// look in this files
$snr -> setFiles( $files) ;
// look in this directories
$snr -> setDirectories( array( "/neue_briefe/")) ;
// look in the subdirectories too
$snr -> setIncludeSubdir( true) ;
// ignore lines in the files starting with this chars
$snr -> setIgnoreLines( array( "::", "#")) ;
// restart search'n'replace
$snr -> doSearch() ;
?>
File_SearchReplace supports different
kinds of search functions. The type directly influence the format
of the required $find
-parameter
normal
- default, the
only type supporting the
$IgnoreLines
-parameter
quick
- use str_replace()
preg
- use preg_replace()
ereg
- use ereg_replace()
To set the type, call setSearchFunction() before doSearch().
void
File_SearchReplace::File_SearchReplace (
mixed $find
,
mixed $replace
,
string $files
,
mixed $directories = ''
,
boolean
$include_subdir
= true
,
array $ignore_lines = array()
)
Constructor
mixed $find
- the string/regex or array
of strings/regexes to find
mixed $replace
- the string/regex or
array of strings/regexes to replace $find
array $files
- the file(s) to perform
this operation on.
array $directories
- the directories
to perform this operation on.
boolean $include_subdir
- if performing on
directories, whether to traverse subdirectories.
array $ignore_lines
- ignore lines beginning
with any of the strings in this array. This feature only
works with the "normal" search.
This function can be called statically.
integer
File_SearchReplace::getNumOccurences (
)
Returns the number of replaced strings after a File_SearchReplace::doSearch() call.
integer
- the number of occurences
This function can not be called statically.
void
File_SearchReplace::doSearch (
)
Starts searching for the patterns and replaces them, if found.
This function can not be called statically.
void
File_SearchReplace::setDirectories (
array $directories
)
Set the directories where the files to scan reside.
string $directories
-
the directories to perform
This function can not be called statically.
void
File_SearchReplace::setFiles (
array $files
)
Set the files to scan.
array $files
- the file(s) to scan
This function can not be called statically.
void
File_SearchReplace::setFind (
mixed $find
)
Set the pattern for the string to find.
mixed $find
- the string/regex or arrays of them to find
This function can not be called statically.
void
File_SearchReplace::setIncludeSubdir (
integer $include_subdir
)
Enable or disable look up in sub directories for files to scan.
boolean $inlude_subdir
- Whether to traverse
subdirectories or not.
This function can not be called statically.
void
File_SearchReplace::setReplace (
mixed $replace
)
Set the replacment string.
mixed $replace
- the string/regex or arrays of them
to replace found string/regex with.
This function can not be called statically.
void
File_SearchReplace::setSearchFunction (
array $search_function
)
Function to determine which search function is used.
$search_function
- The search function that
should be used. Possible values: "normal"
,
"quick"
, "preg"
,
"ereg"
This function can not be called statically.
Provides methods to create and manipulate .htaccess files.
File_HtAccess provides common methods to create and manipulate Apache / NCSA style .htaccess files. These files together with accompanying password files are used to protect webserver directories. Since File_HtAccess does not provide any means to manipulate or create password files you should use it together with File_Passwd.
The most common and the original purpose of .htaccess files is to create per-directory password protection of resources. With modern webservers there is vast amount of other things .htaccess files can do. These include: custom error pages, ip based access control, redirecting users automatically, denying directory listing and using different files as an index file.
File_HtAccess concentrates only to password protection of directories, although it is possible to use it to control other things mentioned above too.
A .htaccess file is built from the following basic directives. They differ a bit whether youre using Basic or Digest authentication.
Directive | Purpose |
---|---|
AuthType | Authentication type being used, "Basic" or "Digest". |
AuthName | Authentication realm or name. |
AuthUserFile | Full path to password file if using Basic authentication. |
AuthGroupFile | Full path to group file if using Basic authentication. |
AuthDigestFile | Full path to password file if using Digest authentication. |
AuthDigestGroupFile | Full path to group file if using Digest authentication. |
Require | Requirements which must be met to grant access. |
File_HtAccess provides method accessor methods with corresponding names for each of these directives, such as getAuthType() and setAuthType().
A typical .htaccess file looks like this:
When a client requests resource protected with basic authentication webserver responds with a 401 Authentication Required header. When client receives 401 header it asks the user for username and password. If authentication succeeds, the protected resource will be sent to the client. Otherwise the access will be denied.
Even though the passwords are stored encrypted on serverside they are sent cleartext between client and server when using Basic authentication. With Digest authentication the passwords are never sent cleartext but as a MD5 digest instead. The caveat is, most browsers do not support Digest authentication.
object new File_HtAccess (
string $file='.htaccess'
,
array $params
)
Creates an instance of a File_HtAccess object.
string $file
- filename to use. Defaults
to .htaccess
array $params
-
a array of parameters which can be:
$params['authname']
- authname
$params['authtype']
- authtype
$params['authuserfile']
- authuserfile
$params['authgroupfile']
- authgroupfile
$params['require']
- require
$params['additional']
- additional
object
- instance of File_HtAccess
How to create new instance of File_HtAccess
<?php
require_once('File/HtAccess.php');
$fh = new File_HtAccess('.htaccess');
?>
mixed File_HtAccess::load (
)
Load the contents of .htaccess file.
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Using File_HtAccess::load()
<?php
require_once('File/HtAccess.php');
$fh = new File_HtAccess('.htaccess');
$status = $fh->load();
if (PEAR::isError($status)) {
// handle errors
} else {
// continue processing
}
?>
mixed File_HtAccesss:save (
)
Saves the contents of File_HtAccess object as a corresponding .htaccess file on the disc.
mixed
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
Using File_HtAccess::save()
<?php
require_once('File/HtAccess.php');
/* create a new .htaccess file with given parameters */
$params['authname'] = 'Private';
$params['authtype'] = 'Basic';
$params['authuserfile'] = '/path/to/.htpasswd';
$params['authgroupfile'] = '/path/to/.htgroup';
$params['require'] = array('group', 'admins');
$fh = new File_HtAccess('.htaccess', $params);
$status = $fh->save();
if (PEAR::isError($status)) {
// handle errors
} else {
// continue processing
}
?>
void File_HtAccess::setRequire (
mixed $require
)
Sets the value of require property.This overwrites the previous value. If you need to add a value (user) to require use File_HtAccess::addRequire() instead. Using this method you can control which users will be able to access the protected resources.
mixed $require
- value the requre property
to be set to. Can be given as a string or an array. If given
as a string separate multiple values with a space.
void
This function can not be called statically.
Using File_HtAccess::setRequire()
<?php
require_once('File/HtAccess.php');
/* let any valid user access the resource */
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->setRequire('valid-user');
$fh->save();
/* let user tuupola or panula access the resource */
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->setRequire(array('user', 'tuupola', 'panula'););
$fh->save();
/* let anyone from group admins to access the resource */
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->setRequire(array('group', 'admins'););
$fh->save();
?>
void File_HtAccess::addRequire (
string $require
)
Adds a value (user) into require property. Using this method you can control which users will be able to access the protected resources.
string $require
- value (user) to be added
to require property.
void
This function can not be called statically.
Using File_HtAccess::addRequire()
<?php
require_once('File/HtAccess.php');
/* add user tuupola to list of users to be granted access */
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->addRequire('tuupola');
$fh->save();
?>
void File_HtAccess::delRequire (
string $require
)
Remove a value (user) from require property. Using this method you can control which users will be able to access the protected resources.
string $require
- value (user) to be added
to require property.
void
This function can not be called statically.
Using File_HtAccess::delRequire()
<?php
require_once('File/HtAccess.php');
/* remove user viemero from list of users to be granted access */
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->delRequire('viemero');
$fh->save();
?>
array File_HtAccess::getRequire (
)
Get the value(s) of require property as an array. Require property contains the usernames or groups users who are allowed to access protected resources. Value valid-user means all users listed in password file are allowed to access.
string $type
- if 'string' return value
will be a string with usernames separated by a space character.
Otherwise return value will be an array(). Defaults to an array.
mixed string or array depending on $type parameter.
This function can not be called statically.
Using File_HtAccess::getrequire()
<?php
require_once('File/HtAccess.php');
/* add user tuupola and viemero to list of users to be granted access */
$fh = new File_HtAccess('.htaccess');
$fh->addRequire('tuupola');
$fh->addRequire('viemero');
$require1 = $fh->getRequire();
$require2 = $fh->getRequire('string');
print_r($require1);
/* Array */
/* ( */
/* [0] => tuupola */
/* [1] => viemero */
/* ) */
print_r($require2);
/* tuupola viemero */
?>
void File_HtAccess::setProperties (
array $params
)
Set the values of objects properties as defined by hash given as a parameter. You can use this method as an alternative to passing property values in constructor .
$params['authname']
- authname
$params['authtype']
- authtype
$params['authuserfile']
- authuserfile
$params['authgroupfile']
- authgroupfile
$params['require']
- require
$params['additional']
- additional
void
This function can not be called statically.
Using File_HtAccess::setProperties()
<?php
require_once('File/HtAccess.php');
/* let any valid user access the resource */
$fh = new File_HtAccess('.htaccess');
$params['authname'] = 'Private';
$params['authtype'] = 'Basic';
$params['authuserfile'] = '/path/to/.htpasswd';
$params['authgroupfile'] = '/path/to/.htgroup';
$params['require'] = array('group', 'admins');
$fh->setProperties($params);
?>
void File_HtAccess::setAuthUserFile (
string $file
)
Sets the value of authuserfile property. AuthUserFile is the password file which contains username:password pairs for Basic authentication. You must give full path to the password file in order for it to work.
string $file
- absolute pathname to the
password file.
void
This function can not be called statically.
Using File_HtAccess::setAuthUserFile()
<?php
require_once('File/HtAccess.php');
/* set the password file to /etc/htpasswd */
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->setAuthUserFile('/etc/htpasswd');
$fh->save();
/* set the password file to .htpasswd in current working directory */
$file = getcwd() . '/.htpasswd';
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->setAuthUserFile($file);
$fh->save();
?>
void File_HtAccess::setAuthGroupFile (
string $file
)
Sets the value of authgroupfile property. AuthGroupFile a file containing names of the groups and usernames belonging to the group. You must give full path to the group file in order for it to work.
string $file
- absolute pathname to the
group file.
void
This function can not be called statically.
Using File_HtAccess::setauthgroupfile()
<?php
require_once('File/HtAccess.php');
/* set the group file to /etc/htgroup */
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->setAuthGroupFile('/etc/htgroup');
$fh->save();
?>
void File_HtAccess::setAuthType (
string $type='Basic'
)
Sets the value of authtype property. Almost allways you will be using Basic authentication. Since most browsers don't yet support Digest authentication you should only use Digest if you can control what browsers will be accessing the resources.
string $type
- authentication type to
use. Should be Basic os Digest. Defaults to Basic.
void
This function can not be called statically.
Using File_HtAccess::setAuthType()
<?php
require_once('File/HtAccess.php');
/* use Digest authentication */
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->setAuthType('Digest');
$fh->save();
?>
void File_HtAccess::setAuthDigestFile (
string $file
)
Sets the value of authdigestfile property. AuthDigestFile is the password file which contains username:realm:password pairs for Digest authentication. You must give full path to the password file in order for it to work.
string $file
- absolute pathname to the
password file.
void
This function can not be called statically.
Using File_HtAccess::setauthdigestfile()
<?php
require_once('File/HtAccess.php');
/* set the password file to /etc/htdigest */
$fh = new File_HtAccess('.htaccess');
$fh->load();
$fh->setauthdigestfile('/etc/htdigest');
$fh->save();
?>
Provides useful packages for the PHP-Gtk extension.
Setting up GtkWidgets to accept files via Drag'n'Drop in a very easy way.
Gtk_FileDrop provides an easy interface to set up GtkWidgets to receive files via Drag'n'Drop. Widgets can be told to accept a number of MIME-Types as well as a number of file extensions. Values of widgets can automatically be set when drops with valid files occur, or a callback function can be specified which is invoked in this case.
Now one may think that the whole setup process is really simple and would not need a class like this to make widgets accept files. That is right. The setup for making widgets accept drops is an ease - but there are two more steps to do:
The first item is not so hard to implement; we do this by either
checking the file extension, or using the MIME_Type package to
get the mime type. (Note: *
is supported).
The second item is the main problem as all applications seem to ignore the standard for file name exchange and cook their own soup (so using their own format) when passing the file names. This package knows the differences and converts the dropped strings into usable local filenames, regardless of the format the source application uses. For a deeper insight in this topic you should read the PHP-Gtk FileDrop tutorial.
A note about Mozilla/Firefox: Mozilla provides file names only with mime typetext/plain
only instead oftext/uri-list
. If we would accept this type as well, the problem arises that every text could be dropped - if it is a file or not. That's why the decision was made not to accept drops from Mozilla.
By default, the dropped files are displayed in the widget,
e.g. if you drop a file over a GtkEntry
, the
text value of the widget will be overwritten to the filename.
As not all widgets have such simple values, Gtk_FileDrop has built-in support for a number of widget classes and acts different on different types.
Widget class | Action taken |
---|---|
GtkEntry | Entry value is replaced with first file |
GtkLabel | Label text is replaced with first file |
GtkButton | Label text is replaced with first file if the first and only child is a GtkLabel |
GtkToggleButton | Label text is replaced with first file if the first and only child is a GtkLabel |
GtkRadioButton | Label text is replaced with first file if the first and only child is a GtkLabel |
GtkCheckButton | Label text is replaced with first file if the first and only child is a GtkLabel |
GtkCombo | The entry text is replaced with the first file. The list is not affected. |
GtkFileSelection | File name of the file dialog is set to the first dropped file. The directory is changed to the directory name of the file, and the file name (without directory) is set in the file name entry. |
GtkList | Every accepted file is appended at the end of the list. |
Beside the automatic actions, you can specify a callback as the third
parameter of the attach
function which is called
with the widget itself and an array of accepted files as parameters.
General setup
<?php
$accepted_types = array(
'text/html',
'text/plain',
'.dat'
);
Gtk_FileDrop::attach($widget, $accepted_types);
?>
Here we set up the array of accepted file types:
The widget shall accept files with MIME-Type
text/html
, text/plain
and
files ending with .dat
.
After this we set up the $widget
to accept this
files.
Accepting directories
<?php
$accepted_types = array(
'inode/directory',
);
?>
The MIME type inode/directory
has to be used if
the widget shall accept directories.
When a file is dropped over a widget which accepts directories only, the directory of the filename is used.
Setup with callback
<?php
function filesDropped($widget, $arFiles) {
foreach($arFiles as $strFile) {
echo "The file \"$strFile\" has been dropped\r\n";
}
}
$accepted_types = array(
'text/html',
'text/plain',
'.dat'
);
Gtk_FileDrop::attach($widget, $accepted_types, 'filesDropped', false);
?>
In this example we use a callback function to print out all
dropped files to the console. Further we determine the fourth
parameter as false
which means that the widget's
value is not changed automatically.
To use the callback with objects, you pass an array with the reference of the object:
Using a callback
<?php
Gtk_FileDrop::attach($widget, $accepted_types, array( &$this, 'filesDropped'));
?>
Working example
<?php
if (!extension_loaded('gtk')) {
dl('php_gtk.' . PHP_SHLIB_SUFFIX);
}
require_once('Gtk/FileDrop.php');
$window = &new GtkWindow();
$window->set_default_size(300, 30);
$window->connect_object('destroy', array('gtk', 'main_quit'));
$window->show();
$entry =& new GtkEntry();
$window->add($entry);
$entry->set_text('drop text files on me');
Gtk_FileDrop::attach($entry, array('text/plain'));
$window->show_all();
gtk::main();
?>
A class that creates a scrolling marquee.
This package has been superseded. Please use Gtk2_ScrollingLabel for new projects.
Gtk_ScrollingLabel is a class that creates a pseudo-widget which makes scrolling text within a label easy. The text within the label can be set to scroll from left to right, right to left, or bounce within the boundries of the label. The speed of the scrolling text can also be controlled. There are also built in methods to make the text pause or stop when the user moves the mouse over the label and start up again when the mouse leaves the label space.
Getting started with Gtk_ScrollingLabel is easy. All you need to do is instantiate the class, add it to a window, and start the scrolling. Take at look at the example below.
Simple Setup
<?php
// Create a Gtk_ScrollingLabel instance.
require_once 'Gtk/ScrollingLabel.php';
$sLabel =& new Gtk_ScrollingLabel('Scrolling Labels Rule!');
// Create a GtkWindow to put the label in.
$gWin =& new GtkWindow;
$gWin->connect_object('destroy', array('gtk', 'main_quit'));
$gWin->add($sLabel->getWidget());
// Show everything.
$gwin->show_all();
// Start the label and the main loop.
$sLabel->startScroll();
gtk::main();
?>
string Gtk_ScrollingLabel::getFullText (
)
Returns the full text of the label. The full text is the text that was set on construction or with setFullText() not just what is currently visible.
returns the full text of the label.
This function can not be called statically.
void Gtk_ScrollingLabel::getScrollingLabel (
)
Returns the GtkEventBox that contains the scrolling label.
returns the GtkEventBox containing the scrolling label.
This function can not be called statically.
&object Gtk_ScrollingLabel::getStyle (
)
Returns the label's style widget. This lets you make changes to the style to alter the look and feel of the widget. You can change the font of the display, the color of the background, etc.
returns the GtkStyle widget for the label.
This function can not be called statically.
int Gtk_ScrollingLabel::getVisibleLength (
)
Returns the length of the visible area in characters. The length of the visible area is the maximum number of characters that are visible at one time.
integer the maximum number of characters that are visible at one time.
setVisibleLength()
This function can not be called statically.
void Gtk_ScrollingLabel::pause (
)
Stops the label from scrolling but does not reset the label text to the begining. If you want the label to stop scrolling and return to its original position (blank), use stopScroll().
returns true
if the scrolling has
stopped.
This function can not be called statically.
boolean Gtk_ScrollingLabel::setBounce (
boolean $bounce
)
If $bounce
is true
, the text
will scroll to the left until the first character hits the left edge of
the visible window then it will scroll back to the right. When the last
character hits the right edge of the visible window the text will scroll
back to the left again.
If all of the scrolling text cannot be viewed at once, making the text bounce will have an undesired result. Therefore it is suggested that the length of the text always be at least one character less than the maximum viewable string length.
boolean $bounce
-
Whether or not the text should change direction when it hits a label
edge.
returns Whether or not the text is set to bounce
This function can not be called statically.
void Gtk_ScrollingLabel::setStartSignal (
string $signal
)
setStartSignal() will make the event box listen for the
signal $signal
and will envoke
startScroll() when ever it is heard. The signal should
be a signal that GtkEventBox listens for normally
or a button press event.
string $signal
-
The Gtk signal to connect to startScroll().
returns The handler id for the connection.
This function can not be called statically.
boolean Gtk_ScrollingLabel::startScroll (
)
startScroll() will make the text begin scrolling in the direction given by calling setDirection(). If the text is scrolling left to right (GTK_SCROLLINGLABEL_RIGHT), the current character position is set to the end of the text. This means that the last character of the label will be the first character shown. If the text is scrolling right to left (GTK_SCROLLINGLABEL_LEFT), the current character position is set to zero. This means that first character shown will be a blank space.
This method is best used when connected to an event.
returns true
if the text has
started scrolling. false
otherwise.
This function can not be called statically.
void Gtk_ScrollingLabel::unPause (
)
Begins scrolling the label from its current position. unPause() is different from startScroll() in that it does not reset the label to the begining of the text.
returns true
if the text has
started scrolling.
This function can not be called statically.
Provides a simple GUI to examine PHP variables, for example complex arrays or objects.
The GUI consists of two parts: a tree on the left side, displaying the values/objects/arrays that have subkeys/subvalues, and a list with scalar key-value pairs on the right side.
To use it, just include Gtk/VarDump.php
and
call the constructor with the variable to be dumped.
Inspecting a variable with Gtk_VarDump
<?php
//some test class to dump
class Test
{
var $color = 'blue';
var $foo = 'bar';
var $self = null;
var $server = null;
function Test()
{
$this->self =& $this;
$this->server =& $_SERVER;
}
function doNothing()
{
//we're doing nothing here
}
}
//include & display the variable
require_once 'Gtk/VarDump.php';
new Gtk_VarDump(new Test());
?>
Provides useful packages for the PHP-Gtk2 extension.
The package provides a simple dialogbox for text input.
Gtk2_EntryDialog provides a dialog with a message and one single text input field, and some buttons like OK and Cancel. The class tries to make it as easy as possible to get the text from the user with only a few lines of code, while keeping the full power of the GtkDialog class.
The most easy way to retrieve the text with one line of code is using the get() static method.
void constructor
Gtk2_EntryDialog::__construct
(
GtkWindow $parent
,
GtkDialogFlags $flags
,
GtkMessageType $type
,
GtkButtonsType $buttons
,
string $message
,
string $default
)
Creates a new entry dialog instance. After creating it, you can modify it and finally use the run() to show it and await the input.
You might want to use a more simple constructor, e.g. get() or new_simple()
<?php
require_once 'Gtk2/EntryDialog.php';
$id = new Gtk2_EntryDialog(
null, //parent window
0, //flags (GtkDialogFlags)
Gtk::MESSAGE_QUESTION, //type of message
Gtk::BUTTONS_OK_CANCEL, //which buttons shall be there
'What\'s your name?', //the message
'Don\'t know' //The default text
);
$answer = $id->run();
$id->destroy();
if ($answer == Gtk::RESPONSE_OK) {
echo 'The name is: ';
var_dump($id->get_text());
} else {
echo "You cancelled\r\n";
}
?>
$parent
Parent window (can be null).
$flags
Dialog flags (use 0 as default)
$type
Message type (e.g. Gtk::MESSAGE_QUESTION)
$buttons
Buttons to show (e.g. Gtk::BUTTONS_OK)
$message
Message to display.
$default
Default text for the entry.
Gtk2_EntryDialog
Gtk2_EntryDialog::new_simple
(
string $message
,
string $default = null
,
GtkWindow $parent = null
)
Simplified constructor with not so much parameters.
Message type is Gtk::MESSAGE_QUESTION, the flags will be Gtk::DIALOG_MODAL if the parent is set. Only one button, OK, will be visible.
<?php
require_once 'Gtk2/EntryDialog.php';
$id = Gtk2_EntryDialog::new_simple(
'What\'s your name?', //the message
'Don\'t know' //The default text
);
$answer = $id->run();
$id->destroy();
if ($answer == Gtk::RESPONSE_OK) {
echo 'The name is: ';
var_dump($id->get_text());
} else {
echo "You cancelled\r\n";
}
?>
$message
Message to display.
$default
Default text for the entry.
$parent
Parent window (can be null).
string
Gtk2_EntryDialog::get
(
string $message
,
string $default
,
GtkWindow $parent
)
Creates a dialog with the given parameters, runs it, and returns the text set. If the user cancelled the dialog, this method returns FALSE. In any other case (even when the text is empty), the a string with the text is returned.
<?php
require_once 'Gtk2/EntryDialog.php';
$text = Gtk2_EntryDialog::get(
'What\'s your name?', //the message
'Don\'t know' //The default text
);
if ($text !== false) {
echo 'The name is: ';
var_dump($text);
} else {
echo "You cancelled\r\n";
}
?>
$message
Message to display.
$default
Default text for the entry.
$parent
Parent window (can be null).
returns False if the user cancelled input, the text (string) otherwise.
int
Gtk2_EntryDialog::run
(
)
Show the dialog and block until a button has been pressed.
returns The response id of the pressed button.
string
Gtk2_EntryDialog::get_text
(
)
Retrieves the text from the entry.
returns The input from the user.
string
Gtk2_EntryDialog::set_text
(
string $text
)
Sets the text for the entry.
void
Gtk2_EntryDialog::set_default_response
(
int
$response_id
)
Set the default response.
The button with the id will be the default one, allowing you to just press return to activate it.
$response_id
Response code
Catches and displays PHP errors, notices and warnings as well as PEAR Errors and uncaught exceptions.
Gtk2_ExceptionDump catches and displays common errors and warnings that can be produced in a PHP scripts: PHP errors, notices and warnings as well as PEAR Errors and uncaught exceptions. They are displayed in a Gtk2 window with a tree view, providing the error message and the backtrace with passed parameters.
The most easy thing to do is letting the class handle everything automatically. You can decide which errors should be catched: All, PHP errors, PEAR errors or uncaught exceptions. The following methods need to be called statically to set up error handling:
setupExceptionHandler()
setupPearErrorHandler()
setupPhpErrorHandler()
setupAllHandlers()
Letting Gtk2_ExceptionDump handle all errors
<?php
require_once 'Gtk2/ExceptionDump.php';
Gtk2_ExceptionDump::setupAllHandlers();
?>
If you don't want to let Gtk2_ExceptionDump handle errors automatically, you can display the window by hand, e.g. in a catch() statement.
Catching exceptions by hand
<?php
try {
//do some stuff that
//could throw an exception
} catch (Exception $e) {
require_once 'Gtk2/ExceptionDump.php';
Gtk2_ExceptionDump::display($e);
}
?>
As usually, the package has more examples in the
doc directory:
/path/to/pear/docs/Gtk2_ExceptionDump/examples/
.
You can make use of the brain power that went into the development of Gtk2_ExceptionDump by embedding the stack trace treeview into your own application.
There class you want is Gtk2_ExceptionDump_Stack. It's a subclass of GtkTreeView and should be embedded in a GtkScrolledWindow if you use it. The constructor takes the exception object, but it can be omitted and set later by using setException().
Setting up GtkWidgets to accept files via Drag'n'Drop in a very easy way.
Gtk2_FileDrop provides an easy interface to set up GtkWidgets to receive files via Drag'n'Drop. Widgets can be told to accept a number of MIME-Types as well as a number of file extensions. Values of widgets can automatically be set when drops with valid files occur, or a callback function can be specified which is invoked in this case.
Now one may think that the whole setup process is really simple and would not need a class like this to make widgets accept files. That is right. The setup for making widgets accept drops is an ease - but there are two more steps to do:
The first item is not so hard to implement; we do this by either
checking the file extension, or using the MIME_Type package to
get the mime type. (Note: *
is supported).
The second item is the main problem as all applications seem to ignore the standard for file name exchange and cook their own soup (so using their own format) when passing the file names. This package knows the differences and converts the dropped strings into usable local filenames, regardless of the format the source application uses. For a deeper insight in this topic you should read the PHP-Gtk FileDrop tutorial.
A note about Mozilla/Firefox: Mozilla provides file names only with mime typetext/plain
only instead oftext/uri-list
. If we would accept this type as well, the problem arises that every text could be dropped - if it is a file or not. That's why the decision was made not to accept drops from Mozilla.
Gtk2_FileDrop is the port of Gtk_FileDrop to PHP-Gtk2 and PHP5. Thus, it has identical methods and behaves the same.
By default, the dropped files are displayed in the widget,
e.g. if you drop a file over a GtkEntry
, the
text value of the widget will be overwritten to the filename.
As not all widgets have such simple values, Gtk2_FileDrop has built-in support for a number of widget classes and acts different on different types.
Widget class | Action taken |
---|---|
GtkEntry | Entry value is replaced with first file |
GtkLabel | Label text is replaced with first file |
GtkButton | Label text is replaced with first file if the first and only child is a GtkLabel |
GtkToggleButton | Label text is replaced with first file if the first and only child is a GtkLabel |
GtkRadioButton | Label text is replaced with first file if the first and only child is a GtkLabel |
GtkCheckButton | Label text is replaced with first file if the first and only child is a GtkLabel |
GtkCombo | The entry text is replaced with the first file. The list is not affected. |
GtkFileSelection | File name of the file dialog is set to the first dropped file. The directory is changed to the directory name of the file, and the file name (without directory) is set in the file name entry. |
GtkList | Every accepted file is appended at the end of the list. |
Beside the automatic actions, you can specify a callback as the third
parameter of the attach
function which is called
with the widget itself and an array of accepted files as parameters.
General setup
<?php
$accepted_types = array(
'text/html',
'text/plain',
'.dat'
);
Gtk2_FileDrop::attach($widget, $accepted_types);
?>
Here we set up the array of accepted file types:
The widget shall accept files with MIME-Type
text/html
, text/plain
and
files ending with .dat
.
After this we set up the $widget
to accept this
files.
Accepting directories
<?php
$accepted_types = array(
'inode/directory',
);
?>
The MIME type inode/directory
has to be used if
the widget shall accept directories.
When a file is dropped over a widget which accepts directories only, the directory of the filename is used.
Setup with callback
<?php
function filesDropped($widget, $arFiles) {
foreach($arFiles as $strFile) {
echo "The file \"$strFile\" has been dropped\r\n";
}
}
$accepted_types = array(
'text/html',
'text/plain',
'.dat'
);
Gtk2_FileDrop::attach($widget, $accepted_types, 'filesDropped', false);
?>
In this example we use a callback function to print out all
dropped files to the console. Further we determine the fourth
parameter as false
which means that the widget's
value is not changed automatically.
To use the callback with objects, you pass an array with the reference of the object:
Using a callback
<?php
Gtk2_FileDrop::attach($widget, $accepted_types, array( &$this, 'filesDropped'));
?>
Working example
<?php
require_once 'Gtk2/FileDrop.php';
$window = new GtkWindow();
$window->set_default_size(300, 30);
$window->connect_simple('destroy', array('gtk', 'main_quit'));
$window->show();
$entry = new GtkEntry();
$window->add($entry);
$entry->set_text('drop text files on me');
Gtk2_FileDrop::attach($entry, array('text/plain'));
$window->show_all();
Gtk::main();
?>
Gtk2_IndexedComboBox is a Gtk2 widget similar
to the HTML select
box
- it lets you not only store values as the normal
GtkComboBox, but associated key/value pairs.
Gtk2_IndexedComboBox is meant to be as easy to use as possible, and to have an API similar to the combo boxes created with GtkComboBox::new_text(). You can directly add key/value pairs to the widget instead of using the model (which is possible, too). Retrieving the currently selected key can be done with a single method call.
Both keys and values can be strings, thus allowing integers implicitly. This is very useful if you need a widget to select some row ID from a database, but want to display some descriptive string. By default, a single GtkCellRendererText is used as cell renderer.
In this example, we will create a combo box, add an array of key/value pairs and display the currently selected pair in a label.
Simple Gtk2_IndexedComboBox example
<?php
require_once 'Gtk2/IndexedComboBox.php';
//display this data
$arData = array(
1 => 'Germany',
2 => 'United Kingdom',
3 => 'Spain',
4 => 'France'
);
//Create combo and set the array
$combo = new Gtk2_IndexedComboBox();
$combo->set_array($arData);
//make #2 active
$combo->set_active_key(2);
//trace changes
$combo->connect('changed', 'comboChanged', $lbl);
function comboChanged($combo, $lbl)
{
$lbl->set_text(
"Combo changed:\r\n"
. 'Key: ' . $combo->get_active_key() . "\r\n"
. 'Value: ' . $combo->get_active_text()
);
}
//some label to display changes
$lbl = new GtkLabel("Status\r\n\r\n");
//standard stuff
$vbox = new GtkVBox();
$vbox->pack_start($combo);
$vbox->pack_start($lbl);
$wnd = new GtkWindow();
$wnd->connect_simple('destroy', array('Gtk', 'main_quit'));
$wnd->add($vbox);
$wnd->show_all();
Gtk::main();
?>
At first, we create a new combo widget object. You already could
pass the array of data in the constructor if you wanted to, but
here we use the set_array() method to do this.
After providing the data, entry with id 2
is made
active/pre-selected.
Whenever the selection changes, the example displays the selected key and value in a label below the combo box. We use get_active_key() to retrieve the key, and get_active_text() to retrieve the value.
While designing your user interface with Glade is really convenient and easy, it doesn't allow you to use custom classes as this combo here. Thanks to the MVC model in Gtk2, you still can use this class's power by using the same model as Gtk2_IndexedComboBox uses internally: Gtk2_IndexedComboBox_Model.
Glade file to use
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> <!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> <glade-interface> <widget class="GtkWindow" id="wndTest"> <property name="visible">True</property> <property name="title" translatable="yes">Gtk2_IndexedComboBox_Model test</property> <property name="type">GTK_WINDOW_TOPLEVEL</property> <property name="window_position">GTK_WIN_POS_NONE</property> <property name="modal">False</property> <property name="resizable">True</property> <property name="destroy_with_parent">False</property> <property name="decorated">True</property> <property name="skip_taskbar_hint">False</property> <property name="skip_pager_hint">False</property> <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> <signal name="destroy" handler="Gtk::main_quit"/> <child> <widget class="GtkComboBox" id="cmbNormal"> <property name="visible">True</property> </widget> </child> </widget> </glade-interface>
PHP code
The important thing to remember is that you need to setup your own cell renderer, and tell it which column in the model shall be displayed. Beside that, you just do the normal model-creation-and-setting via set_model().
All data manipulation methods of Gtk2_IndexedComboBox
you saw in the previous example are available in the model,
Gtk2_IndexedComboBox_Model.
Only get_active_key() and
get_active_text() cannot be used on the model,
since this doesn't know anything about selections. Retrieve
the selected GtkTreeIter by combining the
calls of get_active() and get_iter(),
and pass this iter
object to
get_key() and get_text().
Gtk2_IndexedComboBox automatically creates the model and sets up the cell renderer when instantiating the widget. You may pass an array of key/value pairs to the constructor if you like to, but also can set them later.
The currently selected (active) key can be retrieved via get_active_key(), the selected (active) value by using get_active_text(). You can preselect a certain key with set_active_key().
If you don't like the renderer or want to change it or its attributes, use get_cell_renderer() and set_cell_renderer().
All other methods to retrieve/store data are convenience methods that act as proxy to the model. For example, Gtk2_IndexedComboBox' method set_array() internally calls the same method on the model, Gtk2_IndexedComboBox_Model. This allows you to quickly add some data to the combo without getting the model first and acting on it.
The model class is the data storage for your key/value pairs.
It contains two columns, both are of type
Gtk::TYPE_STRING
and can hold text and numbers.
The first is used for the keys, the values are stored in the
second column.
The class has several method that allow you to store data in the model: The classis set_array() takes an associative array of key/value pairs. Once set, you can use append_array() and prepend_array() to add new data after or before the current values. You also have the possibility to use insert_array() that takes the position as first, and the data array as second parameter.
Beside the *_array
methods, you can add
single key/value pairs by using
append() and prepend()
which take the key as first, and the value as second parameter.
insert() wants an additional position number
as first parameter, and key/value as second and third.
To delete data from the model, use remove_key(). After finishing your data manipulation, you can retrieve the full associative array of key/value pairs via get_array().
To obtain the key or value of a certain GtkTreeIter (that you got e.g. from the model by using get_iter()), use get_key() and get_text().
Provides a simple GUI to enable the user to edit php.ini values.
This package will be useful for PHP-GTK 2 based applications which require the user to make changes to php.ini. The class can parse any existing ini file, or create new ones. Parsing of existing files is currently a little inefficient, suggestions for improvement are welcome. New files and files previously generated by Gtk2_PHPConfig are parsed at a good speed though.
The interface displays all available configuration sections on the left pane, and their corresponding options in the top right pane. The bottom right pane displays the description of the option selected and the facility to change the value of that option.
Simple Configuration GUI
<?php
require_once("Gtk2/PHPConfig.php");
switch ($argc) {
case 1:
$test = new Gtk2_PHPConfig(NULL);
Gtk::main();
break;
case 2:
if (file_exists($argv[1])) {
echo "\nThe INI File is being parsed... Please be patient...\n";
$test = new Gtk2_PHPConfig($argv[1]);
Gtk::main();
break;
} else {
echo "\nThe file does not exist!\n";
}
default:
echo "\nUsage: gtk2_phpconfig [INI File]\n";
echo "Where [INI File] is an optional argument, and the name of the
file you want to edit.";
echo "\nIf no arguments are passed, a new INI file is created\n\n";
exit();
}
?>
This example is actually the gtk2_phpconfig executable found in
/usr/bin
(for *nix systems) after installation of
the Gtk2_PHPConfig package.
The example script accepts a single optional argument, i.e. the location of the ini file to edit. If not provided, a new file will be created.
Gtk2_PHPConfig extends GtkWindow and can be treated as an application by itself. Hence, the methods used internally are of no significance to the end user. However, you may refer to the API documentation if you are interested in customization or just understanding how the package works.
A class that creates a scrolling marquee.
Gtk2_ScrollingLabel is a class that creates a pseudo-widget which makes scrolling text within a label easy. The text within the label can be set to scroll from left to right, right to left, or bounce within the boundries of the label. The speed of the scrolling text can also be controlled. There are also built in methods to make the text pause or stop when the user moves the mouse over the label and start up again when the mouse leaves the label space.
Getting started with Gtk2_ScrollingLabel is easy. All you need to do is instantiate the class, add it to a window, and start the scrolling. Take at look at the example below.
Simple Setup
<?php
// Create a Gtk2_ScrollingLabel instance.
require_once 'Gtk2/ScrollingLabel.php';
$sLabel = new Gtk2_ScrollingLabel('Scrolling Labels Rule!');
// Create a GtkWindow to put the label in.
$gWin = new GtkWindow;
$gWin->connect_simple('destroy', array('Gtk', 'main_quit'));
$gWin->add($sLabel->getWidget());
// Show everything.
$gwin->show_all();
// Start the label and the main loop.
$sLabel->startScroll();
gtk::main();
?>
void Gtk2_SrollingLabel (
string $text = ''
)
Class constructor
string $text
-
The text to scroll
This function can not be called statically.
string Gtk2_ScrollingLabel::getFullText (
)
Returns the full text of the label. The full text is the text that was set on construction or with setFullText() not just what is currently visible.
returns the full text of the label.
This function can not be called statically.
void Gtk2_ScrollingLabel::getScrollingLabel (
)
Returns the GtkEventBox that contains the scrolling label.
returns the GtkEventBox containing the scrolling label.
This function can not be called statically.
&object Gtk2_ScrollingLabel::getStyle (
)
Returns the label's style widget. This lets you make changes to the style to alter the look and feel of the widget. You can change the font of the display, the color of the background, etc.
returns the GtkStyle widget for the label.
This function can not be called statically.
int Gtk2_ScrollingLabel::getVisibleLength (
)
Returns the length of the visible area in characters. The length of the visible area is the maximum number of characters that are visible at one time.
integer the maximum number of characters that are visible at one time.
setVisibleLength()
This function can not be called statically.
void Gtk2_ScrollingLabel::pause (
)
Stops the label from scrolling but does not reset the label text to the begining. If you want the label to stop scrolling and return to its original position (blank), use stopScroll().
returns true
if the scrolling has
stopped.
This function can not be called statically.
boolean Gtk2_ScrollingLabel::setBounce (
boolean $bounce
)
If $bounce
is true
, the text
will scroll to the left until the first character hits the left edge of
the visible window then it will scroll back to the right. When the last
character hits the right edge of the visible window the text will scroll
back to the left again.
If all of the scrolling text cannot be viewed at once, making the text bounce will have an undesired result. Therefore it is suggested that the length of the text always be at least one character less than the maximum viewable string length.
boolean $bounce
-
Whether or not the text should change direction when it hits a label
edge.
returns Whether or not the text is set to bounce
This function can not be called statically.
void Gtk2_ScrollingLabel::setStartSignal (
string $signal
)
setStartSignal() will make the event box listen for the
signal $signal
and will envoke
startScroll() when ever it is heard. The signal should
be a signal that GtkEventBox listens for normally
or a button press event.
string $signal
-
The Gtk signal to connect to startScroll().
returns The handler id for the connection.
This function can not be called statically.
boolean Gtk2_ScrollingLabel::startScroll (
)
startScroll() will make the text begin scrolling in the direction given by calling setDirection(). If the text is scrolling left to right (GTK2_SCROLLINGLABEL_RIGHT), the current character position is set to the end of the text. This means that the last character of the label will be the first character shown. If the text is scrolling right to left (GTK2_SCROLLINGLABEL_LEFT), the current character position is set to zero. This means that first character shown will be a blank space.
This method is best used when connected to an event.
returns true
if the text has
started scrolling. false
otherwise.
This function can not be called statically.
void Gtk2_ScrollingLabel::unPause (
)
Begins scrolling the label from its current position. unPause() is different from startScroll() in that it does not reset the label to the begining of the text.
returns true
if the text has
started scrolling.
This function can not be called statically.
Provides a simple GUI to examine PHP variables, for example complex arrays or objects.
Gtk2_VarDump is a simple GUI to examine PHP variables, for example complex arrays or objects. It displays the variable and all sub-variables/array keys/sub-objects in the left pane (a GtkTreeView), and the actual values in a list view on the right side.
The class is very easy to use: Just include the file, and call the static display method with the variable you want to have dumped.
Showing a variable with Gtk2_VarDump
<?php
//include the file
require_once 'Gtk2/VarDump.php';
$myvar = array(
'hello', 2 => 'world',
array(1, 2, 3, 'blubb')
);
//graphically dump the variable in an own window
Gtk2_VarDump::display($myvar, 'variable title');
?>
Gtk2_VarDump consists of two main parts: A tree on the left, displaying the structure of the variable-to-dump, and a list on the right side, showing the keys and their values. They are arranged on a pane, which itself is contained in the main window.
Now you might want to dump some variables yourself, but integrate this display directly into your own application - without opening a new window for that. Gtk2_VarDump is prepared for that; you can re-use the existing classes.
Gtk2_VarDump_Pane is the container for both tree and list. You can use instantiate it, add it to your interface and call the setVariable() method to display the contents of the variable. The method takes the variable as first parameter, and a user-definable title as optional second parameter.
Gtk2_VarDump_Tree is an descendant of GtkTreeView, and as usual without any scrollbars. Remember to add it to a GtkScrolledWindow. The class has, as Gtk2_VarDump_Pane does, a setVariable() method that takes the variable to be displayed and an optional title that is used for the root element.
The tree has another method: setList() is used to set the list widget that shall display the values. Be sure that the list widget also provides a setVariable() method. Whenever the selection in the tree changes, the list's setVariable() method is called.
Gtk2_VarDump_List is a descendant of GtkTreeView and simply displays plain key-value pairs. It has, like the other two classes, a setVariable() method that takes the variable to display as first parameter, and an optional title as second parameter.
Provides packages for working and creating HTML
The HTML_Common2 package provides methods for HTML attributes handling and setting document-wide options. It is quite helpful as a building block for packages generating HTML and is currently used as such by HTML_QuickForm2 package. Main features:
Note that HTML_Common2 is an abstract class, so you would probably subclass it and use a instance of a child class rather than HTML_Common2 itself. One notable exception is using its static methods to get and set global document options.
HTML_Common2 class is intended as a parent class for classes representing HTML elements and its main purpose is to allow easy attribute handling for instances of these child classes.
Inividual attribute values can be set by HTML_Common2::setAttribute() and read by HTML_Common2::getAttribute() methods. Use HTML_Common2::removeAttribute() to remove an attribute, calling setAttribute() without explicitly giving a new attribute value serves a different purpose:
<?php
// these calls are identical
$html->setAttribute('checked');
$html->setAttribute('checked', 'checked');
?>
You can completely replace attributes of HTML_Common2 instance by using HTML_Common2::setAttributes() method and add/replace several new attributes at once by using HTML_Common2::mergeAttributes(). By default constructor of HTML_Common2 calls mergeAttributes(), so that some default attributes can be provided by a subclass and only overridden if needed. Note that both of the above methods can accept either a string of HTML attributes or an array of these.
Finally, HTML_Common2::getAttributes() method returns the values of all the instance's attributes. Those can be returned either as an associative array or a string. As the package is intended for XHTML-compliant output, attribute names will always be lowercased and quotes will be used around attribute values when outputting the attribute string.
Since release 2.1.0 HTML_Common2 implements ArrayAccess interface, allowing access to attributes using array notation:
Accessing attributes as array keys
<?php
if (!isset($html['id'])) {
// equivalent to $html->setAttribute('id', 'foo');
$html['id'] = 'foo';
}
// equivalent to $html->setAttribute('checked');
$html[] = 'checked';
// equivalent to $html->removeAttribute('checked');
unset($html['checked']);
?>
HTML_Common2 contains several methods to easily handle
'class'
attribute of HTML tags: HTML_Common2::addClass(), HTML_Common2::removeClass() and HTML_Common2::hasClass(). Their behaviour should be easily deducable from their
names:
<?php
$html->setAttribute('class', 'foo bar');
$html->removeClass('foo');
if (!$html->hasClass('foo')) {
$html->addClass('notFoo');
}
echo $html->getAttribute('class');
?>
will output
bar notFoo
The following example shows a somewhat minimal subclass of HTML_Common2 and possible ways to change its attributes.
Methods available for attribute handling
<?php
// a non-abstract subclass of HTML_Common2
class HTML_Tag_Foo extends HTML_Common2
{
// some predefined attributes, won't be overwritten
protected $attributes = array('class' => 'pretty');
// basic implementation of magic __toString() method
public function __toString()
{
return '<foo' . $this->getAttributes(true) . ' />';
}
}
$foo = new HTML_Tag_Foo(array('size' => 'small', 'align' => 'top left corner',
'foo' => 'foo value'));
// note how the attributes from this string will be handled
$foo->mergeAttributes("bar LEVEL=0 value='Whatever'");
$foo->removeAttribute('align');
$foo->setAttribute('size', 'smaller');
echo $foo;
?>
The above code will output:
<foo class="pretty" size="smaller" foo="foo value" bar="bar" level="0" value="Whatever" />
HTML_Common2 provides static HTML_Common2::setOption() and HTML_Common2::getOption() methods for defining the document-wide configuration. Predefined options in HTML_Common2 are:
'charset'
'ISO-8859-1'
'indent'
"\11"
'linebreak'
"\12"
It is suggested that child classes of HTML_Common2 use the above parameters when generating HTML.
Note that setOption() and getOption() allow any option names so packages depending on HTML_Common2 may add their own configuration:
<?php
HTML_Common2::setOption('my_option_name', 'My option value');
// ...
if (HTML_Common2::getOption('my_option_name')) {
// do something
}
?>
getOption() will return NULL for an unknown option name, it will return an array of all options and their values if option name is omitted.
HTML_Common2 does not generate any HTML itself, except for a HTML attribute string. However, it provides several methods and configuration parameters that can be used by child classes to format their output.
It is possible to specify indentation level of the current tag via HTML_Common2::setIndentLevel() and HTML comment to output beside tag via HTML_Common2::setComment(). These methods have corresponding getters HTML_Common2::getIndentLevel() and HTML_Common2::getComment().
There is also a protected HTML_Common2::getIndent() method that returns a string to indent the current tag
based on indent level and 'indent'
configuration parameter.
Child classes may take advantage of protected static methods for handling of attributes strings and arrays:
('value' => 'value')
. This
is the preferred method to handle incoming attributes.
It is sometimes necessary either to prevent changing some attribute of a HTML tag (e.g.
type
attribute of <input />
element) or monitor
changes to an attribute to do some additional processing (e.g. on changing element's
id
attribute we should also update some references to that attribute).
HTML_Common2 provides means to do this additional processing in the form
of HTML_Common2::$watchedAttributes
property and HTML_Common2::onAttributeChange() method. When a change of an attribute with name
in $watchedAttributes
array is attempted,
onAttributeChange() is called instead of performing the attempted change. It
is up to the programmer implementing the method to decide what to do with the attribute.
The following code prevents setting type
attribute except via
constructor and to update the value
attribute when name
attribute changes. It also shows how to use methods provided by
HTML_Common2 to format the resultant HTML.
Complex subclass of HTML_Common2
<?php
$_REQUEST = array(
'foo' => 'Foo value',
'bar' => 'Bar value'
);
class HTML_Tag_Input extends HTML_Common2
{
protected $watchedAttributes = array('name', 'type');
public function __construct($type, $name, $attributes = null)
{
$this->attributes['type'] = (string)$type;
$this->setName($name);
parent::__construct($attributes);
}
public function setName($name)
{
$this->attributes['name'] = (string)$name;
if (!empty($_REQUEST[$name])) {
$this->attributes['value'] = $_REQUEST[$name];
}
}
protected function onAttributeChange($name, $value)
{
if ('type' == $name) {
throw new Exception("Attribute 'type' is read-only");
} elseif ('name' == $name) {
if (null === $value) {
throw new Exception("Required attribute 'name' cannot be removed");
}
$this->setName($value);
}
}
public function __toString()
{
return ($this->getComment()
? $this->getIndent() . '<!-- ' . $this->getComment() . ' -->' . HTML_Common2::getOption('linebreak')
: '')
. $this->getIndent() . '<input' . $this->getAttributes(true) . ' />';
}
}
$input = new HTML_Tag_Input('text', 'foo');
echo $input . "\n";
try {
$input->setAttribute('type', 'file');
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}
$input->setAttribute('name', 'bar')
->setIndentLevel(1)
->setComment('Simplified version of HTML_QuickForm2_Element_Input');
echo $input;
?>
The above code will produce the following output:
<input type="text" name="foo" value="Foo value" />
Attribute 'type' is read-only
<!-- Simplified version of HTML_QuickForm2_Element_Input -->
<input type="text" name="bar" value="Bar value" />
The HTML_Crypt provides methods to encrypt text, which can be later be decrypted using JavaScript on the client side.
This is very useful to prevent spam robots collecting email addresses from your site, included is a method to add mailto links to the text being generated.
A basic example to encrypt an email address
<?php
// encrypt 'yourname@emailaddress.com'
// with offset 8
$c = new HTML_Crypt('yourname@emailaddress.com', 8);
// add <a href="mailto:"></a> around this email
$c->addMailTo();
// output the javascript which writes the decrypted email.
$c->output();
?>
The "encryption" function is basically works like ROT13,
with the difference that the $offset
setting
replaces the 13.
It is also limited to ASCII characters between 32 and 127 (included).
Other characters will not be encrypted.
If you don't want to output the generated javascript directly to the browser but use it otherwise or replace a template variable, use getOutput() instead of output() - the first returns the generated javascript, the latter echoes it out.
Provides a simple interface for validate, handle and generate cascading style sheets.
Even if a CSS file is easy to modify (text format), there is no PHP interface that provides such degree of handling style sheet declarations. Create from scratch or parsing single or multi data sources begin really so easy with HTML_CSS.
Mandatory resources :
PHP 4.3.0 or newer.
PEAR 1.5.4 or newer.
PEAR::HTML_Common 1.2.4 or newer.
pcre extension.
Optional resources :
PHP 5.0.0 or newer.
PEAR::Services_W3C_CSSValidator 0.1.0 or newer.
PHPUnit 3.2.0 or newer.
You can download and use it for free. But don't delete the copyright notice. You can read terms of the license.
YES if there is no answer in this Guide and if you are ready to share some informations such as : your configuration (platform Win *nix mac, PHP version, PEAR packages installed) and perharps your script.
You can report it with the bug tracker at PEAR.
CSS (an acronym for Cascading Style Sheets) is a simple mechanism for adding style (e.g. fonts, colors, spacing) to Web documents.
HTML_CSS is a PEAR package that provides methods for handling stylesheet declarations.
Version 1.0.0 does not offers yet methods to validate a style sheet.
PEAR (an acronym for PHP Extension and Application Repository) is a framework and distribution system for reusable PHP components.
Don't forget to read also the PEAR Manual and PEAR FAQ.
Yes it is. First, you need to download package PEAR::HTML_Common version 1.2 or greater.
Extract content (Common.php
file) in same directory
HTML/
as CSS.php
file.
Last, you should create your own error handler, or disable it. See chapter Error Handler for details.
With previous release than 1.1.0 it was not possible because
HTML_CSS::getStyle()
require at least an existing property,
or it will return HTML_CSS_ERROR_NO_ELEMENT_PROPERTY.
With new function HTML_CSS::grepStyle()
of version 1.1.0,
it's now possible.
01: <?php
02: require_once 'HTML/CSS.php';
03:
04: function myErrorHandler()
05: {
06: return PEAR_ERROR_PRINT; // always print all error messages
07: }
08:
09: $styles = '
10: h1, h2, h3, h4 { padding: 1em; }
11: .highlight p, .highlight ul { margin-left: .5em; }
12: #main p, #main ul { padding-top: 1em; }
13: ';
14:
15: $prefs = array(
16: 'push_callback' => 'myErrorHandler',
17: );
18: $attribs = null;
19:
20: $css = new HTML_CSS($attribs, $prefs);
21: $css->parseString($styles);
22:
23: $css->getStyle('.highlight p', 'color');
24:
25: $styles = $css->grepStyle('/highlight/', '/^color$/');
26: echo '<pre>'; var_dump($styles); echo '</pre>';
27: ?>
Custom error handler is defined to allow script to continue (print message),
when HTML_CSS::getStyle()
will raise error at line 23.
Stylesheet used is :
h1, h2, h3, h4 { padding: 1em; } .highlight p, .highlight ul { margin-left: .5em; } #main p, #main ul { padding-top: 1em; }
While HTML_CSS::getStyle()
will raise error (printed
by custom error handler), line 25 will return an empty array with no error raised.
Reason : color
property does not exist for .highlight p
class selector.
Initial PEAR public release occured at end of July 2003.
Added the ability to parse CSS from string
with HTML_CSS::parseString()
Added the ability to parse CSS from file
with HTML_CSS::parseFile()
Added the ability to generate CSS and store it
in a file with HTML_CSS::toFile()
Introduced error handling system with PEAR_ErrorStack.
Added unit tests with PHPUnit 1.x for API and bugs.
Retrieve the settings of individual properties
Introduced a new error handling system: PEAR_ErrorStack was replaced by a simple way to plug in any error handling system you might want (default used PEAR_Error object)
HTML_CSS::apiVersion()
returns now a string rather than a float; compatible with
php.version_compare().
Added the ability to parse multiple data sources
(filename, string) at once with
HTML_CSS::parseData()
Added the ability to identify if HTML_CSS API output
returns an error with
HTML_CSS::isError()
Introduces package xml 2.0 and drop support of package xml 1.0
Unified API arguments for all group functions
Klaus Guenther (first package leader) became inactive.
Added ability to search if an element/property
is defined or not with
HTML_CSS::grepStyle()
Prevent invalid CSS data source in to be parse
Add Content-Disposition to the headers in display() Implement request 12195
Remove pointless NEWS file from distribution, clone of ChangeLog.
Upgrade requirement to PHP 4.3.0 and PEAR 1.5.4 (to avoid security vulnerability)
Coding Standard fixes: (errors/warnings) following recommandation by PHP_CodeSniffer
Unit test suites migrated from PHPUnit 1.x to 3.x
API 1.4.0 introduces new setter/getter PHP5 facility compatible (magic function __set, __get) for read/write CSS options.
Error handler allow now to use PEAR_ERROR_CALLBACK (local to HTML_CSS) to customize action when an error/exception is raised. No more need to use global PEAR::setErrorHandling.
Check validity of CSS data source to be parse using the W3C CSS validator service with help of package PEAR::Services_W3C_CSSValidator (require PHP5)
Ability to retrieve all options in effect at once
with HTML_CSS::getOptions()
Add support of At-Rules Implement request 12194
Improve support of At-Rules. See bugs
16354 Does not parse multiple simple At-rules properly
16355 Simple at rules nested within other at rules are reported as top level at rules
16357 Multiple equal complex at rules not parsed correctly
16358 Multiple media types on media at rule not parsed correctly
16359 Multiple selectors on a single rule inside a complex at rule not properly parsed
16360 Multiple selectors inside a complex at rule not properly parsed
HTML_CSS::createAtRule() signature changed. Add optional duplicates parameter to allow to handle two or more instance of a same @charset or @import or @namespace At-Rule. See HTML_CSS_TestSuite_Standard::testCreateAtRuleWithManyDefinitions for example.
The purpose of this tutorial is to give the new users of HTML_CSS an overview of its features and usage patterns. It describes a small subset of available functionality, but points to the parts of the documentation that give a more in-depth overview.
Selectors are one of the most important aspects of CSS as they are used to "select" elements on an HTML page so that they can be styled. We will try to explain basic concepts, and how to do the same with HTML_CSS.
A complete tutorial about CSS selectors can be find at maxDesign .
A rule or "rule set" is a statement that tells browsers how to render particular elements on an HTML page. A rule set consists of a selector followed by a declaration block.
The HTML_CSS::setStyle()
method is the only one
to handle a declaration block.
For example, to declare a such rule :
p { padding: 5px; }
Here is the php code to create the same sentence with HTML_CSS :
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$css->setStyle('p', 'padding', '5px');
$css->display();
?>
You can use more than one declaration within a declaration block. Each declaration must be separated with a semicolon ";".
For example, to declare a such rule :
p { padding: 5px; margin: 1px; }
Or, with whitespace added to aid readability :
p { padding: 5px; margin: 1px; }
Here is the php code to create the same sentence with HTML_CSS :
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$css->setStyle('p', 'padding', '5px');
$css->setStyle('p', 'margin', '1px');
$css->display();
?>
Notice that we use twice HTML_CSS::setStyle()
method call to declare each declaration block for the same selector.
When several selectors share the same declarations, they may be grouped together to save writing the same rule more than once. Each selector must be separated by a comma. For example :
h1, h2, h3, h4 { padding: 1em; } .highlight p, .highlight ul { margin-left: .5em; } #main p, #main ul { padding-top: 1em; }
To produce such result, we will need help of 3 new methods :
HTML_CSS::setSameStyle()
,
HTML_CSS::createGroup()
and
HTML_CSS::setGroupStyle()
.
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
// two selector groups only
$css->setStyle('.highlight p', 'margin-left', '.5em');
$css->setSameStyle('.highlight ul', '.highlight p');
// or even this way
$g2 = $css->createGroup('#main p, #main ul');
$css->setGroupStyle($g2, 'padding-top', '2em');
// more than two selector groups
$g1 = $css->createGroup('h1, h2, h3, h4');
$css->setGroupStyle($g1, 'padding', '1em');
$css->display();
?>
We should take care than grouping two selectors may be write
either with
HTML_CSS::setSameStyle()
or with couple
HTML_CSS::createGroup()
and
HTML_CSS::setGroupStyle()
.
When we have to group three or more selectors, there is only one possibility:
couple
HTML_CSS::createGroup()
and
HTML_CSS::setGroupStyle()
.
You can insert comments in CSS to explain your code. Like HTML comments, CSS comments will be ignored by the browser. A CSS comment begins with "/*", and ends with "*/". Comments can appear before or within rule sets as well as across multiple lines.
HTML_CSS 1.0.0 has not yet ability to handle comment such as :
/* Comment here */ p { margin: 1em; /* Comment here */ padding: 2em; /* color: white; */ background-color: blue; } /* multi-line comment here */
The common mistake that people make when writing comments is to expect getting all comments describe with such code below : it's an error.
<?php
$css->setComment('comment here');
$css->setStyle('p', 'margin', '1em');
$css->setStyle('p', 'padding', '2em');
$css->setComment('color: white;');
$css->setStyle('p', 'background-color', 'blue');
$css->setComment('
multi-line
comment here
');
?>
You will only get such result:
/* multi-line comment here */ p { margin: 1em; padding: 2em; background-color: blue; }
Only one comment is allowed due to usage of parent class method
HTML_Common::setComment()
.
Remember, when several selectors share the same declarations, they may be grouped together to save writing the same rule more than once. Each selector must be separated by a comma.
Two cases exists to handle group of selector. The quick and limited solution
(accept only two selectors in a same group) with :
HTML_CSS::setSameStyle()
.
And the extended/common solution with :
HTML_CSS::createGroup()
,
HTML_CSS::setGroupStyle()
,
HTML_CSS::addGroupSelector()
and
HTML_CSS::removeGroupSelector()
.
HTML_CSS::createGroup()
creates basically
a group for selectors given as first argument. You can identify this group
by an identifier (alphanumeric) or let HTML_CSS manage an internal numeric
identifier itself.
Let creates this group of selectors :
#main p, #main ul { padding-top: 1em; color: red; }
Identifier is named myGroupID in this example:
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$g1 = $css->createGroup('#main p, #main ul', 'myGroupID');
$css->setGroupStyle($g1, 'padding-top', '1em');
$css->setGroupStyle($g1, 'color', 'red');
$css->display();
?>
This is the default behavior.
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$g1 = $css->createGroup('#main p, #main ul');
$css->setGroupStyle($g1, 'padding-top', '1em');
$css->setGroupStyle($g1, 'color', 'red');
$css->display();
?>
With
HTML_CSS::addGroupSelector()
and
HTML_CSS::removeGroupSelector()
, we have ability to
add or remove one selector from a selectors list (of a group).
Suppose we have these selectors :
html, body { margin: 2px; padding: 0; border: 0; }
and we want to apply same share properties to new class 'large' and do not anymore apply it to 'body'.
Here are how to do :
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
// 1. create the pre-requires data
$g1 = $css->createGroup('html, body');
$css->setGroupStyle($g1, 'margin', '2px');
$css->setGroupStyle($g1, 'padding', '0');
$css->setGroupStyle($g1, 'border', '0');
// 2. remove the body selector
$css->removeGroupSelector(1, 'body');
// 3. add the new 'large' class
$css->addGroupSelector(1, '.large');
$css->display();
?>
Remember that HTML_CSS handle group (numeric) identifier itself (begin at 1, and go to 2, 3, and more).
One of the great features of HTML_CSS is to parse and load existing style sheet
from different data sources : (simple string, css file, combine).
We will see in detail all these possibilities with 3 methods :
HTML_CSS::parseString()
,
HTML_CSS::parseFile()
and
HTML_CSS::parseData()
.
A quick way to create multiple selector definitions in one step is to use the
HTML_CSS::parseString()
function.
Let creates these definitions :
html, body { margin: 2px; padding: 0px; border: 0px; } p, body { margin: 4px; }
Very easy with such script :
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$strcss = '
html, body {
margin: 2px;
padding: 0px;
border: 0px;
}
p, body {
margin: 4px;
}
';
$css->parseString($strcss);
$css->display();
?>
Another quick way to create multiple selector definitions in one step is to use the
HTML_CSS::parseFile()
function.
CSS file to load is supposed to be W3C/CSS compliant.
If previous selector definitions was into a file named 'styles.css', then to parse and load contents will be done with such script :
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$css->parseFile('styles.css');
$css->display();
?>
If there are duplicate definitions into file contents, you should parse file with call :
<?php
$css->parseFile('styles.css', true);
?>We will see next why we may find such duplicate definitions.
We have already seen two single sources where CSS data could come from.
The last one allow to parse data from multiple sources at once.
HTML_CSS::parseData()
function take an array
as first argument. Each item is either a filename (with .css extension) or
a string. Each source is merge to produce at end a unique style sheet.
In script below we load selector definitions from two independent sources: a string and a css file which contents duplicate definitions (parseFile() 2nd argument to set to true to catch this situation; see next section for explains). Internet Explorer).
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
// 1. a string to parse
$strcss = '
body, p { background-color: white; font: 1.2em Arial; }
p, div#black { color: black; }
div{ color: green; }
p { margin-left: 3em; }
';
$css->parseString($strcss);
// 2. a css file with duplicate definitions
$css->parseFile('iehack.css', true);
$css->display();
?>
Internet Explorer <= 6 does not handle box model in same way as others browsers that are better W3C compliant. For this reason, we need to fix boxes size with a hack such as this one you can find in example that follow. You can notice the duplicate 'voice-family' and 'height' properties.
To parse these definitions we need to activate the duplicate option of HTML_CSS.
#header { background-color: ivory; font-family: "Times New Roman", Times, serif; font-size: 5mm; text-align: center; /* IE 5.5 */ height:81px; border-top:1px solid #000; border-right:1px solid #000; border-left:1px solid #000; voice-family: "\"}\""; voice-family: inherit; /* IE 6 */ height: 99px; }
There are two ways :
global to all functions: set option allowduplicates on class constructor
local to a function: set argument duplicates to TRUE
For example here, we could wrote either (allow global duplicate) :
<?php
require_once 'HTML/CSS.php';
$attr = array('allowduplicates' => true);
$css = new HTML_CSS($attr);
$strcss = '
#header {
background-color: ivory;
font-family: "Times New Roman", Times, serif;
font-size: 5mm;
text-align: center;
/* IE 5.5 */
height:81px;
border-top:1px solid #000;
border-right:1px solid #000;
border-left:1px solid #000;
voice-family: "\"}\"";
voice-family: inherit;
/* IE 6 */
height: 99px;
}
';
$css->parseString($strcss);
$css->display();
?>
or also (duplicate on local call) :
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$strcss = '
#header {
background-color: ivory;
font-family: "Times New Roman", Times, serif;
font-size: 5mm;
text-align: center;
/* IE 5.5 */
height:81px;
border-top:1px solid #000;
border-right:1px solid #000;
border-left:1px solid #000;
voice-family: "\"}\"";
voice-family: inherit;
/* IE 6 */
height: 99px;
}
';
$css->parseString($strcss, true);
$css->display();
?>
Handle selectors, groups, properties are some basic features of HTML_CSS but
getting results in different format should be the final step of your process.
We have four/five different functions (depending or render you want to get) :
HTML_CSS::toArray()
,
HTML_CSS::toInline()
,
HTML_CSS::toFile()
,
HTML_CSS::toString()
and
HTML_CSS::display()
.
HTML_CSS::toArray()
must be considered
as a debug/help function. An easy way to explore HTML_CSS data structures.
For example, explore these definitions :
html, body { margin: 2px; padding: 0px; border: 0px; } p, body { margin: 4px; } div#header { text-align: center; color: red; }
With script :
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$strcss = '
html, body {
margin: 2px;
padding: 0px;
border: 0px;
}
p, body {
margin: 4px;
}
div#header { text-align: center; color: red; }
';
$css->parseString($strcss);
$arr = $css->toArray();
var_export($arr);
?>
HTML_CSS::toInline()
allow to integrate html tags
with styles.
Dissociate html code and presentation layout (CSS) is more than recommanded
Here is a basic example that show a white 'Hello World' on a black background :
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$css->setStyle('body', 'background-color', '#0c0c0c');
$css->setStyle('body', 'color', '#ffffff');
echo '<body style="' . $css->toInline('body') . '">';
echo '<p>Hello World</p>';
echo '</body>';
?>
HTML_CSS::toFile()
is probably the most interresting
solution. You can create a pure CSS file on the fly.
If file already exists, it will be replace without warning !
This script will load an existing style sheet and replace the body definition :
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$css->parseFile('example.css');
$css->setStyle('body', 'background-color', '#0c0c0c');
$css->setStyle('body', 'color', '#ffffff');
$css->toFile('example.css');
?>
HTML_CSS::toString()
is analogous to
HTML_CSS::toFile()
except that result is capture
to a PHP variable rather than to a text/css file.
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$strcss = '
ul, body {
padding: 1em 2em;
}
';
$css->parseString($strcss);
$css->setGroupStyle(1, 'color', 'red');
$str = $css->toString();
echo $str;
// will display
// ul, body { padding: 1em 2em; color: red; }
?>
HTML_CSS::display()
is no more no less than just a
<?php
echo $css->toString();
?>
with cache and charset management for browser output.
See also :
HTML_CSS::setCharset()
and
HTML_CSS::setCache()
functions.
One of features of HTML_CSS that was still missing to version less or equal
than 1.0.1 is ability to detect if a selector (or group of selectors), and/or
a property (or group of properties) was already defined.
This is now possible with method :
HTML_CSS::grepStyle()
Let creates these definitions into progress2.css
file :
#PB1.cellPB1I, #PB1.cellPB1A { width: 15px; height: 20px; font-family: Courier, Verdana; font-size: 8px; float: left; } #PB1.progressBorderPB1 { width: 172px; height: 24px; border-width: 1px; border-style: solid; border-color: #404040 #dfdfdf #dfdfdf #404040; background-color: #CCCCCC; } .progressPercentLabelpct1PB1 { width: 50px; text-align: right; background-color: transparent; font-size: 11px; font-family: Verdana, Tahoma, Arial; font-weight: normal; color: #000000; } .progressTextLabeltxt1PB1 { text-align: left; background-color: transparent; font-size: 11px; font-family: Verdana, Tahoma, Arial; font-weight: normal; color: #000000; } .cellPB1I { background-color: #CCCCCC; } .cellPB1A { background-color: #0033FF; } body { background-color: #E0E0E0; color: #000000; font-family: Verdana, Arial; }
Very easy with such script to find where is declared #PB1
class selectors (names like).
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$css->parseFile('progress2.css');
// find all selectors beginning with #PB1
$styles = $css->grepStyle('/^#PB1/');
echo '<pre>'; var_dump($styles); echo '</pre>';
?>
We still used the CSS definitions of previous example,
identified by progress2.css
file :
Very easy with such script to find where is declared color
property.
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$css->parseFile('progress2.css');
// find all selectors that set the color property
$styles = $css->grepStyle('/./', '/^color$/');
echo '<pre>'; var_dump($styles); echo '</pre>';
?>
mixed
HTML_CSS::__get
(
string
$option
)
Return current value of an individual option. If option does not exist, returns value is NULL.
$option
Name of option to set
throws no exceptions thrown
since version 1.4.0 (2007-12-13)
This function can not be called statically.
void
HTML_CSS::__set
(
string
$option
,
string
$val
)
Set an individual option value. Option must exist.
$option
Name of option to set
$val
Value of option to set
throws no exceptions thrown
since version 1.4.0 (2007-12-13)
This function can not be called statically.
string HTML_CSS::getCharset (
)
By default, HTML_CSS uses iso-8859-1 encoding.
throws no exceptions thrown
since version 0.2.0 (2003-07-31)
This function can not be called statically.
mixed HTML_CSS::getContentDisposition (
)
Get value of Content-Disposition header (inline filename) used to display results
throws no exceptions thrown
since version 1.3.0 (2007-10-22)
This function can not be called statically.
returns boolean FALSE if no content disposition, otherwise string for inline filename
array
HTML_CSS::getOptions
(
)
Return all configuration options at once
throws no exceptions thrown
since version 1.5.0 (2008-01-15)
This function can not be called statically.
void|PEAR_Error HTML_CSS::setCache (
bool $cache
= true
)
Define if the document should be cached by the browser. Default to false.
$cache
(optional)
throws HTML_CSS_ERROR_INVALID_INPUT
since version 0.2.0 (2003-07-31)
This function can not be called statically.
void|PEAR_Error HTML_CSS::setCharset (
string $type
= 'iso-8859-1'
)
Define the charset for the file. Default to ISO-8859-1 because of CSS1 compatability issue for older browsers.
$type
(optional) Charset encoding; defaults to ISO-8859-1.
throws HTML_CSS_ERROR_INVALID_INPUT
since version 0.2.0 (2003-07-31)
This function can not be called statically.
void|PEAR_Error HTML_CSS::setContentDisposition (
boolean $enable
= true
, string $filename
= ''
)
Defines the Content-Disposition filename for the browser output. Defaults to basename($_SERVER['PHP_SELF']).'.css'
$enable
(optional)
$filename
(optional)
throws HTML_CSS_ERROR_INVALID_INPUT
since version 1.3.0 (2007-10-22)
This function can not be called statically.
void
HTML_CSS::setLineEnd
(
string
$style
)
Set the line end style to Windows, Mac, Unix or a custom string
$style
"win", "mac", "unix" or custom string.
throws no exceptions thrown
since version 1.4.0 (2007-12-13)
This function can not be called statically.
void|PEAR_Error HTML_CSS::setOutputGroupsFirst (
bool $value
)
Determine whether groups are output before elements or not
$value
flag to true if groups are output before elements, false otherwise
throws HTML_CSS_ERROR_INVALID_INPUT
since version 0.3.3 (2004-05-20)
This function can not be called statically.
void|PEAR_Error HTML_CSS::setSingleLineOutput (
bool $value
)
Determine whether definitions are output on a single line or multi lines
$value
flag to true if single line, false for multi lines
throws HTML_CSS_ERROR_INVALID_INPUT
since version 0.3.3 (2004-05-20)
This function can not be called statically.
void
HTML_CSS::setTab
(
string
$string
)
Sets the string used to indent HTML
$string
String used to indent ("\11", "\t", ' ', etc.).
throws no exceptions thrown
since version 1.4.0 (2007-12-13)
This function can not be called statically.
void|PEAR_Error HTML_CSS::setXhtmlCompliance (
bool $value
)
Active or not the XHTML mode compliant
$value
flag to true if XHTML compliance needed, false otherwise
throws HTML_CSS_ERROR_INVALID_INPUT
since version 0.3.2 (2004-03-24)
This function can not be called statically.
mixed|PEAR_Error HTML_CSS::getStyle (
string $element
, string $property
)
Get the value of a property to an identifed simple CSS element
$element
Element (or class) to be defined
$property
Property defined
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_ELEMENT, HTML_CSS_ERROR_NO_ELEMENT_PROPERTY
since version 0.3.0 (2003-11-03)
This function can not be called statically.
void|PEAR_Error HTML_CSS::setStyle (
string $element
, string $property
, string $value
, bool $duplicates
= null
)
Add or change a single value for an element property
$element
Element (or class) to be defined
$property
Property defined
$value
Value assigned
$duplicates
(optional) Allow or disallow duplicates.
throws HTML_CSS_ERROR_INVALID_INPUT
since version 0.2.0 (2003-07-31)
This function can not be called statically.
<?php
require_once 'HTML/CSS.php';
// generate an instance
$css = new HTML_CSS();
// let's set some styles for <body>
$css->setStyle('body', 'background-color', '#0c0c0c');
$css->setStyle('body', 'color', '#ffffff');
// now for <h1>
$css->setStyle('h1', 'text-align', 'center');
$css->setStyle('h1', 'font', '16pt helvetica, arial, sans-serif');
// and finally for <p>
$css->setStyle('p', 'font', '12pt helvetica, arial, sans-serif');
// let's make <body> inherit from <p>
$css->setSameStyle('body', 'p');
// and let's put this into a tag:
echo '<body style="' . $css->toInline('body') . '">';
// will output:
// <body style="font:12pt helvetica, arial, sans-serif;background-color:#0c0c0c;color:#ffffff;">
?>
mixed|PEAR_Error HTML_CSS::createGroup (
string $selectors
, mixed $group
= null
)
Create a new CSS definition group. Return an integer identifying the group.
$selectors
Selector(s) to be defined, comma delimited.
$group
(optional) Group identifier. If not passed, will return an automatically assigned integer.
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_INVALID_GROUP
since version 0.3.0 (2003-11-03)
This function can not be called statically.
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
// define styles
$css->setStyle('p', 'text-align', 'center');
$css->setStyle('p', 'color', '#ffffff');
$css->setStyle('p', 'text-align', 'left');
$css->setStyle('p', 'font', '16pt helvetica, arial, sans-serif');
$css->setStyle('p', 'font', '12pt helvetica, arial, sans-serif');
// create a selectors group
$groupID = 'myGroup';
$groupID = $css->createGroup('p, a', $groupID);
// define styles of this new group
$css->setGroupStyle($groupID, 'font', '12pt helvetica, arial, sans-serif');
// display result
echo $css->toString();
// will output:
/*
p, a {
font: 12pt helvetica, arial, sans-serif;
}
p {
text-align: left;
color: #ffffff;
font: 12pt helvetica, arial, sans-serif;
}
*/
?>
void|PEAR_Error HTML_CSS::unsetGroup (
mixed $group
)
Remove a CSS definition group. Use the same identifier as for group creation.
$group
CSS definition group identifier
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_GROUP
since version 0.3.0 (2003-11-03)
This function can not be called statically.
mixed|PEAR_Error HTML_CSS::getGroupStyle (
mixed $group
, string $property
)
Get the CSS definition for group created by setGroupStyle()
$group
CSS definition group identifier
$property
Property defined
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_GROUP, HTML_CSS_ERROR_NO_ELEMENT
since version 0.3.0 (2003-11-03)
This function can not be called statically.
void|int|PEAR_Error HTML_CSS::setGroupStyle (
mixed $group
, string $property
, string $value
, bool $duplicates
= null
)
Define the new value of a property for a CSS group. The group should exist. If not, use HTML_CSS::createGroup() first
$group
CSS definition group identifier
$property
Property defined
$value
Value assigned
$duplicates
(optional) Allow or disallow duplicates.
returns Returns an integer if duplicates are allowed.
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_GROUP
since version 0.3.0 (2003-11-03)
This function can not be called statically.
void|PEAR_Error HTML_CSS::addGroupSelector (
mixed $group
, string $selectors
)
Add a selector to a CSS definition group
$group
CSS definition group identifier
$selectors
Selector(s) to be defined, comma delimited.
throws HTML_CSS_ERROR_NO_GROUP, HTML_CSS_ERROR_INVALID_INPUT
since version 0.3.0 (2003-11-03)
This function can not be called statically.
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
// define styles
$g = $css->createGroup('body, html');
$css->setGroupStyle($g, 'margin', '2px');
$css->setGroupStyle($g, 'padding', '0');
$css->setGroupStyle($g, 'border', '0');
// display intermediate result
echo $css->toString();
// will output:
/*
body, html {
margin: 2px;
padding: 0;
border: 0;
}
*/
// did not reflect a real usage, it's only a study case purpose
$css->removeGroupSelector($g, 'body');
$css->addGroupSelector($g, '.large');
$css->setGroupStyle($g, 'border', 'solid thin');
// display final result
echo $css->toString();
// will output:
/*
html, .large {
margin: 2px;
padding: 0;
border: solid thin;
}*/
?>
void|PEAR_Error HTML_CSS::removeGroupSelector (
mixed $group
, string $selectors
)
Definitively remove a selector from a CSS group
$group
CSS definition group identifier
$selectors
Selector(s) to be removed, comma delimited.
throws HTML_CSS_ERROR_NO_GROUP, HTML_CSS_ERROR_INVALID_INPUT
since version 0.3.0 (2003-11-03)
This function can not be called statically.
void|PEAR_Error HTML_CSS::setSameStyle (
string $new
, string $old
)
Set or change the properties of a new basic selector (not group) to the values of an existing selector
$new
New selector that should share the same definitions
$old
Selector that is already defined
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_ELEMENT
since version 0.2.0 (2003-07-31)
This function can not be called statically.
see HTML_CSS::setStyle() , if you want only to apply the same style as a basic selector.
see HTML_CSS::createGroup() , if you want to apply the same style to a list of selectors.
void|PEAR_Error HTML_CSS::parseString (
string $str
, bool $duplicates
= null
)
Parse a string that contains CSS information
$str
text string to parse
$duplicates
(optional) Allows or disallows duplicate style definitions
throws HTML_CSS_ERROR_INVALID_INPUT
since version 0.3.0 (2003-11-03)
This function can not be called statically.
void|PEAR_Error HTML_CSS::parseFile (
string $filename
, bool $duplicates
= null
)
Parse a file that contains CSS information
$filename
file to parse
$duplicates
(optional) Allow or disallow duplicates.
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_FILE
since version 0.3.0 (2003-11-03)
This function can not be called statically.
void|PEAR_Error HTML_CSS::parseData (
array $styles
, bool $duplicates
= null
)
Parse data sources, file(s) or string(s), that contains CSS information
$styles
data sources to parse
$duplicates
(optional) Allow or disallow duplicates.
throws HTML_CSS_ERROR_INVALID_INPUT
since version 1.0.0RC2 (2005-12-15)
This function can not be called statically.
boolean|PEAR_Error
HTML_CSS::validate
(
array
$styles
,
array
&$messages
)
Execute the W3C CSS validator service on each data source (filename or string) given by parameter $styles.
$styles
Data sources to check validity
&$messages
Error and Warning messages issue from W3C CSS validator service
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_INVALID_DEPS, HTML_CSS_ERROR_INVALID_SOURCE
since version 1.5.0 (2008-01-15)
This function can not be called statically.
array HTML_CSS::toArray (
)
Return the full contents of CSS data sources (parsed) in an array
throws no exceptions thrown
since version 0.2.0 (2003-07-31)
This function can not be called statically.
string|PEAR_Error HTML_CSS::toInline (
string $element
)
Generate and return the CSS properties of an element or class as a string for inline use.
$element
Element or class for which inline CSS should be generated
throws HTML_CSS_ERROR_INVALID_INPUT
since version 0.2.0 (2003-07-31)
This function can not be called statically.
void|PEAR_Error HTML_CSS::toFile (
string $filename
)
Generate current parsed CSS data sources and write result in a user file
$filename
Name of file that content the stylesheet
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_WRITE_FILE
since version 0.3.0 (2003-11-03)
This function can not be called statically.
string HTML_CSS::toString (
)
Generate current parsed CSS data sources and return result as a string
throws no exceptions thrown
since version 0.2.0 (2003-07-31)
This function can not be called statically.
void HTML_CSS::display (
)
Send the stylesheet content to standard output, handling cacheControl and contentDisposition headers
throws no exceptions thrown
since version 0.2.0 (2003-07-31)
This function can not be called statically.
array|PEAR_Error HTML_CSS::grepStyle (
string $elmPattern
, string $proPattern
)
Return array entries of styles that match patterns (Perl compatible)
$elmPattern
Element or class pattern to retrieve
$proPattern
(optional) Property pattern to retrieve
throws HTML_CSS_ERROR_INVALID_INPUT
since version 1.1.0 (2007-01-01)
This function can not be called statically.
void
HTML_CSS::getAtRulesList
(
)
Return the list of At-Rules supported by API 1.5.0 of HTML_CSS
throws no exceptions thrown
since version 1.5.0 (2008-01-15)
This function can not be called statically.
<?php
require_once 'HTML/CSS.php';
$list = $css->getAtRulesList();
var_export($list);
// will output:
//
// array (
// 0 => '@charset',
// 1 => '@font-face',
// 2 => '@import',
// 3 => '@media',
// 4 => '@page',
// 5 => '@namespace',
// )
?>
void|PEAR_Error
HTML_CSS::createAtRule
(
string
$atKeyword
,
string
$arguments
= ''
,
bool
$duplicates
= null
)
Create a simple at-rule without declaration style blocks. That include @charset, @import and @namespace
$atKeyword
at-rule keyword
$arguments
argument list for @charset, @import or @namespace
$duplicates
(optional) Allow or disallow duplicates
throws HTML_CSS_ERROR_INVALID_INPUT
since version 1.5.0 (2008-01-15)
This function can not be called statically.
void|PEAR_Error
HTML_CSS::unsetAtRule
(
string
$atKeyword
)
Remove an existing and supported at-rule. See HTML_CSS::getAtRulesList() for a full list of supported At-Rules.
$atKeyword
at-rule keyword
throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_ATRULE
since version 1.5.0 (2008-01-15)
This function can not be called statically.
void|PEAR_Error
HTML_CSS::getAtRuleStyle
(
string
$atKeyword
,
string
$arguments
,
string
$selectors
,
string
$property
)
Retrieve arguments or style value of an existing At-Rule. See HTML_CSS::getAtRulesList() for a full list of supported At-Rules.
$atKeyword
at-rule keyword
$arguments
argument list (optional for @font-face)
$selectors
selectors of declaration style block (optional for @media, @page, @font-face)
$property
property of a single declaration style block
throws HTML_CSS_ERROR_INVALID_INPUT
since version 1.5.0 (2008-01-15)
This function can not be called statically.
void|PEAR_Error
HTML_CSS::setAtRuleStyle
(
string
$atKeyword
,
string
$arguments
,
string
$selectors
,
string
$property
,
string
$value
,
bool
$duplicates
= null
)
Set arguments and declaration style block for at-rules that follow : "@media, @page, @font-face"
$atKeyword
at-rule keyword
$arguments
argument list (optional for @font-face)
$selectors
selectors of declaration style block (optional for @media, @page, @font-face)
$property
property of a single declaration style block
$value
value of a single declaration style block
$duplicates
(optional) Allow or disallow duplicates
throws HTML_CSS_ERROR_INVALID_INPUT
since version 1.5.0 (2008-01-15)
This function can not be called statically.
The HTML_CSS package is implemented with a flexible error handler plug-in system. You may use any error handler that you want. Using PEAR_Error object (default), but also the PEAR_ErrorStack package, or any other error handler you might want to plug in.
Without any configuration, each HTML_CSS API error (basic or exception) will raise a HTML_CSS_Error object that will be return to call script (user script).
Easy to distinct basic PEAR_Error from other PEAR packages to HTML_CSS errors, even if there is a better and more robust solution:
HTML_CSS::isError()
. But also provide a unique way to retrieve the level of error (warning, error, exception) with theHTML_CSS_Error::getLevel()
method.
As usual you can use the PEAR error API as well.
<?php
require_once 'HTML/CSS.php';
$css = new HTML_CSS();
$result = $css->setStyle('div', 'color', 5);
if (PEAR::isError($result)) {
// do something when an error is raised
}
?>
and output to screen will give something like :
Exception*: invalid input, parameter #3 "$value" was expecting "string", instead got "integer"** in html_css->setstyle (file [path_to]\[filename] on line 6)***
* |
error level |
** |
message body with context informations |
*** |
call context |
Perhaps this standard behavior is not what you want. Don't worry, you can change everything :
HTML_CSS obey at display_errors and log_errors protocol
A error handler's configuration is determined by the arguments used in its construction. Here's an overview of these parameters.
<?php
require_once 'HTML/CSS.php';
$errorConf = array('error_handler' => 'myErrorHandler',
'push_callback' => 'myError',
// ... more options
);
$css = new HTML_CSS(null, $errorConf);
?>
Option | Type | Description |
---|---|---|
error_handler | callback |
A valid callback (function) to manage errors raised by the
HTML_CSS::raiseError() method.
Default is: HTML_CSS::_errorHandler
|
push_callback | callback | A valid callback (function) that decides to following action. Default return: PEAR_ERROR_DIE if exception, NULL otherwise. |
error_callback | callback | A valid callback (function) that decides to call a real free user function. Default call: none |
message_callback | callback |
A valid callback (function) to control message generation.
Default is: HTML_CSS_Error::_msgCallback
|
context_callback | callback |
A valid callback (function) to control error context generation.
Default is: HTML_CSS_Error::getBacktrace
|
handler | mixed | any handler-specific settings |
There are many scenarios in which fine-grained control over error raising is absolutely necessary.
The first level to control error generation is the php.ini
directives
display_errors and log_errors.
When these directives are set to TRUE, then browser and file outputs are
effective.
If you want to ignore all errors raised (no display, no logs) and avoid to include PEAR core class, then you should have something like :
<?php
require_once 'HTML/CSS.php';
function myErrorHandler()
{
return null;
}
$errorConf = array('error_handler' => 'myErrorHandler');
$css = new HTML_CSS(null, $errorConf);
// ...
?>
Note for users of HTML_CSS 1.4.0 or greaterYou may want to decide to print (yes/no), log (yes/no) error messages with a full free user function local to HTML_CSS
<?php
require_once 'HTML/CSS.php';
function myErrorAction($css_error)
{
// do what you want: print and/or log $css_error instance of HTML_CSS_Error object
}
$errorConf = array('error_callback' => 'myErrorAction');
$css = new HTML_CSS(null, $errorConf);
// ...
?>rather than using global PEAR error handler (PEAR::setErrorHandling, PEAR::pushErrorHandling, PEAR::popErrorHandling).
<?php
require_once 'HTML/CSS.php';
function myErrorAction($css_error)
{
// do what you want: print and/or log $css_error instance of HTML_CSS_Error object
}
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'myErrorAction');
$css = new HTML_CSS();
// ...
?>
With push_callback option, you can decides to stop script execution (as done with exceptions by default: returns PEAR_ERROR_DIE constant), or continue without filtering (returns NULL).
If you want to write your own callback function for the push_callback option, this one should have two arguments: first one will get the error code, and second will get error level. These are all the necessary informations to do a filtering. Example that follow show how to be aware that a script use wrong argument data type.
<?php
require_once 'HTML/CSS.php';
function myErrorFilter($code, $level)
{
if ($code === HTML_CSS_ERROR_INVALID_INPUT) {
error_log('script: '.__FILE__.' used wrong argument data type', 1, 'admin@yoursite.com');
}
return null;
}
$errorConf = array('push_callback' => 'myErrorFilter');
$css = new HTML_CSS(null, $errorConf);
// ...
?>
In some cases, you may want to customize error generation. For instance, for each error (basic/exception), it is useful to include file, line number, and class/function context information in order to trace it. The default option will be sufficient for most cases but you want perhaps customize the output format (render) of context information.
With this example we will change display and log renders.
<?php
require_once 'HTML/CSS.php';
$displayConfig = array(
'lineFormat' => '<b>%1$s</b>: %2$s<br />%3$s',
'contextFormat' => '<b>File:</b> %1$s <br />'
. '<b>Line:</b> %2$s <br />'
. '<b>Function:</b> %3$s '
);
$logConfig = array(
'lineFormat' => '%1$s %2$s [%3$s] %4$s',
'timeFormat' => '%b'
);
$prefs = array(
'handler' => array('display' => $displayConfig,
'log' => $logConfig
));
$css = new HTML_CSS(null, $prefs);
// ...
$result = $css->setStyle('div', 'color', 5);
?>
Display render will give something like:
Exception****: invalid input, parameter #3 "$value" was expecting "string", instead got "integer"***** File: [path_to]\[filename] ****** Line: 22 Function: html_css->setstyle
* |
error level |
** |
message body with context informations |
*** |
call context (file, line, function) |
Log render will give something like:
Jun 127.0.0.1******* [exception********] invalid input, parameter #3 "$value" was expecting "string", instead got "integer"*********
* |
client ip address and execution date |
** |
error level |
*** |
message body with context informations |
To have both display and log output, check the
php.ini
display_errors and log_errors values : must be set to TRUE.
Let review, step by step, how to get such results.
Remember that with default classes, there are two drivers : display and log that have both their own configuration parameters. You can override these parameters values with the handler entry in the hash of second argument of the HTML_CSS class constructor.
We did it here with the $prefs
variable; it's a two key
associative array. First key display defines the display driver
values, and the second key log defines the log driver values.
Review the display driver custom values. Only two keys:
are redefined, thats means remains key
keep its default value. See table below.
Parameter | Type | Default | Description |
---|---|---|---|
eol | string | <br />\n | The end-on-line character sequence |
lineFormat | string | <b>%1$s</b>: %2$s %3$s | Log line format specification:
|
contextFormat | string | in <b>%3$s</b> (file <b>%1$s</b> on line <b>%2$s</b>) | Context format (class, file, line) specification:
|
If you don't wish to see context information in the error message, then remove the parameter %3$ in the lineFormat option even if contextFormat is set.
Review now the log driver custom values. Only two keys
are redefined, thats means six remains keys
keep their default values. See table below.
Parameter | Type | Default | Description |
---|---|---|---|
eol | string | \n | The end-on-line character sequence |
lineFormat | string | %1$s %2$s [%3$s] %4$s %5$s | Log line format specification:
|
contextFormat | string | in %3$s (file %1$s on line %2$s) | Context format (class, file, line) specification:
|
timeFormat | string | %b %d %H:%M:%S | Time stamp format used by strftime |
ident | string | REMOTE_ADDR | Client IP |
message_type | string | 3 | Destination type used by error_log |
destination | string | html_css_error.log | Destination name used by error_log |
extra_headers | string | NULL | Extra headers depending of destination type |
If you don't wish to see context information in the error message, then remove the parameter %5$ in the lineFormat option even if contextFormat is set.
There are two methods of HTML_CSS_Error designed for use with generating error messages efficiently. To use them, you must set the options below in the HTML_CSS class constructor (argument #2):
The default message handling callback
(HTML_CSS_Error::_getErrorMessage
)
get an array mapping error codes to error message templates, like so:
<?php
$messages = array(
HTML_CSS_ERROR_UNKNOWN =>
'unknown error',
HTML_CSS_ERROR_INVALID_INPUT =>
'invalid input, parameter #%paramnum% '
. '"%var%" was expecting '
. '"%expected%", instead got "%was%"'
);
?>
Basically, if a variable name is enclosed in percent signs (%), it will be replaced with the value passed in the associative array.
The default context handling callback
(HTML_CSS_Error::getBackTrace
)
gets an array of execution functions and discovers where the error was generated.
This is a simple HTML form generator. It supports all the HTML form element types including file uploads, may return or print the form, just individual form elements or the full form in "table mode" with a fixed layout.
This is a simple HTML form generator. It supports all the HTML form element types including file uploads, may return or print the form, just individual form elements or the full form in "table mode" with a fixed layout.
Generally one can use HTML_Form in HTML based projects of any form. But especially when using the package in "table mode" one may soon run into design problems, because the table that is created by HTML_Form cannot be equipped with CSS or other ways to control their look. Thus the main usage area of HTML_Form are sites where design does not play the most important role but where it is necessary to get fast results while keeping the code clean at the same time. (Examples are back office websites or places like pear.php.net, where the package is used at a lot of places.)
Basic example
<?php
require_once "HTML/Form.php";
$form = new HTML_Form('receivingscript.php');
$form->addText("name", "What's your name?");
$form->addSubmit("submit", "Go, Go");
$form->display();
?>
The above example code creates a HTML form that only contains a single textfield with the name "name" and with the label "What's your name" and a submit button labeled "Go, Go".
The following HTML form tags are supported by HTML_Form: textfields, password fields, checkboxes, textareas, submit buttons, reset buttons, select fields, radio buttons, image fields, hidden fields, file upload fields. Additionally one can add blank spaces and plain text to the form.
Creating a form in "table mode" means that the package returns the complete form in a fixed width table. Whilst this does not work very well for most frontend websites, it is a very convenient way to create forms during development phase or for backend systems where the design does not matter (much).
Creating a form
<?php
require_once "HTML/Form.php";
$form = new HTML_Form('receivingscript.php');
$form->addText("name", "What's your name?");
$form->addText("email", "What's your email address?");
$form->addPassword("password", "Please enter the desired password");
$form->addPlaintext("Tip", "Your password should be hard to guess");
$form->addSubmit("submit", "Submit");
$form->display();
?>
This will display a table where the left column holds the labels of the different fields. In the right column the different fields are placed.
Apart from retrieving the table as a whole, one can also use the API of HTML_Form to directly display single form tags.
Directly displaying form tags
<?php
require_once "HTML/Form.php";
$form = new HTML_Form('receivingscript.php');
$form->displayText("name", "What's your name?");
?>
The above example will print the HTML markup
being necessary to render a text input field. It will not print
the surrounding <form />
tag or something
else!
Similar to displaying form tags with the display*() functions HTML_Form can also return the tag instead of printing it.
Returning form tags
<?php
require_once "HTML/Form.php";
$form = new HTML_Form('receivingscript.php');
$str = $form->returnPassword("password", "Choose a password");
?>
The variable $str
now contains
HTML markup like
<input type="password" name="password" />
.
With the HTML_Menu class one can easily create and maintain a navigation structure for websites, configuring it via a multidimensional hash structure. Different modes for the HTML output are supported.
API to create HTML Pages
A HTML page is created by instantiating a new HTML_Page2 object. While you may pass an array of attributes to the constructor, it isn't required since the default settings are fairly intuitive.
Unix line endings (\n)
UTF-8
charset
text/html
MIME type
XHTML 1.0 Transitional
doctype
English language
If you don't like these settings, you can either pass an array of settings to the constructor or use setter methods such as setDoctype(), setMimeEncoding() or setCharset() .
Creating a HTML page
<?php
require_once 'HTML/Page2.php';
$page = new HTML_Page2();
$page->setTitle('My first HTML page');
$page->addBodyContent('<h1>Hello!</h1>');
$page->display();
?>
The method you surely will use is setTitle() that sets the title of the page; the one that your browser displays in its title/window bar.
<?php
$page->setTitle('My first HTML page');
?>
Meta tags can be set with setMetaData() which takes two parameters, the tag name and the content.
<?php
$page->setMetaData('author', 'Christian Weiske');
?>
A link to a bookmark icon (favicon) can be set using addFavicon() .
Links to related pages (previous, next, home etc.) are added with addHeadLink() - the first parameter determines the URL, the second the relation.
<?php
$page->addHeadLink('home.htm', 'Start');
?>
A HTML page may contain two types of stylesheets: Inline
css and a link to external .css
file.
Inline CSS declarations are made with addStyleDeclaration()
<?php
$page->addStyleDeclaration('
div.block {
border: 1px solid #000;
}
');
?>
Links to external CSS files are created by using addStyleSheet(). The optional third parameter determines which media type the stylesheet shall be used for.
<?php
$page->addStyleSheet('css/dark.css');
$page->addStyleSheet('css/print.css', 'text/css', 'print');
?>
You may want to add some javascript code to your page; either inline
or link to an external .js
file.
Inline code blocks are added with
addScriptDeclaration()
. The optional second parameter defaults to text/javascript
but can be changed as needed.
<?php
$page->addScriptDeclaration('
function javaScriptTest() {
alert("Test");
}
');
?>
Links to external script files are created by using addScript().
<?php
$page->addScript('js/functions.js');
?>
After creating the page and setting headers, you want to add actual content to the page. The method in charge is addBodyContent().
By default, if content already exists, the new content is appended. If you wish to overwrite whatever is in the body, use setBody(); unsetBody() completely empties the body without inserting new content. You can also use prependBodyContent() to prepend content to whatever is currently in the array of body elements.
The following constants are defined to be passed as the flag attribute:
HTML_PAGE2_APPEND
, HTML_PAGE2_PREPEND
and
HTML_PAGE2_REPLACE
.
Their usage should be quite clear from their names.
<?php
$page->addBodyContent('<h1>Hello!</h1>');
$page->addBodyContent('<p>This is a paragraph.</p>');
?>
After adding all the headers and content to the page, you want to get the page to the user. display() directly echoes the page to the browser.
If you want to do something with the result page before pushing it to the browser, use toHtml() . It returns the page as string that can be modified further.
<?php
//push it to the browser
$page->display();
//or put it into a variable
$html = $page->toHtml();
?>
How to include a loading bar in your XHTML documents quickly and easily
HTML_Progress2 is designed to display a loading bar in your XHTML documents during a long process. Progress bar may be synchronous (COMET) or asynchronous (AJAX).
external documentation ( full user guide )
Download page to get the full definitive guide. Three formats available : single HTML page, many HTML pages, Windows HTML help.
The PEAR::HTML_QuickForm package provides methods for creating, validating and processing HTML forms.
QuickForm is a convenience library for dealing with HTML forms.
It provides Javascript and server-side form validation, and is customizable and extensible in many ways.
QuickForm consists of multiple files. The main file is QuickForm.php
and should be installed in
your pear/HTML
directory. The other important files are element.php
,
which handles all methods relative to form elements, and group.php
, which deals with
methods relative to groups of elements in the form. Both are located in your HTML/QuickForm
directory along with the other form objects. input.php
contains a common class for all
the elements of input type (text, password...). QuickForm has objects for all the common form elements:
select, text, password, checkbox, file, submit, reset, button, image, radio, hidden, textarea.
QuickForm provides the possibility to create your own elements as long as you comply with the common API.
The purpose of this tutorial is to give the new users of QuickForm an overview of its features and usage patterns. It describes a small subset of available functionality, but points to the parts of the documentation that give a more in-depth overview.
There also exists a somewhat bigger tutorial on QuickForm usage made by Keith Edmunds.
Basic QuickForm usage
<?php
// Load the main class
require_once 'HTML/QuickForm.php';
// Instantiate the HTML_QuickForm object
$form = new HTML_QuickForm('firstForm');
// Set defaults for the form elements
$form->setDefaults(array(
'name' => 'Joe User'
));
// Add some elements to the form
$form->addElement('header', null, 'QuickForm tutorial example');
$form->addElement('text', 'name', 'Enter your name:', array('size' => 50, 'maxlength' => 255));
$form->addElement('submit', null, 'Send');
// Define filters and validation rules
$form->applyFilter('name', 'trim');
$form->addRule('name', 'Please enter your name', 'required', null, 'client');
// Try to validate a form
if ($form->validate()) {
echo '<h1>Hello, ' . htmlspecialchars($form->exportValue('name')) . '!</h1>';
exit;
}
// Output the form
$form->display();
?>
Lets review this example step by step.
The line
<?php
$form = new HTML_QuickForm('firstForm');
?>
creates a HTML_QuickForm object that will contain the objects representing elements and all the other
necessary information. We only pass the form's name to the constructor,
which means that default values will be used for other parameters. In particular, the form's method will
default to POST
and the form's action to the current file. When using QuickForm, it is
easier to keep all the form related logic in one file.
You might guess that
<?php
$form->setDefaults(array(
'name' => 'Joe User'
));
?>
sets the default value for name
element to 'Joe User'
. QuickForm has the
concept of default values (set via setDefaults() method) and
constant ones (set via setConstants()).
The difference between them is that user's input overrides default values but not constant ones.
Our form will consist of three elements:
<?php
$form->addElement('header', null, 'QuickForm tutorial example');
$form->addElement('text', 'name', 'Enter your name:', array('size' => 50, 'maxlength' => 255));
$form->addElement('submit', null, 'Send');
?>
The first one is not the 'real' element, it is just a heading to improve presentation. The second one actually is a text input box and the third is a submit button. Note that parameters for addElement() method have different meanings for different elements. That is so because they are actually passed to these elements' constructors.
The line
<?php
$form->applyFilter('name', 'trim');
?>
defines a filter for the 'name' element value - the function that will be applied to it after form submit. In this case it is a builtin trim() function, but can be any valid callback. Thus we will strip all whitespace from the name, as we do not actually need it and as we want to be sure that a name was entered, not just some spaces.
Next we define a rule for the name field:
<?php
$form->addRule('name', 'Please enter your name', 'required', null, 'client');
?>
This means that QuickForm will display an error message if the name was not entered. The
'client'
modifier is here to switch on client-side JavaScript validation in addition to
server-side one. Note also that QuickForm will automatically mark required fields in the form.
We now have the form built and rules defined and need to decide whether to process it or display:
<?php
if ($form->validate()) {
// Do some stuff
}
?>
The validate() method will consider the form valid (i.e. return TRUE) if some data was actually submitted and all the rules defined for the form were satisfied. In our case this means that 'name' element was not empty.
If the form is validated we need to process the values
<?php
echo '<h1>Hello, ' . htmlspecialchars($form->exportValue('name')) . '!</h1>';
exit;
?>
This is an example, in your scripts you'll usually want to store the values somewhere and to redirect to some other page to prevent a duplicate submit. The process() and exportvalues() methods may be of interest here.
The last line is pretty easy:
<?php
$form->display();
?>
If the form is not valid, which means that it either was not yet submitted or that there were errors, it will be displayed. Error messages (if any) will be displayed near the corresponding elements.
You now should have an understanding of basic QuickForm functionality, but there are many more features in the package, each of them deserving a separate tutorial. This section will give a short overview of them and point you to the complete documentation.
Groups allow to combine several individual elements into one entity and to use it as one element. This is also used to create pseudo-elements like date and hierselect.
QuickForm offers a lot of possibilities to customize form layout and appearance. Form output is done via renderers - special classes containing necessary logic. There are renderers that directly output HTML and those that use template engines for this.
And finally, you can extend QuickForm by adding your own element types, rules and renderers.
This document is based on questions asked on PEAR general mailing list. You are encouraged to search the list archives to find more verbose answers and examples.
<formArray>
.
Recent versions of HTML_QuickForm package require HTML_Common package version 1.2.1
(CVS revision 1.8 in HTML/Common.php
) to work properly. If
(and only if) an older version of HTML_Common is loaded, these symptoms occur.
Please note that
$ pear list
command may tell you that you have HTML_Common 1.2.1 installed.
In this case you also have an older version of HTML_Common somewhere and are including it instead
of the proper one. Check your include_path
setting in php.ini
and/or use PHP's get_included_files()
function to find out which file you are really including.
Constructor
of HTML_QuickForm accepts a $trackSubmit
parameter.
Setting this to TRUE will make QuickForm check whether the form was actually submitted.
This also helps if you have several forms defined on one page.
'date'
element?
Date element is essentially a group
of selects, you define the structure of this group
in the 'format'
option when
creating the element:
<?php
$form->addElement('date', 'foo', 'The date:', array('format' => 'Y m d'));
?>
Thus you pass the defaults as an array, just like you do with any other group:
<?php
$form->setDefaults(array(
'foo' => array('Y' => 2004, 'm' => 9, 'd' => 29)
));
?>
To ease using it with database-backed applications, date element also accepts Unix timestamps (generated by mktime()) and strings. The strings are processed by strtotime() functions, so consider its limitations.
"Call to a member function on a non-object"
or "Undefined function"
errors, especially when dealing with groups.
These errors tend to appear when you have something which is not a
HTML_QuickForm_element in the $elements
array
passed to addGroup().
This "something" is usually either a PEAR_Error instance (check for these
or setup a handler) or, if register_globals
is switched on in php.ini
,
some submitted values (clear the array before adding elements to it).
QuickForm does add a 'name'
attribute to the <form>
tag,
which is invalid in XHTML Strict. Quickform does not depend on that attribute since release 3.2.2, and it's only
kept for backwards compatibility. If you desire XHTML Strict compliance and your code does not depend
on said attribute, you can remove it via removeAttribute() method.
'required'
rule does not work for my elements!
'uploadedfile'
rule instead.
'date'
and 'hierselect'
as well.
* denotes required field
and Invalid information entered. / Please correct these fields.
validation messages?
You should use setRequiredNote() and setJsWarnings() methods, respectively.
As of release 3.2.5, HTML_QuickForm knows 23 element types that can be created via createElement() and added to the form via addElement(). These can be divided into two big groups: standard HTML elements and custom elements.
'button'
Class for <input type="button" /> elements, HTML_QuickForm_button
'checkbox'
Class for <input type="checkbox" /> elements, HTML_QuickForm_checkbox
'file'
Class for <input type="file" /> elements, HTML_QuickForm_file
'hidden'
Class for <input type="hidden" /> elements, HTML_QuickForm_hidden
'image'
Class for <input type="image" /> elements, HTML_QuickForm_image
'password'
Class for <input type="password" /> elements, HTML_QuickForm_password
'radio'
Class for <input type="radio" /> elements, HTML_QuickForm_radio
'reset'
Class for <input type="reset" /> elements, HTML_QuickForm_reset
'select'
Class for <select> elements, HTML_QuickForm_select. The class allows loading of <option> elements from array or database.
'submit'
Class for <input type="submit" /> elements, HTML_QuickForm_submit
'text'
Class for <input type="text" /> elements, HTML_QuickForm_text
'textarea'
Class for <textarea> elements, HTML_QuickForm_textarea
'xbutton'
Class for <button> elements, HTML_QuickForm_xbutton
'advcheckbox'
Class for an advanced checkbox type field, HTML_QuickForm_advcheckbox. Basically this fixes a problem that HTML has had where checkboxes can only pass a single value (the value of the checkbox when checked).
'autocomplete'
Class for a text field with autocompletion feature, HTML_QuickForm_autocomplete. The element looks like a normal HTML input text element that at every keypressed javascript event, searches the array of options for a match and autocompletes the text in case of match.
'date'
Class for a group of elements used to input dates (and times), HTML_QuickForm_date
'group'
Class for a form element group, HTML_QuickForm_group. QuickForm allows grouping of several elements into one entity and using this entity as a new element.
'header'
Class for adding headers to the form, HTML_QuickForm_header
'hiddenselect'
This class, HTML_QuickForm_hiddenselect, behaves as a select element, but instead of creating a <select> it creates hidden elements for all values already selected with setDefaults() or setConstants().
'hierselect'
Class to dynamically create "chained" HTML <select> elements, HTML_QuickForm_hierselect. Choosing an option in the first <select> changes the available options of the second select and so on.
'html'
Deprecated. A pseudo-element used for adding raw HTML to form, HTML_QuickForm_html. Intended for use with the default renderer only, template-based ones may (and probably will) completely ignore this.
'link'
Class for a link type field, HTML_QuickForm_link
'static'
Class for static data, HTML_QuickForm_static
HTML_QuickForm has improved a lot since version 2.x. With the addition of a new renderer layer, a lot of methods that were located in the main QuickForm class were actually duplicates of methods in the renderers. Those methods were kept to give user time to adjust their code. With release 3.2 they will be removed, making QuickForm class much lighter and consistent.
At the same time, file upload validation was moved to the file element as this is a more appropriate place.
Removed methods
QuickForm related
Renderer related
File upload related
<?php
$form->getAttributes(true);
?>
will return the same value by using HTML_Common::getAttributes() method.
Arguments order was changed to conform to the way elements are usually added to QuickForm by addElement(). Use HTML_QuickForm::addGroup() instead and swap the element label with the element name.
A header is now considered like any other element. There is a new HTML_QuickForm_header element that extends HTML_QuickForm_static. Just use
<?php
$form->addElement('header', $header_name, $header_text);
?>
This will also allow you to customize the header rendering based on its name.
If you absolutely need this feature, use
<?php
$form->addElement('html', $data)
?>
or consider using some template-based renderer.
Those methods are now handled by the renderers. How to use these methods depends on your choice of renderer. With QuickForm default renderer, you can use these methods like that:
<?php
$form =& new HTML_QuickForm('myform');
$renderer =& $form->defaultRenderer();
$renderer->setFormTemplate('<table><form{attributes}>{content}</form></table>');
$renderer->setHeaderTemplate('<tr><td colspan="2"><b>{header}</b></td></tr>');
$renderer->setGroupTemplate('<table><tr>{content}</tr></table>');
$renderer->setGroupElementTemplate('<td>{element}<br /><!-- BEGIN required -->*<!-- END required -->{label}</td>');
?>
defaultRenderer()will return a reference to QuickForm integrated renderer. You can of course use any other renderer available in QuickForm such as Sigma, ITX, Smarty, Flexy and so on. Have a look at their documentation to see which methods are available for them.
File-related methods and rules have been moved to the file element HTML_QuickForm_file because it makes more sense this way and you don't have to include upload-related code if you are not using uploads. You have access to these methods like that:
<?php
$form = new HTML_QuickForm('myform');
$file =& $form->addElement('file', 'myfile', 'Your file:');
$form->addRule('myfile', 'Cannot exceed 1776 bytes', 'maxfilesize', 1776);
if ($file->isUploadedFile()) {
$file->moveUploadedFile('/tmp', 'testfile.txt');
}
?>
or like that:
<?php
$file =& $form->getElement('myfile');
if ($file->isUploadedFile()) {
$fileInfo = $file->getValue();
}
?>
This section describes the available subpackages for HTML_QuickForm, e.g. custom elements or renderers.
A custom element that emulates via two select boxes a select box that allows selecting of multiple options.
If you use the Tableless renderer (see below, HTML_QuickForm_Renderer_Tableless), this subpackage replaces the default client-side validation with a JavaScript alert window by dynamic (using DHTML) error messages that are printed directly above each erroneous element. (documentation)
This is another custom element. It creates an HTML input text element that at every keypressed javascript event, returns a list of options in a dynamic dropdown select box (especially useful to emulate a select box with a huge number of options). This element uses the AJAX technology.
This is a replacement for the default renderer of HTML_QuickForm that uses only XHTML and CSS but no table tags, and generates fully valid XHTML output. (documentation)
Another custom element that is used to define dynamic filters on the client-side for select elements.
There are some more subpackages available that were not yet proposed as PEAR packages. An example is a DHTMLRules subpackage for forms using the default renderer by Justin Patrin that was not yet proposed as a PEAR package.
This section describes some packages that makes working with HTML_QuickForm easier, either in the case of working with databases or in the case of forms that have multiple pages.
FormBuilder aids in rapid application development using the packages DB_DataObject and HTML_QuickForm. (documentation)
This is a package that builds on PEAR DB and MDB2 to abstract datatypes and automate table creation, data validation, insert, update, delete, and select. It combines these with HTML_QuickForm to automatically generate input forms that match the table column definitions. (documentation)
If you want to create forms with multiple pages, this is the right package for you. It is an implementation of a PageController pattern. (documentation)
You can find more packages that have optional or required dependencies on HTML_QuickForm on the package site.
To be written.
void HTML_QuickForm::HTML_QuickForm (
string $formName = ''
, string $method = 'post'
, string $action = ''
, string $target = ''
, mixed $attributes
= null
, bool $trackSubmit
= false
)
Constructor, sets <form> tag attributes and loads submitted values.
$formName
Form's name.
$method
(optional) Form's method
$action
(optional) Form's action
$target
(optional) Form's target
$attributes
(optional) Extra attributes for <form> tag
$trackSubmit
(optional) Whether to track if the form was submitted by adding a special hidden field. If the name of such field is not present in the $_GET
or $_POST
values, the form will be considered as not submitted.
throws no exceptions thrown
This function can not be called statically.
object &HTML_QuickForm::addElement (
mixed $element
)
Adds an element into the form. If $element
is a string representing an element type, then this method accepts variable number of parameters, their meaning and count depending on element type.
Parameters starting from second will be passed to the element's constructor, consult the docs for the appropriate element to find out which parameters to pass.
$element
element object or type of element to add (text, textarea, file...)
return reference to added element
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_UNREGISTERED_ELEMENT | Element '$element ' does not exist in HTML_QuickForm::_loadElement() |
Tried to add an element of unknown type | Check the type name spelling or use HTML_QuickForm::registerElementType() |
QUICKFORM_INVALID_ELEMENT_NAME | Element 'elementName ' already exists in HTML_QuickForm::addElement() |
Tried to add an element having a name of an existing element, but of different type | Choose a different name for an element |
since 1.0
This function can not be called statically.
float HTML_QuickForm::apiVersion (
void
)
Returns the current API version
throws no exceptions thrown
since 1.0
This function can be called statically.
object &HTML_QuickForm::createElement (
string $elementType
)
Creates a new form element of the given type. This method accepts variable number of parameters, their meaning and count depending on $elementType
.
Parameters starting from second will be passed to the element's constructor, consult the docs for the appropriate element to find out which parameters to pass.
$elementType
type of element to create (text, textarea, file...)
return object, descendant of HTML_QuickForm_element
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_UNREGISTERED_ELEMENT | Element '$element ' does not exist in HTML_QuickForm::_loadElement() |
Tried to create an element of unknown type | Check the type name spelling or use HTML_QuickForm::registerElementType() |
since 1.0
This function can be called statically.
boolean HTML_QuickForm::elementExists (
string $element
= null
)
Returns TRUE if element is in the form, FALSE otherwise.
$element
form name of element to check
throws no exceptions thrown
since 1.0
This function can not be called statically.
string HTML_QuickForm::errorMessage (
int $value
)
Returns a textual error message for QuickForm error code.
$value
int error code
return error message
throws no exceptions thrown
This function can be called statically.
string HTML_QuickForm::getElementType (
string $element
)
Returns the type of the given element.
$element
Name of form element
return element type or FALSE if element is not found
throws no exceptions thrown
since 1.1
This function can not be called statically.
object &HTML_QuickForm::getElement (
string $element
)
Returns a reference to the form element.
$element
Element name
return reference to element
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_NONEXIST_ELEMENT | Element '$element ' does not exist in HTML_QuickForm::getElement() |
Tried to get a non-existant element | Check the element's name spelling |
since 2.0
This function can not be called statically.
int HTML_QuickForm::getMaxFileSize (
void
)
Returns the value of MAX_FILE_SIZE hidden element (used for file uploads).
return max file size in bytes
throws no exceptions thrown
since 3.0
This function can not be called statically.
array HTML_QuickForm::getRegisteredTypes (
void
)
Returns an array of registered element types.
throws
since 1.0
This function can be called statically.
object &HTML_QuickForm::insertElementBefore (
object &$element
, string $nameAfter
)
Inserts a new element right before the other element.
It is not possible to check whether the $element
is already added to the form, therefore if you want to move the existing form
element to a new position, you'll have to use
removeElement():
<?php
$form->insertElementBefore($form->removeElement('foo', false), 'bar');
?>
&$element
Element to insert (instance of HTML_QuickForm_element)
$nameAfter
Name of the element before which the new one is inserted
return reference to inserted element.
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_INVALID_ELEMENT_NAME | Several elements named $nameAfter exist in HTML_QuickForm::insertElementBefore() |
Several elements named $nameAfter (e.g.: radios) exist in the form. The method does not handle this case. |
Insert before some other element. Consider adding a dummy element with a unique name. |
QUICKFORM_INVALID_ELEMENT_NAME | Element '$elementName' already exists in HTML_QuickForm::insertElementBefore() | Element exists with the same name as $element but of different type |
Give some other name to the inserted element. |
QUICKFORM_NONEXIST_ELEMENT | Element $nameAfter does not exist in HTML_QuickForm::insertElementBefore() |
Tried to insert before a non-existant element | Check the element's name spelling |
since 3.2.4
This function can not be called statically.
bool HTML_QuickForm::isError (
mixed $value
)
Tells whether a result is an error (i.e. whether $value
is an instance of HTML_QuickForm_Error).
$value
result of some QuickForm function
return whether $value
is an error
throws no exceptions thrown
This function can be called statically.
boolean HTML_QuickForm::isTypeRegistered (
string $type
)
Returns whether or not the form element type is supported. New types are added via HTML_QuickForm::registerElementType().
$type
Form element type
throws no exceptions thrown
since 1.0
This function can be called statically.
void HTML_QuickForm::registerElementType (
string $typeName
, string $include
, string $className
)
Registers a new element type. After that, elements of this type may be created via createElement() and added to form via addElement().
$typeName
Name of element type
$include
Include path for element type
$className
Element class name
throws no exceptions thrown
since 1.0
This function can be called statically.
object &HTML_QuickForm::removeElement (
string $elementName
, boolean $removeRules
= true
)
Removes an element from the form.
$elementName
The element name
$removeRules
TRUE if rules for this element are to be removed too
return reference to element being removed.
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_NONEXIST_ELEMENT | Element '$elementName ' does not exist in HTML_QuickForm::removeElement() |
Tried to remove a non-existant element | Check the element's name spelling |
since 2.0
This function can not be called statically.
void HTML_QuickForm::setMaxFileSize (
int $bytes
)
Sets the value of MAX_FILE_SIZE hidden element (used for file uploads).
$bytes
Size in bytes
throws no exceptions thrown
since 3.0
This function can not be called statically.
void HTML_QuickForm::updateElementAttr (
mixed $elements
, mixed $attrs
)
Updates Attributes for one or more elements
This may not work for groups and group-based elements (date, hierselect). To ensure proper behaviour, you should update attributes of grouped elements manually.
$elements
Array of element names/objects or string of elements to be updated
$attrs
Array or string of html attributes
throws no exceptions thrown
since 2.10
This function can not be called statically.
To be written
This is a base class for all QuickForm elements. It defines the API that is implemented by all the child classes representing actual form elements. You should use these elements, there is no need to directly instantiate HTML_QuickForm_element.
If you would like to create your own element to use with QuickForm, you should extend this class or one of its descendants and make sure to implement all methods defined here. There is also toHtml() method defined in HTML_Common that should be implemented.
HTML_Common
HTML_QuickForm_element
Class | Summary |
---|---|
HTML_QuickForm_group | HTML class for a form element group |
HTML_QuickForm_input | Base class for input form elements |
HTML_QuickForm_select | Class to dynamically create an HTML SELECT |
HTML_QuickForm_static | HTML class for static data |
HTML_QuickForm_textarea | HTML class for a textarea type field |
void constructor HTML_QuickForm_element::HTML_QuickForm_element (
string $elementName
= null
, mixed $elementLabel
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
Name of the element
$elementLabel
Label(s) for the element
$attributes
Associative array of tag attributes or HTML attributes name="value" pairs
throws no exceptions thrown
see setName(), setLabel()
since 1.0
This function can not be called statically.
void HTML_QuickForm_element::accept (
object HTML_QuickForm_Renderer &$renderer
, bool $required
= false
, string $error
= null
)
This method rarely needs to be called directly, it is usually called from HTML_QuickForm::accept() method.
&$renderer
an instance of HTML_QuickForm_Renderer subclass
$required
Whether an element is required
$error
An error message associated with an element
throws no exceptions thrown
This function can not be called statically.
float HTML_QuickForm_element::apiVersion (
)
This package is not documented yet.
throws no exceptions thrown
since 1.0
This function can not be called statically.
mixed HTML_QuickForm_element::exportValue (
array &$submitValues
, bool $assoc
= false
)
This method is not intended to be called directly. It is called by HTML_QuickForm::exportValue() and HTML_QuickForm::exportValues() methods.
The method first tries to find a value for itself in a passed array, if such a value is not found it takes the display value via getValue(). It then filters out the values that cannot possibly be submitted by this element and returns the result.
&$submitValues
array of submitted values to search
$assoc
whether to return the value as associative array
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_element::freeze (
)
When the element is displayed after the call to freeze(), only its value is displayed without the input tags, thus the element cannot be edited. If persistant freeze is set, then hidden field containing the element value will be output, too.
This method makes sense only with the elements that actually are editable in the first place. It has no effect on buttons, images, hidden fields, static content and the like.
throws no exceptions thrown
see unfreeze(), isFrozen(), setPersistantFreeze(), getFrozenHtml(), HTML_QuickForm::freeze(), HTML_QuickForm::isFrozen().
This function can not be called statically.
Freezing the text element
<?php
require_once 'HTML/QuickForm.php';
$text =& HTML_QuickForm::createElement('text', 'freezeMe');
$text->setValue('Some value');
echo $text->toHtml() . "\n";
$text->freeze();
echo $text->toHtml() . "\n";
$text->setPersistantFreeze(false);
echo $text->toHtml() . "\n";
$text->unfreeze();
echo $text->toHtml() . "\n";
?>
Output
<input name="freezeMe" type="text" value="Some value" />
Some value<input type="hidden" name="freezeMe" value="Some value" />
Some value
<input name="freezeMe" type="text" value="Some value" />
string HTML_QuickForm_element::getFrozenHtml (
)
This method returns the HTML for the element in frozen state. There is rarely a need to call it directly.
throws no exceptions thrown
since 1.0
This function can not be called statically.
string HTML_QuickForm_element::getLabel (
)
This package is not documented yet.
throws no exceptions thrown
see setLabel().
since 1.3
This function can not be called statically.
string HTML_QuickForm_element::getName (
)
This package is not documented yet.
throws no exceptions thrown
see setName().
since 1.0
This function can not be called statically.
string HTML_QuickForm_element::getType (
)
This package is not documented yet.
throws no exceptions thrown
since 1.0
This function can not be called statically.
mixed HTML_QuickForm_element::getValue (
)
This package is not documented yet.
throws no exceptions thrown
since 1.0
This function can not be called statically.
bool HTML_QuickForm_element::isFrozen (
)
This package is not documented yet.
throws no exceptions thrown
see freeze().
since 1.3
This function can not be called statically.
void HTML_QuickForm_element::onQuickFormEvent (
string $event
, mixed $arg
, object &$caller
)
This method is not intended to be called directly.
If you are creating your own element, you should override this method and create handlers for each of available QuickForm events.
'addElement'
This event is sent by HTML_QuickForm::addElement() method when adding a new element to the form. Its handler should usually send 'createElement'
and 'updateValue'
events.
'createElement'
This event is sent by HTML_QuickForm::createElement() method after the element object is instantiated. Its handler should usually call class constructor using the contents of $arg
as parameters.
'setGroupValue'
The event is sent by HTML_QuickForm_group::setValue() method to each of the grouped elements. The handler generally should set the element's value to $arg
.
'updateValue'
The event is sent by QuickForm when the element is added to the form and when form default and constant values are set. The handler for this event is the most complex one: it should search for element's value within form's constant, submit (if applicable) and default values (in that order) and set the element's value to the found one.
$event
Name of event
$arg
event arguments
&$caller
calling object
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_element::setLabel (
mixed $label
)
Label is a description text that will be displayed near the element. Some renderers can handle multiple labels for the element.
$label
Display text for the element
throws no exceptions thrown
see getLabel().
since 1.3
This function can not be called statically.
void HTML_QuickForm_element::setName (
string $name
)
This package is not documented yet.
$name
Input field name attribute
throws no exceptions thrown
see getName().
since 1.0
This function can not be called statically.
void HTML_QuickForm_element::setPersistantFreeze (
bool $persistant
= false
)
Sets whether an element value should be kept in an hidden field when the element is frozen or not.
$persistant
True if persistant value
throws no exceptions thrown
see freeze(), getFrozenHtml().
since 2.0
This function can not be called statically.
void HTML_QuickForm_element::setValue (
mixed $value
)
This sets the display value for the element. It can be different from its submit value.
$value
Default value of the form element
throws no exceptions thrown
see getValue().
since 1.0
This function can not be called statically.
void HTML_QuickForm_element::unfreeze (
)
Unfreezes the element so that it becomes editable again.
throws no exceptions thrown
see freeze(), isFrozen(), setPersistantFreeze(), getFrozenHtml(), HTML_QuickForm::freeze(), HTML_QuickForm::isFrozen().
This function can not be called statically.
Since <input> elements have very similar HTML representations, they have this common base class. You don't need to instantiate it directly, use one of the child classes.
HTML_Common
HTML_QuickForm_input
Class | Summary |
---|---|
HTML_QuickForm_button | HTML class for a button type element |
HTML_QuickForm_checkbox | HTML class for a checkbox type field |
HTML_QuickForm_file | HTML class for a file type element |
HTML_QuickForm_hidden | HTML class for a hidden type element |
HTML_QuickForm_image | HTML class for a image type element |
HTML_QuickForm_password | HTML class for a password type field |
HTML_QuickForm_radio | HTML class for a radio type element |
HTML_QuickForm_reset | HTML class for a reset type element |
HTML_QuickForm_submit | HTML class for a submit type element |
HTML_QuickForm_text | HTML class for a text field |
HTML_QuickForm_input Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_input::HTML_QuickForm_input (
string $elementName
= null
, mixed $elementLabel
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
Input field name attribute
$elementLabel
Label(s) for the input field
$attributes
Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_input::setType (
string $type
)
This package is not documented yet.
$type
Element type
throws no exceptions thrown
since 1.0
This function can not be called statically.
This package is not documented yet.
HTML_Common
HTML_QuickForm_button
HTML_QuickForm_button Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_button::HTML_QuickForm_button (
string $elementName
= null
, string $value
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
(optional)Input field name attribute
$value
(optional)Input field value
$attributes
(optional)Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
This package is not documented yet.
HTML_Common
HTML_QuickForm_checkbox
Class | Summary |
---|---|
HTML_QuickForm_advcheckbox | HTML class for an advanced checkbox type field |
HTML_QuickForm_checkbox Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_checkbox::HTML_QuickForm_checkbox (
string $elementName
= null
, string $elementLabel
= null
, string $text = ''
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
(optional)Input field name attribute
$elementLabel
(optional)Input field label
$text
(optional)Checkbox display text
$attributes
(optional)Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
bool HTML_QuickForm_checkbox::getChecked (
)
Returns TRUE if checkbox has a "checked" attribute, FALSE otherwise. getValue() is an alias for this method. Thus the only value checkbox element can have in QuickForm is TRUE.
throws no exceptions thrown
see setChecked().
since 1.0
This function can not be called statically.
string HTML_QuickForm_checkbox::getText (
)
This package is not documented yet.
throws no exceptions thrown
see setText().
since 1.1
This function can not be called statically.
void HTML_QuickForm_checkbox::setChecked (
bool $checked
)
This sets or removes the element's "checked" attribute based on $checked
value. setValue() is an alias for this method.
$checked
Whether the field is checked or not
throws no exceptions thrown
see getChecked().
since 1.0
This function can not be called statically.
void HTML_QuickForm_checkbox::setText (
string $text
)
This means the text that would be displayed with the checkbox, automatically enclosed in <label> tags. The label in QuickForm's sense is set via setLabel().
$text
throws no exceptions thrown
see getText().
since 1.1
This function can not be called statically.
Alongside the usual element's methods, the class has special methods for working with uploaded files. When the class is included it also registers rules for validating the uploaded files.
HTML_Common
HTML_QuickForm_file
HTML_QuickForm_file Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_file::HTML_QuickForm_file (
string $elementName
= null
, string $elementLabel
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
Input field name attribute
$elementLabel
Input field label
$attributes
(optional)Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
int HTML_QuickForm_file::getSize (
)
This means 'size' attribute of the <input /> element, not size of the uploaded file.
throws no exceptions thrown
see setSize().
since 1.0
This function can not be called statically.
array HTML_QuickForm_file::getValue (
)
Returns the information about the file upload, as in the $_FILES
array. Note that while there exists a setValue() method, the method does nothing at all. The file element does not have a value if the form was not submitted.
throws no exceptions thrown
since 3.0
This function can not be called statically.
bool HTML_QuickForm_file::isUploadedFile (
)
This package is not documented yet.
returns true if file has been uploaded, false otherwise
throws no exceptions thrown
This function can not be called statically.
bool HTML_QuickForm_file::moveUploadedFile (
string $dest
, string $fileName = ''
)
This package is not documented yet.
$dest
Destination directory path
$fileName
New file name (if not given, original file name will be used).
returns true if file has been moved successfully, false otherwise.
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_file::setSize (
int $size
)
This package is not documented yet.
$size
Size of file element
throws no exceptions thrown
see getSize().
since 1.0
This function can not be called statically.
This package is not documented yet.
HTML_Common
HTML_QuickForm_image
HTML_QuickForm_image Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_image::HTML_QuickForm_image (
string $elementName
= null
, string $src = ''
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
(optional)Element name attribute
$src
(optional)Image source
$attributes
(optional)Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_image::setAlign (
string $align
)
This is just a shortcut for
<?php
$element->updateAttributes(array('align' => $align));
?>
$align
alignment for image element
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_image::setBorder (
string $border
)
This is just a shortcut for
<?php
$element->updateAttributes(array('border' => $border));
?>
$border
border for image element
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_image::setSource (
string $src
)
This is just a shortcut for
<?php
$element->updateAttributes(array('src' => $src));
?>
$src
source for image element
throws no exceptions thrown
since 1.0
This function can not be called statically.
This package is not documented yet.
HTML_Common
HTML_QuickForm_password
HTML_QuickForm_password Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_password::HTML_QuickForm_password (
string $elementName
= null
, string $elementLabel
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
(optional)Input field name attribute
$elementLabel
(optional)Input field label
$attributes
(optional)Either a typical HTML attribute string or an associative array
throws
since 1.0
This function can not be called statically.
void HTML_QuickForm_password::setMaxlength (
string $maxlength
)
This is just a shortcut for
<?php
$element->updateAttributes(array('maxlength' => $maxlength));
?>
$maxlength
Maximum length of password field
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_password::setSize (
string $size
)
This is just a shortcut for
<?php
$element->updateAttributes(array('size' => $size));
?>
$size
Size of password field
throws no exceptions thrown
since 1.0
This function can not be called statically.
This package is not documented yet.
HTML_Common
HTML_QuickForm_radio
HTML_QuickForm_radio Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_radio::HTML_QuickForm_radio (
string $elementName
= null
, mixed $elementLabel
= null
, string $text
= null
, string $value
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
Input field name attribute
$elementLabel
Label(s) for a field
$text
Text to display near the radio
$value
Input field value
$attributes
Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
string HTML_QuickForm_radio::getChecked (
)
This package is not documented yet.
throws no exceptions thrown
see setChecked().
since 1.0
This function can not be called statically.
string HTML_QuickForm_radio::getText (
)
This package is not documented yet.
throws no exceptions thrown
see setText().
since 1.1
This function can not be called statically.
void HTML_QuickForm_radio::setChecked (
bool $checked
)
This package is not documented yet.
$checked
Whether the field is checked or not
throws no exceptions thrown
see getChecked().
since 1.0
This function can not be called statically.
void HTML_QuickForm_radio::setText (
string $text
)
This means the text that would be displayed with the radio, automatically enclosed in <label> tags. The label in QuickForm's sense is set via setLabel().
$text
Text to display near the radio button
throws no exceptions thrown
see getText().
since 1.1
This function can not be called statically.
This package is not documented yet.
HTML_Common
HTML_QuickForm_reset
HTML_QuickForm_reset Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_reset::HTML_QuickForm_reset (
string $elementName
= null
, string $value
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
(optional)Input field name attribute
$value
(optional)Input field value
$attributes
(optional)Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
The highlight of this class is that it allows populating the options from associative array or from the database.
HTML_Common
HTML_QuickForm_select
Class | Summary |
---|---|
HTML_QuickForm_hiddenselect | Creates hidden elements with select's values |
HTML_QuickForm_select Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_select::HTML_QuickForm_select (
string $elementName
= null
, mixed $elementLabel
= null
, mixed $options
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
Select name attribute
$elementLabel
Label(s) for the select
$options
Data to be used to populate options
$attributes
Either a typical HTML attribute string or an associative array
throws no exceptions thrown
see load().
since 1.0
This function can not be called statically.
<?php
$select = $quickform->createElement(
'select', 'selectname',
'Label for select',
array('fr' => 'French', 'de' => 'German', 'en_GB' => 'British English'),
array('id' => 'selectname', 'onchange' => '...'));
);
?>
void HTML_QuickForm_select::addOption (
string $text
, string $value
, mixed $attributes
= null
)
This package is not documented yet.
$text
Display text for the OPTION
$value
Value for the OPTION
$attributes
Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
bool HTML_QuickForm_select::getMultiple (
)
This method returns TRUE if a multiple
attribute is present in the select, FALSE otherwise.
returns true if multiple select, false otherwise
throws no exceptions thrown
see setMultiple().
since 1.2
This function can not be called statically.
string HTML_QuickForm_select::getPrivateName (
)
If select has a multiple
attribute present, then this method returns the name with brackets appended, else the result is identical to getName().
throws no exceptions thrown
see getName(), getMultiple().
since 1.0
This function can not be called statically.
array HTML_QuickForm_select::getSelected (
)
This function is an alias to getValue().
returns of selected values
throws no exceptions thrown
see getValue(), setSelected().
since 1.0
This function can not be called statically.
int HTML_QuickForm_select::getSize (
)
This package is not documented yet.
throws no exceptions thrown
see setSize().
since 1.0
This function can not be called statically.
PEAR_Error HTML_QuickForm_select::load (
mixed &$options
, mixed $param1
= null
, mixed $param2
= null
, mixed $param3
= null
, mixed $param4
= null
)
This method is a simulated overloaded method. The arguments, other than the first are optional and only mean something depending on the type of the first argument.
If the first argument is an array then all arguments are passed in order to loadArray(). If the first argument is a DB_Result then all arguments are passed in order to loadDbResult(). If the first argument is a string or a DB connection then all arguments are passed in order to loadQuery().
&$options
Options source currently supports assoc array or DB_result
$param1
(optional) See function detail
$param2
(optional) See function detail
$param3
(optional) See function detail
$param4
(optional) See function detail
returns TRUE on success
throws PEAR_Error
see loadArray(), loadDbResult(), loadQuery().
since 1.1
This function can not be called statically.
mixed HTML_QuickForm_select::loadArray (
array $arr
, mixed $values
= null
)
The array should have the form 'option value' => 'option text'.
$arr
Associative array of options
$values
(optional) Array or comma delimited string of selected values
returns TRUE on success
throws PEAR_Error
see load(), addOption().
since 1.0
This function can not be called statically.
mixed HTML_QuickForm_select::loadDbResult (
object DB_Result &$result
, string $textCol
= null
, string $valueCol
= null
, mixed $values
= null
)
If no column names are specified the first two columns of the result are used as the text and value columns respectively.
&$result
DB_result object
$textCol
(optional) Name of column to display as the OPTION text
$valueCol
(optional) Name of column to use as the OPTION value
$values
(optional) Array or comma delimited string of selected values
returns TRUE on success or PEAR_Error
throws PEAR_Error
see load(), addOption().
since 1.0
This function can not be called statically.
mixed HTML_QuickForm_select::loadQuery (
mixed &$conn
, string $sql
, string $textCol
= null
, string $valueCol
= null
, mixed $values
= null
)
If no column names are specified the first two columns of the result are used as the text and value columns respectively.
&$conn
Either an existing DB connection or a valid dsn
$sql
SQL query string
$textCol
(optional) Name of column to display as the OPTION text
$valueCol
(optional) Name of column to use as the OPTION value
$values
(optional) Array or comma delimited string of selected values
throws PEAR_Error
see load(), loadDbResult(), addOption().
since 1.1
This function can not be called statically.
void HTML_QuickForm_select::setMultiple (
bool $multiple
)
This method just adds or removes the multiple
attribute of select depending on $multiple
value.
$multiple
Whether the select supports multi-selections
throws no exceptions thrown
see getMultiple().
since 1.2
This function can not be called statically.
void HTML_QuickForm_select::setSelected (
mixed $values
)
This method is an alias for setValue(). For multiple selects you can pass either an array or a comma delimited string of values.
$values
Array or comma delimited string of selected values
throws no exceptions thrown
see getSelected().
since 1.0
This function can not be called statically.
void HTML_QuickForm_select::setSize (
int $size
)
This package is not documented yet.
$size
Size of select field
throws no exceptions thrown
see getSize().
since 1.0
This function can not be called statically.
This package is not documented yet.
HTML_Common
HTML_QuickForm_submit
HTML_QuickForm_submit Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_submit::HTML_QuickForm_submit (
string $elementName
= null
, string $value
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
Input field name attribute
$value
Input field value
$attributes
Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
This package is not documented yet.
HTML_Common
HTML_QuickForm_text
Class | Summary |
---|---|
HTML_QuickForm_autocomplete | HTML class for a text field with autocompletion feature |
HTML_QuickForm_text Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_text::HTML_QuickForm_text (
string $elementName
= null
, string $elementLabel
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
(optional)Input field name attribute
$elementLabel
(optional)Input field label
$attributes
(optional)Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_text::setMaxlength (
string $maxlength
)
This is just a shortcut for
<?php
$element->updateAttributes(array('maxlength' => $maxlength));
?>
$maxlength
Maximum length of text field
throws no exceptions thrown
since 1.3
This function can not be called statically.
void HTML_QuickForm_text::setSize (
string $size
)
This is just a shortcut for
<?php
$element->updateAttributes(array('size' => $size));
?>
$size
Size of text field
throws no exceptions thrown
since 1.3
This function can not be called statically.
This package is not documented yet.
HTML_Common
HTML_QuickForm_textarea
HTML_QuickForm_textarea Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_textarea::HTML_QuickForm_textarea (
string $elementName
= null
, mixed $elementLabel
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
Input field name attribute
$elementLabel
Label(s) for a field
$attributes
Either a typical HTML attribute string or an associative array
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_textarea::setCols (
string $cols
)
This package is not documented yet.
$cols
Width expressed in cols
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_textarea::setRows (
string $rows
)
This package is not documented yet.
$rows
Height expressed in rows
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm_textarea::setWrap (
string $wrap
)
This package is not documented yet.
$wrap
Wrap type
throws no exceptions thrown
since 1.0
This function can not be called statically.
This class is named 'xbutton' since the name 'button' was already taken by class representing an <input type="button" /> HTML element. Available since HTML_QuickForm release 3.2.3
HTML_Common
HTML_QuickForm_xbutton
HTML_QuickForm_xbutton Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_xbutton::HTML_QuickForm_xbutton (
string $elementName
= null
, string $elementContent
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
(optional) Button name
$elementContent
(optional) Button content (HTML to add between <button></button> tags)
$attributes
(optional) Either a typical HTML attribute string or an associative array
throws no exceptions thrown
This function can not be called statically.
since 3.2.3
void HTML_QuickForm_xbutton::setContent (
string $content
)
Sets the contents of the button element: HTML to add between <button></button> tags.
$content
HTML to add between <button></button> tags
throws no exceptions thrown
This function can not be called statically.
since 3.2.3
HTML class for an advanced checkbox type field. Basically this fixes a problem that HTML has had where checkboxes can only pass a single value (the value of the checkbox when checked). A value for when the checkbox is not checked cannot be passed, and furthermore the checkbox variable doesn't even exist if the checkbox was submitted unchecked.
It works by creating a hidden field with the passed-in name and creating the checkbox with no name, but with a javascript onclick which sets the value of the hidden field.
HTML_Common
HTML_QuickForm_advcheckbox
HTML_QuickForm_advcheckbox Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_checkbox::HTML_QuickForm_checkbox() | Class constructor |
HTML_QuickForm_checkbox::exportValue() | Return true if the checkbox is checked, null if it is not checked (getValue() returns false) |
HTML_QuickForm_checkbox::getChecked() | Returns whether a checkbox is checked |
HTML_QuickForm_checkbox::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_checkbox::getText() | Returns the checkbox text |
HTML_QuickForm_checkbox::getValue() | Returns the value of the form element |
HTML_QuickForm_checkbox::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_checkbox::setChecked() | Sets whether a checkbox is checked |
HTML_QuickForm_checkbox::setText() | Sets the checkbox text |
HTML_QuickForm_checkbox::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_advcheckbox::HTML_QuickForm_advcheckbox (
string $elementName
= null
, string $elementLabel
= null
, string $text
= null
, mixed $attributes
= null
, mixed $values
= null
)
This package is not documented yet.
$elementName
(optional)Input field name attribute
$elementLabel
(optional)Input field label
$text
(optional)Text to put after the checkbox
$attributes
(optional)Either a typical HTML attribute string or an associative array
$values
(optional)Values to pass if checked or not checked
throws no exceptions thrown
since 1.0
This function can not be called statically.
string HTML_QuickForm_advcheckbox::getOnclickJs (
string $elementName
)
Create the javascript for the onclick event which will set the value of the hidden field.
$elementName
The element name
throws no exceptions thrown
This function can not be called statically.
string HTML_QuickForm_advcheckbox::getPrivateName (
string $elementName
)
This is the name that will be used by the actual checkbox.
$elementName
The element name to make private
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_advcheckbox::setValues (
mixed $values
)
If $values
is a string then it will be used for checked state. If it is an array, then $values[0]
will be used for unchecked state and $values[1]
for checked.
$values
The values, either a string or an array
throws no exceptions thrown
This function can not be called statically.
The element looks like a normal HTML input text element that at every keypressed javascript event searches the array of options for a match and autocompletes the text in case of match. This is similar to the browsers' behaviour when one enters the URL into the Address field.
HTML_Common
HTML_QuickForm_text
HTML_QuickForm_autocomplete Inherited Methods
Method Name | Summary |
---|---|
HTML_QuickForm_text::HTML_QuickForm_text() | Class constructor |
HTML_QuickForm_text::setMaxLength() | Sets maxlength of text field |
HTML_QuickForm_text::setSize() | Sets size of text field |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_input::HTML_QuickForm_input() | Class constructor |
HTML_QuickForm_input::exportValue() | We don't need values from button-type elements (except submit) and files |
HTML_QuickForm_input::getName() | Returns the element name |
HTML_QuickForm_input::getValue() | Returns the value of the form element |
HTML_QuickForm_input::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_input::setName() | Sets the input field name |
HTML_QuickForm_input::setType() | Sets the element type |
HTML_QuickForm_input::setValue() | Sets the value of the form element |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_autocomplete::HTML_QuickForm_autocomplete (
string $elementName
= null
, string $elementLabel
= null
, array $options
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
(optional)Input field name attribute
$elementLabel
(optional)Input field label
$options
Autocomplete options
$attributes
(optional)Either a typical HTML attribute string or an associative array
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_autocomplete::setOptions (
array $options
)
The strings in this array will be checked by the javascript function for a match with the text being typed. In case of a match the text will be autocompleted.
$options
Array of options for the autocomplete input text element
throws no exceptions thrown
This function can not be called statically.
The element is basically a group of <select>s that allow to input dates and times.
HTML_Common
HTML_QuickForm_date
HTML_QuickForm_date Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_group::HTML_QuickForm_group() | Class constructor |
HTML_QuickForm_group::accept() | Accepts a renderer |
HTML_QuickForm_group::exportValue() | As usual, to get the group's value we access its elements and call |
HTML_QuickForm_group::getElementName() | Returns the element name inside the group such as found in the html form |
HTML_QuickForm_group::getElements() | Gets the grouped elements |
HTML_QuickForm_group::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_group::getGroupType() | Gets the group type based on its elements Will return 'mixed' if elements contained in the group are of different types. |
HTML_QuickForm_group::getName() | Returns the group name |
HTML_QuickForm_group::getValue() | Returns the value of the group |
HTML_QuickForm_group::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_group::setElements() | Sets the grouped elements |
HTML_QuickForm_group::setName() | Sets the group name |
HTML_QuickForm_group::setValue() | Sets values for group's elements |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_date::HTML_QuickForm_date (
string $elementName
= null
, mixed $elementLabel
= null
, array $options = array()
, mixed $attributes
= null
)
The $options
parameter controls the element's appearance. It is an associative array of the form 'option name' => 'option value'.
'language'
'en'
.
date element supports many languages. If your one is not supported, send us the translation, we'll gladly include it.
'format'
Format string for the date, based on PHP's date() function. The following characters are recognised:
Default is 'dMY'
.
'minYear'
Minimum year in year select. Default is 2001.
'maxYear'
Maximum year in year select. Default is 2010.
On'minYear'
and'maxYear'
When
'minYear'
>'maxYear'
the years in the select will be displayed in descending order.
'addEmptyOption'
Should an empty option be added to the top of each select box? Default is FALSE. This may be set for individual fields also, if one passes an array of the form array('format char' => TRUE, ..., 'another format char' => FALSE)
'emptyOptionValue'
The value passed by the empty option. Default is ''
.
'emptyOptionText'
The text displayed for the empty option. Default is ' '
.
This may be set for individual fields also, if one passes an array of the form
array('format char' => 'some text', ..., 'another format char' => 'some other text')
'optionIncrement'
Step to increase the option values by. Works for 'i'
and 's'
formats currently. Default is array('i' => 1, 's' => 1).
$elementName
Element's name
$elementLabel
Label(s) for an element
$options
Options to control the element's display
$attributes
Either a typical HTML attribute string or an associative array
throws no exceptions thrown
This function can not be called statically.
This element is used for adding headers to the form. Unlike usual static elements, the headers are usually rendered using a special template.
HTML_Common
HTML_QuickForm_header
HTML_QuickForm_header Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_static::HTML_QuickForm_static() | Class constructor |
HTML_QuickForm_static::exportValue() | We override this here because we don't want any values from static elements |
HTML_QuickForm_static::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_static::getName() | Returns the element name |
HTML_QuickForm_static::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_static::setName() | Sets the element name |
HTML_QuickForm_static::setText() | Sets the text |
HTML_QuickForm_static::setValue() | Sets the text (uses the standard setValue call to emulate a form element. |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_header::HTML_QuickForm_header (
string $elementName
= null
, string $text
= null
)
This package is not documented yet.
$elementName
Header name
$text
Header text
throws no exceptions thrown
This function can not be called statically.
Class to dynamically create "chained" HTML Select elements. Choosing an option in the first <select> changes the content of the second select and so on.
This element is considered as a group. Selects will be named groupName[0], groupName[1], ...
Creating a hierselect element based on values from a database table
<?php
require_once 'HTML/QuickForm.php';
$form = new HTML_QuickForm('example');
$form->setDefaults(array('test' => array('4','15')));
$sel =& $form->addElement('hierselect', 'test', 'Test:', null, '/');
$mainOptions = $db->getAssoc('select pkparent, par_desc from parent');
$result = $db->query("select fk_parent, pkchild, chi_desc from child");
while ($result->fetchInto($row)) {
$secOptions[$row[0]][$row[1]] = $row[2];
}
// Using setMainOptions and setSecOptions is now deprecated
// use setOptions.
$sel->setOptions(array($mainOptions, $secOptions));
$form->display();
?>
Creating more than two select elements is just as simple.
Creating a hierselect element with three select elements
<?php
require_once 'HTML/QuickForm.php';
$form = new HTML_QuickForm('example');
$select1[0] = 'Pop';
$select1[1] = 'Classical';
$select1[2] = 'Funeral doom';
// second select
$select2[0][0] = '--- Artist ---';
$select2[0][1] = 'Red Hot Chil Peppers';
$select2[0][2] = 'The Pixies';
$select2[1][0] = '--- Artist ---';
$select2[1][1] = 'Wagner';
$select2[1][2] = 'Strauss';
$select2[2][0] = '--- Artist ---';
$select2[2][1] = 'Pantheist';
$select2[2][2] = 'Skepticism';
// Create a third select with prices for the cds
$select3[0][0][0] = '--- Choose the artist ---';
$select3[0][1][0] = '15.00$';
$select3[0][2][1] = '17.00$';
$select3[1][0][0] = '--- Choose the artist ---';
$select3[1][1][0] = '15.00$';
$select3[1][2][1] = '17.00$';
$select3[2][0][0] = '--- Choose the artist ---';
$select3[2][1][0] = '15.00$';
$select3[2][2][1] = '17.00$';
// Create the Element
$sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
// And add the selection options
$sel->setOptions(array($select1, $select2, $select3));
$form->display();
?>
HTML_Common
HTML_QuickForm_hierselect
HTML_QuickForm_hierselect Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_group::HTML_QuickForm_group() | Class constructor |
HTML_QuickForm_group::accept() | Accepts a renderer |
HTML_QuickForm_group::exportValue() | As usual, to get the group's value we access its elements and call |
HTML_QuickForm_group::getElementName() | Returns the element name inside the group such as found in the html form |
HTML_QuickForm_group::getElements() | Gets the grouped elements |
HTML_QuickForm_group::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_group::getGroupType() | Gets the group type based on its elements Will return 'mixed' if elements contained in the group are of different types. |
HTML_QuickForm_group::getName() | Returns the group name |
HTML_QuickForm_group::getValue() | Returns the value of the group |
HTML_QuickForm_group::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_group::setElements() | Sets the grouped elements |
HTML_QuickForm_group::setName() | Sets the group name |
HTML_QuickForm_group::setValue() | Sets values for group's elements |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_hierselect::HTML_QuickForm_hierselect (
string $elementName
= null
, string $elementLabel
= null
, mixed $attributes
= null
, mixed $separator
= null
)
This package is not documented yet.
$elementName
(optional)Input field name attribute
If you are displaying several forms on one page, then hierselects in these forms should have unique names. In the other case the values for last hierselect with the same name will overwrite the values for the previous one. It is extremely difficult to cleanly fix this behaviour with current QuickForm architecture, therefore please rely on this workaround. See Request #5718.
$elementLabel
(optional)Input field label in form
$attributes
(optional)Either a typical HTML attribute string or an associative array.
$separator
String to separate the grouped elements
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_hierselect::setOptions (
array $options
)
Sets the options for the select elements within hierselect. Note that the actual number of selects that will be displayed is governed by the number of the elements in the array passed to this function.
$options
Array of options for the elements, having the following structure:
The options for subsequent elements should have keys for all options of the previous elements. Having a select without options is invalid HTML and will break hierselect's JavaScript. See also Bug #5218.
throws no exceptions thrown
This function can not be called statically.
since 3.2.2
Setting the hierselect options
<?php
$select1 = $select2 = $select3 = array();
$select1[0] = 'Pop';
$select1[1] = 'Classical';
$select1[2] = 'Funeral doom';
// second select
$select2[0][0] = '--- Artist ---';
$select2[0][1] = 'Red Hot Chil Peppers';
$select2[0][2] = 'The Pixies';
$select2[1][0] = '--- Artist ---';
$select2[1][1] = 'Wagner';
$select2[1][2] = 'Strauss';
$select2[2][0] = '--- Artist ---';
$select2[2][1] = 'Pantheist';
$select2[2][2] = 'Skepticism';
// Create a third select with prices for the cds
$select3[0][0][0] = '--- Choose the artist ---';
$select3[0][1][0] = '15.00$';
$select3[0][2][1] = '17.00$';
$select3[1][0][0] = '--- Choose the artist ---';
$select3[1][1][0] = '15.00$';
$select3[1][2][1] = '17.00$';
$select3[2][0][0] = '--- Choose the artist ---';
$select3[2][1][0] = '15.00$';
$select3[2][2][1] = '17.00$';
// Create the Element
$sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
// And add the selection options
$sel->setOptions(array($select1, $select2, $select3));
?>
void HTML_QuickForm_hierselect::setMainOptions (
array $options
)
Format is standard key/value pairs for select elements. Example is available in the docs for setSecOptions().
This method has been deprecated. Use setOptions() instead.
$options
Array of options for the first select
throws no exceptions thrown
This function can not be called statically.
This function is deprecated. That means that future versions of this package may not support it anymore.
Deprecated in release 3.2.2
void HTML_QuickForm_hierselect::setSecOptions (
array $options
)
Sets the options for the secondary select. Options are passed as a two-dimensional array, where the first key is parent id and the second key is child id, as it is needed to know the parent option to which the secondary option relates.
This method has been deprecated. Use setOptions() instead.
$options
Array of options for the second select
throws no exceptions thrown
see setMainOptions().
This function can not be called statically.
This function is deprecated. That means that future versions of this package may not support it anymore.
Deprecated in release 3.2.2
Setting the hierselect options
<?php
$hierSel =& $form->addElement('hierselect', 'test', 'Test:', null, '/');
$main[0] = 'Pop';
$main[1] = 'Classical';
$main[2] = 'Funeral doom';
$sec[0][1] = 'Red Hot Chili Peppers';
$sec[0][2] = 'The Pixies';
$sec[1][3] = 'Wagner';
$sec[1][4] = 'Strauss';
$sec[2][5] = 'Pantheist';
$sec[2][6] = 'Skepticism';
$hierSel->setMainOptions($main);
$hierSel->setSecOptions($sec);
?>
A pseudo-element used for adding raw HTML to form output. Its contents are output as is, without applying any element template.
This element is deprecated. No fixes and changes to its behaviour will be done and it will be removed in the next major version of the package. If you really need to add raw html to the form, you may consider using 'static' element instead.
The element can be used with the Default Renderer only, all other Renderers completely ignore this. Consider using some template-based renderer instead of relying on this feature.
HTML_Common
HTML_QuickForm_html
HTML_QuickForm_html Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_static::HTML_QuickForm_static() | Class constructor |
HTML_QuickForm_static::exportValue() | We override this here because we don't want any values from static elements |
HTML_QuickForm_static::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_static::getName() | Returns the element name |
HTML_QuickForm_static::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_static::setName() | Sets the element name |
HTML_QuickForm_static::setText() | Sets the text |
HTML_QuickForm_static::setValue() | Sets the text (uses the standard setValue call to emulate a form element. |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_html::HTML_QuickForm_html (
string $text
= null
)
This package is not documented yet.
$text
raw HTML to add
throws no exceptions thrown
This function can not be called statically.
This is used to add <a href="...">...</a> tag within a standard element template.
HTML_Common
HTML_QuickForm_link
HTML_QuickForm_link Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_static::HTML_QuickForm_static() | Class constructor |
HTML_QuickForm_static::exportValue() | We override this here because we don't want any values from static elements |
HTML_QuickForm_static::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_static::getName() | Returns the element name |
HTML_QuickForm_static::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_static::setName() | Sets the element name |
HTML_QuickForm_static::setText() | Sets the text |
HTML_QuickForm_static::setValue() | Sets the text (uses the standard setValue call to emulate a form element. |
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_link::HTML_QuickForm_link (
mixed $elementName
= null
, string $elementLabel
= null
, string $href
= null
, string $text
= null
, mixed $attributes
= null
)
This package is not documented yet.
$elementName
$elementLabel
(optional)Link label
$href
(optional)Link href
$text
(optional)Link display text
$attributes
(optional)Either a typical HTML attribute string or an associative array
throws
since 1.0
This function can not be called statically.
void HTML_QuickForm_link::setHref (
string $href
)
This is just a shortcut for
<?php
$element->updateAttributes(array('href' => $href));
?>
$href
throws
since 1.0
This function can not be called statically.
A static element is an element that cannot have a submit value and thus cannot change due to user input. Such elements are usually used to improve form presentation. Note that elements of HTML_QuickForm_static type are by default rendered inside the same template as the normal form elements.
HTML_Common
HTML_QuickForm_static
Class | Summary |
---|---|
HTML_QuickForm_header | A pseudo-element used for adding headers to form |
HTML_QuickForm_html | A pseudo-element used for adding raw HTML to form |
HTML_QuickForm_link | HTML class for a link type field |
HTML_QuickForm_static Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_static::HTML_QuickForm_static (
string $elementName
= null
, mixed $elementLabel
= null
, string $text
= null
)
This package is not documented yet.
$elementName
Name of the element
$elementLabel
(optional)Label
$text
(optional)Display text
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_static::setText (
string $text
)
This text is essentially the static element itself. Note that setValue() is an alias for setText().
$text
text of the element
throws no exceptions thrown
This function can not be called statically.
To be written.
object &HTML_QuickForm::addGroup (
array $elements
, string $name
= null
, mixed $groupLabel = ''
, string $separator
= null
, string $appendName
= true
)
Adds an element group.
$elements
array of elements composing the group
$name
(optional) group name
$groupLabel
(optional) group label
$separator
(optional) string or array of strings to separate elements
$appendName
(optional) specify whether the group name should be used in the form element name: groupName[elementName] vs elementName
return reference to added group of elements
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_INVALID_ELEMENT_NAME | Element '$elementName ' already exists in HTML_QuickForm::addElement() |
Tried to add a group having a name of an existing element | Choose a different name for a group |
since 2.8
This function can not be called statically.
Using addGroup()
<?php
$group[] =& HTML_QuickForm::createElement('text', 'first', 'First');
$group[] =& HTML_QuickForm::createElement('text', 'last', 'Last');
$form->addGroup($group, 'name', 'Name:', ', ');
?>
Groups allow you to combine several individual elements into one entity and to use it as usual form element. Most of the group's methods use the methods of the grouped elements to do their job. For example, groups do not have values themselves, their setValue() and getValue() methods just call the appropriate methods of grouped elements to set and get their values.
Groups can be used both for visual grouping of the elements (e.g. putting "Submit" and "Reset" buttons on one line), grouping of the elements with the same name (e.g. groups of checkboxes and radiobuttons) and logical grouping of the elements (e.g. group for person's name consisting of two text fields for first and last name).
HTML_Common
HTML_QuickForm_group
Class | Summary |
---|---|
HTML_QuickForm_date | Class for a group of elements used to input dates (and times). |
HTML_QuickForm_hierselect | Class to dynamically create two HTML Select elements The first select changes the content of the second select. |
HTML_QuickForm_group Inherited Methods
Method Name | Summary |
---|---|
Constructor HTML_QuickForm_element::HTML_QuickForm_element() | Class constructor |
HTML_QuickForm_element::accept() | Accepts a renderer |
HTML_QuickForm_element::apiVersion() | Returns the current API version |
HTML_QuickForm_element::exportValue() | Returns a 'safe' element's value |
HTML_QuickForm_element::freeze() | Freeze the element so that only its value is returned |
HTML_QuickForm_element::getFrozenHtml() | Returns the value of field without HTML tags |
HTML_QuickForm_element::getLabel() | Returns display text for the element |
HTML_QuickForm_element::getName() | Returns the element name |
HTML_QuickForm_element::getType() | Returns element type |
HTML_QuickForm_element::getValue() | Returns the value of the form element |
HTML_QuickForm_element::isFrozen() | Returns whether or not the element is frozen |
HTML_QuickForm_element::onQuickFormEvent() | Called by HTML_QuickForm whenever form event is made on this element |
HTML_QuickForm_element::setLabel() | Sets display text for the element |
HTML_QuickForm_element::setName() | Sets the input field name |
HTML_QuickForm_element::setPersistantFreeze() | Sets wether an element value should be kept in an hidden field when the element is frozen or not |
HTML_QuickForm_element::setValue() | Sets the value of the form element |
HTML_QuickForm_element::unfreeze() | Unfreezes the form element |
void constructor HTML_QuickForm_group::HTML_QuickForm_group (
string $elementName
= null
, array $elementLabel
= null
, array $elements
= null
, mixed $separator
= null
, bool $appendName
= true
)
This package is not documented yet.
$elementName
(optional)Group name
$elementLabel
(optional)Group label
$elements
(optional)Group elements
$separator
(optional)Use a string for one separator, use an array to alternate the separators.
$appendName
(optional)whether to change elements' names to the form $groupName[$elementName] or leave them as is.
throws no exceptions thrown
since 1.0
This function can not be called statically.
mixed HTML_QuickForm_group::getElementName (
mixed $index
)
Returns the name of an element inside the group. This is the name that will be shown in the form HTML output.
$index
Element name or element index in the group
returns string with element name, false if not found
throws no exceptions thrown
since 3.0
This function can not be called statically.
array &HTML_QuickForm_group::getElements (
)
This package is not documented yet.
throws no exceptions thrown
since 2.4
This function can not be called statically.
string HTML_QuickForm_group::getGroupType (
)
Will return 'mixed'
if elements contained in the group are of different types.
returns group elements type
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_group::setElements (
array $elements
)
This package is not documented yet.
$elements
Array of elements
throws no exceptions thrown
since 1.1
This function can not be called statically.
This section covers the methods QuickForm offers for managing form element's values and working with submitted values.
void HTML_QuickForm::setConstants (
array $constantValues
= null
, mixed $filter
= null
)
Sets constant form values. These values won't be overridden by either default (set via setDefaults()) or submitted (POST or GET) values.
$constantValues
values used to fill the form, array('element name' => 'element value')
$filter
(optional) filter(s) to apply to all default values
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_INVALID_FILTER | Callback function does not exist in QuickForm::setConstants() | Tried to pass a name of a non-existant function as a callback | Check spelling |
since 2.0
This function can not be called statically.
void HTML_QuickForm::setDefaults (
array $defaultValues
= null
, mixed $filter
= null
)
Sets default form values. There are overriden by either constant (set via setConstants()) or submitted (POST or GET) values.
$defaultValues
values used to fill the form, array('element name' => 'element value')
$filter
(optional) filter(s) to apply to all default values
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_INVALID_FILTER | Callback function does not exist in QuickForm::setDefaults() | Tried to pass a name of a non-existant function as a callback | Check spelling |
since 1.0
This function can not be called statically.
boolean HTML_QuickForm::isSubmitted (
void
)
Tells whether the form was already submitted. Using this method when
$trackSubmit
parameter of QuickForm's
constructor
is used is more reliable than checking whether form's submit
values are empty.
throws no exceptions thrown
This function can not be called statically.
since 3.2.5
mixed HTML_QuickForm::exportValue (
string $element
)
This method first tries to find a cleaned-up submitted value, it will return a value set by setValue()/setDefaults()/setConstants() if submitted value does not exist for the given element.
The value that couldn't have possibly been submitted (e.g.: an option that is not present in <select> element's option list) is not considered valid and is not returned.
As the values returned by this method and by exportValues() are expected to be immediately processed and/or stored somewhere, values for file elements that obviously have special processing needs are not returned.
$element
Name of the element
return element value
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_NONEXIST_ELEMENT | Element '$element ' does not exist in HTML_QuickForm::exportValue() |
Tried to get a value of a non-existant element | Check the element's name spelling |
since 3.1
This function can not be called statically.
array HTML_QuickForm::exportValues (
mixed $elementList
= null
)
Returns the values for the form elements. First it tries to return filtered submitted values, if there were none then it takes the values set by setDefaults() or setConstants().
Unlike getSubmitValues(), this will return only the values corresponding to the elements added to the form and only the values that could actually be submitted: if we have 'Yes'/'No' radiobuttons 'Maybe' will not be considered a valid submit value. You also cannot get values for file elements via this method.
$elementList
Array/string of element names, whose values we want. If not set then return all elements.
return An assoc array of elements' values
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_NONEXIST_ELEMENT | Element '$element ' does not exist in HTML_QuickForm::exportValue() |
Tried to get a value of a non-existant element | Check the element's name spelling |
since 3.1
This function can not be called statically.
mixed &HTML_QuickForm::getElementValue (
string $element
)
Returns the element's raw value such as submitted by the form (not filtered), set by setDefaults() or setConstants().
$element
Element name
return element value
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_NONEXIST_ELEMENT | Element '$element ' does not exist in HTML_QuickForm::getElementValue() |
Tried to get a value of a non-existant element | Check the element's name spelling |
since 2.0
This function can not be called statically.
mixed HTML_QuickForm::getSubmitValue (
string $element
)
Returns the submitted element's value. The value is filtered.
$element
Element name
return submitted element value or NULL if not set
throws no exceptions thrown
since 2.0
This function can not be called statically.
array HTML_QuickForm::getSubmitValues (
bool $mergeFiles
= false
)
Returns the values submitted by the form, possibly with uploaded files as well.
$mergeFiles
Whether uploaded files should be returned too
throws no exceptions thrown
since 2.0
This function can not be called statically.
void HTML_QuickForm::process (
mixed $callback
, bool $mergeFiles
= true
)
Performs the form data processing. It actually calls the $callback
passing the submitted values (and files, when $mergeFiles
=TRUE) to it.
$callback
Callback, either function name or array(&$object, 'method')
$mergeFiles
Whether uploaded files should be processed too
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_INVALID_PROCESS | Callback function does not exist in QuickForm::process() | Tried to pass a name of a non-existant function as a callback | Check spelling |
since 1.0
This function can not be called statically.
QuickForm also provides validation rules support. You can code your own validation rules, register them in QuickForm and then call them in your script. By default, QuickForm can handle validation against regular expressions (preg_match style) and check for required elements. If you want client-side validation, QuickForm can generate the javascript code needed. Server-side validation is always on by default.
QuickForm can also make use of filters for data import into the form or for data processing once the form has been submitted. Filters work the same way as rules except that you don't need to register them. You write your filter functions and call them in your script. You can call any php function (ie. trim, addslashes, htmlentities, etc.) and have them applied recursively to your element values.
QuickForm makes client-side and server-side form validation easy. It allows for validation against regular expressions or custom functions and methods. You can define your own validation rules and apply them to the elements or groups you want. In this section, we will explore the different possibilities QuickForm offers to make validation easier.
QuickForm can verify if required elements are filled when the form is submitted. This works with every type of elements or groups, integer 0 is not considered as an empty value.
<?php
require_once 'HTML/QuickForm.php';
$form = new HTML_QuickForm('myform', 'post');
$form->addElement('text', 'email', 'Your email:');
$form->addElement('submit', 'submit', 'Submit');
// Validation rules
$form->addRule('email', 'E-Mail is required', 'required');
// Validation
if ($form->validate()) {
$form->freeze();
}
$form->display();
?>
On empty elements validationIf the element is empty, no validation rules other than
required
are checked for it. This means that empty element can be invalid only when it is required.
On required uploads
required
rule does not work for file elements. Useuploadedfile
.
The HTML_QuickForm::validate() method will scan through each rules in the order they have been set. If a rule is not validated, the error message corresponding to the element will be displayed and the form will be shown once again. You can use templates to set the position of this error message. The order you decide to set your validation rules is important because it will determine which error message is used.
QuickForm can generate the javascript necessary to validate the form on the client side. This feature works for all standard elements and for groups. Server side validation is always performed in case the client has javascript turned off.
<?php
$form->addRule('email', 'E-Mail is required', 'required', null, 'client');
?>
By setting the parameter
'client'
, you trigger the javascript automatic generation.
QuickForm offers a few registered rules that are often used when validating
forms. Some of the rules may need an extra $format
parameter
passed to addRule() /
addGroupRule()
to work properly.
Name | Description | $format parameter |
---|---|---|
required |
value is not empty | |
maxlength |
value must not exceed given number of characters | number of characters, integer |
minlength |
value must have more than given number of characters | number of characters, integer |
rangelength |
value must have between min and max characters | array(min characters, max characters) |
regex |
value must pass the regular expression | regular expression to check, string |
email |
value is a correctly formatted email | whether to perform an additional domain check via checkdnsrr() function, boolean |
lettersonly |
value must contain only letters | |
alphanumeric |
value must contain only letters and numbers | |
numeric |
value must be a number | |
nopunctuation |
value must not contain punctuation characters | |
nonzero |
value must be a number not starting with 0 | |
compare |
The rule allows to compare the values of two form fields. This can be used for e.g. 'Password repeat must match password' kind of rule. Please note that you need to pass an array of elements' names to compare as a first parameter to addRule(). | Type of comparison to perform, string:
|
callback |
This rule allows to use an external function/method for validation. This can be done either explicitly, by passing a callback as a format parameter or implicitly, by registering it via registerRule(). | Function/method to use, callback. |
Validation rules for file uploads | ||
uploadedfile |
required file upload | |
maxfilesize |
the file size must not exceed the given number of bytes | maximum file size, integer |
mimetype |
the file must have a correct MIME type | either a string for single allowed MIME type, or an array of allowed MIME types |
filename |
the filename must match the given regex | regular expression to test, string |
On rules for file uploadsThese rules are defined in
HTML/QuickForm/file.php
, and are automatically registered when afile
type element is added to the form. These rules are server-side only, for obvious reasons.
Usage of builtin rules is covered in rules-builtin.php
example provided with the package. The rules-custom.php
example covers the usage of custom rule classes and callback
type rules. It also contains a NumericRange class for
checking whether a number is between a maximum and a minimum.
Since release 3.2 all builtin validation is performed by subclasses of HTML_QuickForm_Rule class. You can create your own subclass of it and implement validate() and getValidationScript() methods. Consult the source for the examples.
When you need a more complex validation, QuickForm can use your own custom-made functions to validate an element or a group. QuickForm can also call a method of a class. This way, it is possible to use PEAR's Validate package or any other class. If you want to use your own functions, you basically have two options:
Register the function via registerRule() using 'callback'
as $type
and giving the new rule a custom $ruleName
. Later you add the rule with this name via addRule() like any buitin rule.
Add the rule of callback
type via addRule() passing the name of your function as $format
. This way you'll have less characters to type, but will be unable to pass additional data to your function.
Email validation function
<?php
/**
* Validate an email address
*
* @param string $email Email address to validate
* @param boolean $domainCheck Check if the domain exists
*/
function checkEmail($email, $domainCheck = false)
{
if (preg_match('/^[a-zA-Z0-9\._-]+\@(\[?)[a-zA-Z0-9\-\.]+'.
'\.([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/', $email)) {
if ($domainCheck && function_exists('checkdnsrr')) {
list (, $domain) = explode('@', $email);
if (checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A')) {
return true;
}
return false;
}
return true;
}
return false;
}
$form->registerRule('checkmail', 'callback', 'checkEmail');
$form->addRule('email', 'Email is incorrect', 'checkmail', true);
?>
You can pass an extra parameter of the type you want to your function when set with HTML_QuickForm::addRule(). Here we used TRUE to enable the DNS check of our function.
If you use a method, you must specify the class your method is in. Use this syntax when you register your rule:
<?php
// Method checkEmail is in class Validate
$form->registerRule('checkmail', 'callback', 'checkEmail', 'Validate');
?>
You can also use a javascript function to validate your form, give it the same name as your PHP function, have it return a boolean and set the
'client'
parameter.
Groups of elements can be validated the same way other elements are, or use a more complex validation scheme. To validate a group as a whole, just use HTML_QuickForm::addRule(). The group elements' values will be passed to the validation function as an array.
You can have more complex validation for your groups using the HTML_QuickForm::addGroupRule() method. This allows for a per element validation. It also makes it possible to specify the number of elements that need to be valid for the group to be valid too.
Complex group validation
<?php
// Group
$id[] = &HTML_QuickForm::createElement('text', 'username', 'Username');
$id[] = &HTML_QuickForm::createElement('text', 'code', 'Code');
$form->addGroup($id, 'id', 'Your ID:', '-');
// Validation rules per element
$rule['username'][] = array('Username is required', 'required');
$rule['username'][] = array('Username contains only letters', 'lettersonly');
$rule['username'][] = array('Username has between 5-8 characters', 'rangelength', array(5, 8));
$rule['code'][] = array('Code is required', 'required');
$rule['code'][] = array('Code contains numbers only', 'regex', '/^\d+$/');
$rule['code'][] = array('Code is 5 numbers long', 'rangelength', array(5, 5));
$form->addGroupRule('id', $rule);
?>
In this example, we have set rules for the elements inside our group. Instead of using their names, we could have used their index (determined by the order they were created) in the group, it would speed up the validation process.
The following example takes the same group and will validate the form only if at least one of our two elements is not empty. To achieve this, we use the howmany
parameter and set it to 1.
<?php
$form->addGroupRule('id', 'Fill at least one element', 'required', null, 1);
?>
You have seen that QuickForm makes it easy to validate your elements and groups without having to write all the usually necessary code to find the different values. It takes care of required elements, generates the javascript automatically and adds a lot of flexibility by allowing you to use your own validation functions and regular expressions. It's time to experiment...
If we add a rule like
<?php
$form->addRule('element', 'The element is required', 'required');
?>
to the form, then any input will satisfy it, including, for example, a single space. This is because the rule simply ensures that there are one or more characters present, and a space character satisfies the rule.
Of course this can be fixed by making a custom regex rule, but there is an easier solution. We usually do not care about leading and trailing spaces at all, and we can make the element's value pass through builtin trim() function before doing any validation on it:
<?php
$form->applyFilter('element', 'trim');
?>
Filters are applied recursively, which means that trim() on an array will work, too. You can pass any valid callback as an argument to applyFilter() method.
Use filters if you want to 'sanitize' user input and do not really care about invalid values.
void HTML_QuickForm::addRule (
mixed $element
, string $message
, string $type
, string $format = ''
, string $validation = 'server'
, boolean $reset
= false
, boolean $force
= false
)
If the element is in fact a group, it will be considered as a whole, an array of group elements' values will be passed to validation function. To validate grouped elements as separate entities, use addGroupRule().
$element
Form element name(s). Currently the only builtin rule that expects and correctly handles an array here is compare
:
<?php
$form->addElement('password', 'cmpPasswd', 'Password:');
$form->addElement('password', 'cmpRepeat', 'Repeat password:');
$form->addRule(array('cmpPasswd', 'cmpRepeat'), 'The passwords do not match', 'compare', null, 'client');
?>
All other builtin rules will only handle a single element name. callback
rules can also handle an array here, but the callback function you provide will obviously need to properly handle an array of values.
$message
Message to display for invalid data
$type
Rule type, use getRegisteredRules() to get types. You can also pass a classname for a descendant of HTML_QuickForm_Rule or an instance of such class.
$format
(optional) Required for extra rule data
$validation
(optional) Where to perform validation: "server", "client"
$reset
For client-side validation: reset the form element to its original value if there is an error?
$force
Force the rule to be applied, even if the target form element does not exist
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_NONEXIST_ELEMENT | Element '$element ' does not exist in HTML_QuickForm::addRule() |
Tried to add a rule for a non-existant element | Check the element's name spelling or use $force to suppress the error. |
QUICKFORM_INVALID_RULE | Rule '$type ' is not registered in HTML_QuickForm::addRule() |
Rule is not known to QuickForm | Check rule type spelling or use HTML_QuickForm::registerRule(). |
since 1.0
This function can not be called statically.
void HTML_QuickForm::addGroupRule (
string $group
, mixed $arg1
, string $type = ''
, string $format = ''
, int $howmany = 0
, string $validation = 'server'
, bool $reset
= false
)
Adds a validation rule for the given group of elements
Only groups with a name can be assigned a validation rule. Use addGroupRule() when you need to validate elements inside the group. Also use addRule() if you need to validate the group as a whole.
$group
Form group name
$arg1
Array for multiple elements or error message string for one element. If this is the array, its structure is the following:
rule data here matches the parameters' order and meaning for addRule() method.
If this parameter is an array, all the subsequent parameters are ignored. You should pass all the modifiers for the rules being added within this array (see the example below).
$type
(optional) Rule type. Use getRegisteredRules() to get types. You can also pass a classname for a descendant of HTML_QuickForm_Rule or an instance of such class.
$format
(optional) Required for extra rule data
$howmany
(optional) How many valid elements should be in the group
$validation
(optional)Where to perform validation: "server", "client"
$reset
Client-side: whether to reset the element's value to its original state if validation failed.
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_NONEXIST_ELEMENT | Group '$group ' does not exist in HTML_QuickForm::addGroupRule() |
Tried to add a rule for a non-existant group | Check the group name spelling |
QUICKFORM_NONEXIST_ELEMENT | Element '$elementIndex ' not found in group '$group ' in HTML_QuickForm::addGroupRule() |
$arg1 is an array and contains an index for an element not present in a group |
Check the element index spelling |
QUICKFORM_INVALID_RULE | Rule '$type ' is not registered in HTML_QuickForm::addGroupRule() |
Rule is not known to QuickForm | Check rule type spelling or use HTML_QuickForm::registerRule(). |
since 2.5
This function can not be called statically.
Using addGroupRule()
<?php
// a group of 4 checkboxes
$checkbox[] = &HTML_QuickForm::createElement('checkbox', 'A', null, 'A');
$checkbox[] = &HTML_QuickForm::createElement('checkbox', 'B', null, 'B');
$checkbox[] = &HTML_QuickForm::createElement('checkbox', 'C', null, 'C');
$checkbox[] = &HTML_QuickForm::createElement('checkbox', 'D', null, 'D');
$form->addGroup($checkbox, 'ichkABCD', 'ABCD:', array(' ', '<br />'));
// Simple rule: at least 2 checkboxes should be checked
$form->addGroupRule('ichkABCD', 'Please check at least two boxes', 'required', null, 2);
$idGrp[] = &HTML_QuickForm::createElement('text', 'lastname', 'Name', array('size' => 30));
$idGrp[] = &HTML_QuickForm::createElement('text', 'code', 'Code', array('size' => 5, 'maxlength' => 4));
$form->addGroup($idGrp, 'id', 'ID:', ', ');
// Complex rule for group's elements
$form->addGroupRule('id', array(
'lastname' => array(
array('Name is letters only', 'lettersonly'),
array('Name is required', 'required', null, 'client')
),
'code' => array(
array('Code must be numeric', 'numeric')
)
));
?>
void HTML_QuickForm::addFormRule (
mixed $rule
)
This should be used when you want to add a rule involving several fields or if you want to use some completely custom validation for your form. The rule function/method should return TRUE in case of successful validation and array('element name' => 'error') when there were errors.
$rule
A valid callback
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_INVALID_RULE | Callback function does not exist in HTML_QuickForm::addFormRule() | Tried to pass a name of a non-existant function as a callback | Check spelling |
since 3.1
This function can not be called statically.
Using addFormRule()
<?php
require_once ('HTML/QuickForm.php');
$form = new HTML_QuickForm();
// the function checks whether the passwords are the same
function cmpPass($fields)
{
if (strlen($fields['passwd1']) && strlen($fields['passwd2']) &&
$fields['passwd1'] != $fields['passwd2']) {
return array('passwd1' => 'Passwords are not the same');
}
return true;
}
$form->addElement('password', 'passwd1', 'Enter password');
$form->addElement('password', 'passwd2', 'Confirm password');
$form->addFormRule('cmpPass');
?>
boolean HTML_QuickForm::isElementRequired (
string $element
)
Returns whether or not the form element is required, i.e. whether a 'required' rule was added for it.
$element
Form element name
throws no exceptions thrown
since 1.0
This function can not be called statically.
boolean HTML_QuickForm::validate (
void
)
Performs the server side validation.
return TRUE if no error found
throws no exceptions thrown
since 1.0
This function can not be called statically.
string HTML_QuickForm::getElementError (
string $element
)
Returns error corresponding to validated element. Errors are usually assigned to elements by validate() method.
$element
Name of form element to check
return error message corresponding to checked element
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm::setElementError (
string $element
, string $message
)
Set error message for a form element. Errors are usually assigned to elements by validate() method. Use this if you need to explicitly set error message for an element.
$element
Name of form element to set error for
$message
Error message
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm::registerRule (
string $ruleName
, string $type
, string $data1
, string $data2
= null
)
Registers a new validation rule
$ruleName
Name of validation rule
$type
Either: 'regex'
or 'callback'
('function'
is also kept for backward compatibility). If registering a subclass of HTML_QuickForm_Rule you can pass anything here, preferrably NULL or empty string.
$data1
Name of function, regular expression, classname of HTML_QuickForm_Rule subclass or an instance of such class.
The callback function needs to return true
or
false
, determining if the rule is passed or not.
$data2
Object parent of above function, name of the file containing the subclass of HTML_QuickForm_Rule.
throws no exceptions thrown
since 1.0
This function can not be called statically.
array HTML_QuickForm::getRegisteredRules (
void
)
Returns an array of registered validation rules.
throws no exceptions thrown
since 1.0
This function can not be called statically.
boolean HTML_QuickForm::isRuleRegistered (
string $name
)
Returns whether or not the given rule is supported. New rules are registered via registerRule() method.
$name
Validation rule name
throws no exceptions thrown
since 1.0
This function can not be called statically.
void HTML_QuickForm::applyFilter (
mixed $element
, mixed $filter
)
Applies a data filter for the given field(s). Filter is applied recursively.
$element
Form element name or array of such names. Special name '__ALL__'
means all the form elements.
$filter
Callback, either function name or array(&$object, 'method')
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_INVALID_FILTER | Callback function does not exist in QuickForm::applyFilter() | Tried to pass a name of a non-existant function as a callback | Check spelling |
since 2.0
This function can not be called statically.
Your form can be customised in many ways. QuickForm can use different kind of renderers and provides a default one which allows for customization of the form, the elements, the error messages, the headers, the required elements note and the required elements sign. You can also write your own renderers.
void HTML_QuickForm::freeze (
mixed $elementList
= null
)
Freezes the elements: elements' values will be displayed without HTML input tags.
$elementList
array or string of element(s) to be frozen. If NULL then all the form's elements will be frozen.
Error code | Error message | Reason | Solution |
---|---|---|---|
QUICKFORM_NONEXIST_ELEMENT | Element '$element ' does not exist in HTML_QuickForm::freeze() |
Tried to freeze a non-existant element | Check the element's name spelling |
since 1.0
This function can not be called statically.
boolean HTML_QuickForm::isElementFrozen (
string $element
)
Returns whether or not the form element is frozen.
$element
Form element name
throws no exceptions thrown
since 1.0
This function can not be called statically.
boolean HTML_QuickForm::isFrozen (
void
)
Returns whether or not the whole form is frozen.
throws no exceptions thrown
since 3.0
This function can not be called statically.
void HTML_QuickForm::setRequiredNote (
string $note
)
Sets the required note. This note is usually displayed below the form when the form contains required fields.
$note
Message indicating some elements are required
throws no exceptions thrown
since 1.1
This function can not be called statically.
void HTML_QuickForm::setJsWarnings (
string $pref
, string $post
)
Sets JavaScript warning messages: the first is displayed before element errors, the second after them. Useless if the form had no rules added with 'client' modifier.
$pref
Prefix warning
$post
Postfix warning
throws no exceptions thrown
since 1.1
This function can not be called statically.
string HTML_QuickForm::getRequiredNote (
void
)
Returns the required note. This note is usually displayed below the form when the form contains required fields.
throws no exceptions thrown
since 2.0
This function can not be called statically.
string HTML_QuickForm::getValidationScript (
void
)
Returns JavaScript used to validate the form on client side. Returns an empty string if no rules were added with 'client' modifier.
throws no exceptions thrown
since 2.0
This function can not be called statically.
void HTML_QuickForm::accept (
object &$renderer
)
Accepts a renderer. This method is a part of the Visitor design pattern implementation.
&$renderer
HTML_QuickForm_Renderer object
throws no exceptions thrown
since 3.0
This function can not be called statically.
object &HTML_QuickForm::defaultRenderer (
void
)
Returns a reference to default renderer object. You can use this method to save a few keystrokes:
<?php
$renderer =& $form->defaultRenderer();
?>
instead of
<?php
require_once 'HTML/QuickForm/Renderer/Default.php';
$renderer =& new HTML_QuickForm_Renderer_Default();
?>
It is also used internally to implement toHtml() and some of the deprecated methods.
return default renderer object
throws no exceptions thrown
since 3.0
This function can be called statically.
array HTML_QuickForm::toArray (
boolean $collectHidden
= false
)
Returns the form's contents in an array, uses Array renderer for this.
$collectHidden
Whether to collect hidden elements (passed to the Renderer's constructor)
return array of form contents, structure is described in renderer documentation.
throws no exceptions thrown
since 2.0
This function can not be called statically.
string HTML_QuickForm::toHtml (
string $in_data
= null
)
Returns an HTML version of the form, uses Default renderer for this.
$in_data
(optional) Any extra data to insert right before form is rendered. Data will be inserted via addElement('html', $in_data)
return Html version of the form
throws no exceptions thrown
since 1.0
This function can not be called statically.
string HTML_QuickForm::display (
)
Outputs an HTML version of the form, uses Default renderer for this.
throws no exceptions thrown
since 1.0
This function can not be called statically.
These renderers are based on pre-3.0 HTML_QuickForm code and require no external classes to do their work.
Before release 3.0, form outputting logic was implemented as methods of HTML_QuickForm class. This led to two problems:
Bloat of the said class (80+ methods!)
Difficulties in adding new output logic (i.e. using template engines)
In release 3.0, new system was implemented. The form output logic is now contained in classes that extend HTML_QuickForm_Renderer, their behaviour is based on Visitor design pattern from the classic "Design Patterns" book. This gives the following advantages:
Code for some particular output method is loaded only when the method is used.
It is much easier to add new output method.
There are 8 renderers available since release 3.1.1, of which 2 are based on pre-3.0 code and 6 are new. The following template engines are directly suported: Smarty, HTML_Template_Sigma, HTML_Template_IT, HTML_Template_Flexy.
The main steps of using any available renderer are quite similar:
<?php
// include the renderer class
require_once 'HTML/QuickForm/Renderer/FooBar.php';
// instantiate the renderer
$renderer =& new HTML_QuickForm_Renderer_FooBar($options);
// do some customization
$renderer->adjustSomething('element1', '...');
// ...
$renderer->adjustSomething('elementN', '...');
// process the form
$form->accept($renderer);
// output the results
$renderer->toFooBar();
?>
Actual methods for customization and doing the output will of course depend on renderer type.
Concerning usage examplesUsage examples provided in the manual are pretty basic. More complex examples for Default renderer can be found in
docs/
directory, for all other renderers - indocs/renderers/
directory of HTML_QuickForm.
This renderer directly generates and outputs form HTML. It is based on pre-3.0 built-in form output logic.
The renderer has built-in templates for elements, their format is similar to that of HTML_Template_Sigma or HTML_Template_IT (but only placeholders and blocks are supported). When renderer's renderSomething() method is called, it finds the template for the element, makes necessary substitutions and appends the result to the form's HTML.
It is recommended to use the Default renderer when you are not using any template engine in your application or do not need to do any customization to form output. It is the fastest way to output a form.
Default renderer usage
<?php
require_once 'HTML/QuickForm/Renderer/Default.php';
$renderer =& new HTML_QuickForm_Renderer_Default();
$form->accept($renderer);
echo $renderer->toHtml();
?>
HTML_QuickForm::toHtml() method uses the Default renderer internally.
void HTML_QuickForm_Renderer_Default::clearAllTemplates (
void
)
Clears all the HTML out of the templates that surround notes, elements, etc.
Useful when you want to use addElement('html', '...') to create a completely custom form look.
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Default::setElementTemplate (
string $html
, string $element
= null
)
Sets element template. When called with one parameter, it sets the default element template, when
called with two parameters it sets template for the concrete element. The template should include
at least the {element}
placeholder.
The default element template is
$html
The HTML surrounding an element
$element
(optional) Name of the element to apply template for
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Default::setFormTemplate (
string $html
)
Sets form template. The template should include {attributes}
and {content}
placeholders.
The default form template is
$html
The HTML surrounding the form tags
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Default::setGroupElementTemplate (
string $html
, string $group
)
Sets element template for elements within a group.
By default, the template is empty.
$html
The HTML surrounding an element
$group
Name of the group to apply template for
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Default::setGroupTemplate (
string $html
, string $group
)
Sets template for a group wrapper
This template is contained within a group-as-element template set via setElementTemplate() and contains group's element templates, set via setGroupElementTemplate().
By default, the template is empty.
$html
The HTML surrounding group elements
$group
Name of the group to apply template for
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Default::setHeaderTemplate (
string $html
)
Sets header template. The template should include the {header}
placeholder.
The default header template is
$html
The HTML surrounding the header
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Default::setRequiredNoteTemplate (
string $html
)
Sets the note indicating required fields template. The template should include the {requiredNote}
placeholder.
The default template is
$html
The HTML surrounding the required note
throws no exceptions thrown
This function can not be called statically.
string HTML_QuickForm_Renderer_Default::toHtml (
void
)
Returns the HTML generated for the form.
throws no exceptions thrown
This function can not be called statically.
This renderer does not output anything itself, it returns the form structure as an
array. This array can later be used for generating the output. A usage example is
available for this renderer and Smarty template engine, look in
docs/renderers
directory.
The form array structure is the following:
where element_i is an array of the form:
HTML_QuickForm::toArray() method uses the Array renderer internally.
void HTML_QuickForm_Renderer_Array::HTML_QuickForm_Renderer_Array (
bool $collectHidden
= false
bool $staticLabels
= false
)
This package is not documented yet.
$collectHidden
TRUE: collect all hidden elements into string; FALSE: process them as usual form elements
$staticLabels
TRUE: instead of putting an array of labels into the
'label'
element of resultant array, create
separate keys for them named 'label_$key'
where $key is the key in the array of labels (key + 1 if it is numeric).
The first element of the array occupies the 'label'
element.
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Array::setElementStyle (
mixed $elementName
, string $styleName
= null
)
Sets a style to use for element rendering (this style can later be checked by e.g. a template engine).
$elementName
element name or array ('element name' => 'style name')
$styleName
style name if $elementName
is not an array
throws no exceptions thrown
This function can not be called statically.
array HTML_QuickForm_Renderer_Array::toArray (
void
)
Returns an array representation of the form built by the renderer.
throws no exceptions thrown
This function can not be called statically.
This renderer does not output anything itself, it returns the form structure as an
object. This object can later be used for generating the output. A usage example is
available for this renderer and the Flexy template engine, look in
docs/renderers
directory.
The form object structure is similar to the following:
HTML_QuickForm::toObject() method uses the object renderer internally.
void HTML_QuickForm_Renderer_Object::HTML_QuickForm_Renderer_Object (
bool $collectHidden
= false
)
This package is not documented yet.
$collectHidden
TRUE: collect all hidden elements into string; FALSE: process them as usual form elements
throws no exceptions thrown
This function can not be called statically.
QuickformForm HTML_QuickForm_Renderer_Object::toObject (
void
)
Returns an object representation of the form built by the renderer.
throws no exceptions thrown
This function can not be called statically.
These renderers use template engines to actually generate the HTML for the form.
This renderer was written by Thomas Schulz. It is based on pre-3.0
HTML_QuickForm::toArray() code and ITStatic renderer. It can be used to
output the form into the 'static' Smarty template. Usage example for this is available in
docs/renderers
.
The form array structure is the following:
where an element array has the form:
void HTML_QuickForm_Renderer_ArraySmarty::HTML_QuickForm_Renderer_ArraySmarty (
mixed &$tpl
)
This package is not documented yet.
&$tpl
A Smarty object to use for form output
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_ArraySmarty::setErrorTemplate (
string $template
)
You can use {$label} or {$html} placeholders to let the renderer know where where the element label or the element html are positionned according to the error message. They will be replaced accordingly with the right value. The error message will replace the {$error} placeholder. For example: {if $error}<span style="color: red;">{$error}</span>{/if}{$html} will put the error message in red on top of the element html.
If you want all error messages to be output in the main error block, use the {$form.errors} part of the rendered array that collects all raw error messages.
If you want to place all error messages manually, do not specify {$html} nor {$label}.
Groups can have special layouts. With this kind of groups, you have to place the formated error message manually. In this case, use {$form.group.error} where you want the formated error message to appear in the form.
$template
The element error template
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_ArraySmarty::setRequiredTemplate (
string $template
)
You can use {$label} or {$html} placeholders to let the renderer know where where the element label or the element html are positionned according to the required tag. They will be replaced accordingly with the right value. You can use the full smarty syntax here, especially a custom modifier for I18N. For example: {if $required}<span style="color: red;">*</span>{/if}{$label|translate} will put a red star in front of the label if the element is required and translate the label.
$template
The required element template
throws no exceptions thrown
This function can not be called statically.
The word 'dynamic' in renderer name means that exact form layout is defined at script runtime. This also means that you can create one template file for all your forms. That template should contain a block for every distinct element 'look' appearing in your forms and also some special blocks. If a special block is not set for an element, the renderer falls back to a default one.
If most of your forms tend to share the same look (a good example would be back-office interface), your best bet will be to use Dynamic renderer. If each of your forms has a really special layout, you should go with a Static one.
This is a somewhat minimal template, but it contains all the necessary elements to render any form you are able to define with the package.
qf_main_loop
This block should always be present and should be a parent for all visible elements' blocks. It is used to implement "flow", to render elements one after another.
qf_element
This is the default block for rendering form elements. This block should always be present, as the renderer uses the following logic to decide which block to use for an element's output:
If a special block was set for an element, use that
If a block qf_%element's type%
(e.g. qf_password
) exists, use that
Use qf_element
qf_element_required
The block will be touch'd if an element is required.
qf_element_error
An error associated with an element will be output here.
qf_header
This is the default block used to output headers. Should be present unless you have no headers in your form.
qf_group
Default block to output groups. Should be present unless you do not use grouped elements.
qf_group_loop
An analog of qf_main_loop
, used to render group's elements
one after another. A %group's block%_loop
should
always be present inside %group's block%
qf_group_element
A default block to render group's elements, %group's block%_element
should always be present inside %group's block%_loop
(for the
same reason as the qf_element
should be present in
qf_main_loop
)
qf_error_loop
This is used to output collected errors for the elements that do not have a
%element's block%_error
block.
qf_hidden_loop
<input type="hidden" /> elements are rendered here.
{qf_javascript}
Javascript for client-side form validation will be output here.
{qf_required_note}
If there are required elements in the form, a note will be output here
Assuming the template is in ./qform.html
:
<?php
require_once 'HTML/QuickForm/Renderer/ITDynamic.php';
require_once 'HTML/Template/Sigma.php';
// Instantiate the template object and load the template file
$tpl =& new HTML_Template_Sigma('.');
$tpl->loadTemplateFile('qform.html');
// Instantiate the renderer and process the form
$renderer =& new HTML_QuickForm_Renderer_ITDynamic($tpl);
$form->accept($renderer);
// Output the results
$tpl->show();
?>
Note that we do not show how the form is built here, that's because the example will work for virtually any form.
Template class compatibilityThis renderer was developed and tested using HTML_Template_Sigma. While it probably will work with HTML_Template_IT, no warranties can be given.
Removing empty blocksThe renderer assumes that the template object was set up to remove empty blocks (this is the default behaviour). If that is not true, the results will be pretty discouraging.
void HTML_QuickForm_Renderer_ITDynamic::HTML_QuickForm_Renderer_ITDynamic (
object &$tpl
)
This package is not documented yet.
&$tpl
HTML_Template_ITX/HTML_Template_Sigma object to use
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_ITDynamic::setElementBlock (
mixed $elementName
, string $blockName
= null
)
Sets the block to use for element rendering.
$elementName
element name or array ('element name' => 'block name')
$blockName
block name if $elementName
is not an array
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_ITDynamic::setHeaderBlock (
string $blockName
)
Sets the name of a block to use for header rendering
$blockName
block name
throws no exceptions thrown
This function can not be called statically.
The word 'static' in renderer name means that exact form layout is defined before the execution of the script starts. That also means that you should have an unique template for each form you want to display with this renderer.
The IT Static renderer, as opposed to the IT Dynamic renderer, offers more flexibility in the way you display your forms but might also takes more time to implement. Therefore, if your form is using always the same pattern to display form element labels and form element html, it is recommended to use the Dynamic renderer. On the other hand, if you have many different layouts for your form elements, for example if you alternate text and form elements, you will prefer to use the Static renderer.
With the ITStatic renderer, you have to know beforehand which elements (or group of elements) will compose your form. So we will start by defining our elements.
Defining the elements
<?php
require_once ("HTML/QuickForm.php");
$form = new HTML_QuickForm('myform', 'POST');
$form->addElement('header', 'myheader', 'Registration form');
$form->addElement('hidden', 'session', '123456');
$name['last'] = &HTML_QuickForm::createElement('text', 'first', 'First', array('size' => 10));
$name['first'] = &HTML_QuickForm::createElement('text', 'last', 'Last', array('size' => 10));
$form->addGroup($name, 'name', 'Name:', '-');
$form->addElement('text', 'email', 'Your email:');
$select = array('' => 'Please select...',
'AU' => 'Australia',
'FR' => 'France',
'DE' => 'Germany',
'IT' => 'Italy');
$form->addElement('select', 'country', 'Country:', $select);
$form->addElement('reset', 'reset', 'Reset');
$form->addElement('submit', 'submit', 'Register');
$form->addElement('checkbox', 'news', '', " Check this box if you don't want to receive our newsletter.");
?>
Now our form contains these elements:
One header: 'Registration form'
One hidden field
One group of two textfields: firstname and lastname
One textfield for the E-Mail address
One selectbox for the country with an empty default value
Two buttons: Reset and Register
One checkbox to subscribe to the newsletter
Now that we know which elements compose our form, it will be easy to layout the form
using a WYSIWYG or an HTML editor. The idea is to use placeholders instead of labels
and element html. The placeholders are named after the form and element or group
names: formName_elementName
or formName_groupName_elementName
. Then
you add _label
or _html
. We will save the following code in a
template.html
file.
A template
<html> <head><title>My Form</title> {myform_javascript} </head> <body> <form {myform_attributes}> {myform_session_html} <table> <tr><th colspan="2">{myform_myheader}</th></tr> <tr><td>{myform_name_label}</td><td>{myform_name_html}</td></tr> <tr><td>{myform_email_label}</td><td>{myform_email_html}</td></tr> <tr><td>{myform_country_label}</td><td>{myform_country_html}</td></tr> <tr><td colspan="2" align="right">{myform_reset_html} {myform_submit_html}</td></tr> </table> <br /><br /> {myform_news_html} </form> </body> </html>
As you can notice, the layout is static. This makes the Static renderer only useful if
your form does not change on runtime. Nevertheless, you can still hide blocks if you
use the removeEmptyBlocks
option of
HTML_Template_IT /
HTML_Template_Sigma.
Now that we have the elements and the template, we are going to use our Static renderer.
Using the renderer
<?php
require_once 'HTML/Template/Sigma.php';
require_once 'HTML/QuickForm/Renderer/ITStatic.php';
$tpl =& new HTML_Template_Sigma('.');
$tpl->loadTemplateFile('template.html'); // or whatever you called it
$renderer =& new HTML_QuickForm_Renderer_ITStatic($tpl);
$renderer->setRequiredTemplate('{label}<font color="red" size="1">*</font>');
$renderer->setErrorTemplate('<font color="red">{error}</font><br />{html}');
$form->accept($renderer);
$tpl->show();
?>
You can optionally specify the way your required elements and validation errors are
rendered using respectively setRequiredTemplate() and
setErrorTemplate(). In the given string, you will place either the
{label}
or the {html}
placeholder at
the position you want. If you prefer to have all your errors displayed at the same
place, pass an empty string to setErrorTemplate() and add this
block to your template at the position you want:
<!-- BEGIN myform_error_loop --> <font color="red">{myform_error}</font><br /> <!-- END myform_error_loop -->
With the Static renderer, it is also possible to customize the way your groups are rendered. If you simply use a placeholder for your whole group, the group will be rendered using HTML_QuickForm default renderer. This means that if a separator string (or an array of separators) was specified, it will be used the usual way. In our form, the group called 'name' was rendered this way, using a - to separate the elements.
The problem we have is that elements inside the group don't show their label, so it is not possible to guess which textfield is firstname and which one is lastname. By replacing the layout for the element 'name' by these new placeholders, can customize the way the group will be displayed:
(...) <tr> <td>{myform_name_label}</td> <td> <!-- BEGIN myform_name_error -->{myform_name_error}<!-- END myform_name_error --> <table> <tr><td>{myform_name_first_html}</td><td>{myform_name_last_html}</td></tr> <tr><td>{myform_name_first_label}</td><td>{myform_name_last_label}</td></tr> </table> </td> </tr> (...)
We need to add a new block and placeholder to let the renderer know where to display the error relative to this group. As you have noticed, every placeholder take the name of his group along with its own name.
Also note that if you use elements with the same name, like radios that are not in a group, you will have to add an index to the placeholder name starting at 0, like this:
{myform_myradio_0_html}
,{myform_myradio_1_html}
...
We have seen how to use the Static renderer with standard elements and groups of
elements. We have seen how to display errors and required tags. They won't show in your
form because we did not add any validation rules. Feel free to try to add them as an
exercise. You can also add a special placeholder to your template
{myform_required_note}
, it will display the note that indicates
how to find required elements. This renderer usage is very easy, when you feel
comfortable with it, you can move on to the Dynamic renderer which might also fit your
needs in an other way.
void HTML_QuickForm_Renderer_ITStatic::HTML_QuickForm_Renderer_ITStatic (
object &$tpl
)
This package is not documented yet.
&$tpl
HTML_Template_IT or other compatible Template object to use
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_ITStatic::setErrorTemplate (
string $template
)
You can use {label} or {html} placeholders to let the renderer know where where the element label or the element html are positionned according to the error message. They will be replaced accordingly with the right value. The error message will replace the {error} place holder. For example: <font color="red">{error}</font>{html} will put the error message in red on top of the element html.
If you want all error messages to be output in the main error block, do not specify {html} nor {label}.
Groups can have special layouts. With this kind of groups, the renderer will need to know where to place the error message. In this case, use error blocks like: <!-- BEGIN form_group_error -->{form_group_error}<!-- END form_group_error --> where you want the error message to appear in the form.
$template
The element error template
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_ITStatic::setRequiredTemplate (
string $template
)
You can use {label} or {html} placeholders to let the renderer know where where the element label or the element html are positionned according to the required tag. They will be replaced accordingly with the right value. For example: <font color="red">*</font>{label} will put a red star in front of the label if the element is required.
$template
The required element template
throws no exceptions thrown
This function can not be called statically.
This renderer allows you to output your form using a Flexy template.
A static and dynamic usage example is available in the
docs/renderers
directory.
The following is an example of using the ObjectFlexy renderer with a simple template.
Defining the elements
<?php
require_once 'HTML/Template/Flexy.php';
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/Renderer/ObjectFlexy.php';
// Form name will be used to find the placeholders.
$form = new HTML_QuickForm('form', 'POST');
// Personal information
$form->addElement('header', 'personal', 'Personal Information');
$form->addElement('text', 'email', 'Your email:');
$form->addElement('password', 'pass', 'Your password:', 'size=10');
$name['last'] = &HTML_QuickForm::createElement('text', 'first', 'First', 'size=10');
$name['first'] = &HTML_QuickForm::createElement('text', 'last', 'Last', 'size=10');
$form->addGroup($name, 'name', 'Name:', ', ');
$areaCode = &HTML_QuickForm::createElement('text', '', null,'size=4 maxlength=3');
$phoneNo1 = &HTML_QuickForm::createElement('text', '', null, 'size=4 maxlength=3');
$phoneNo2 = &HTML_QuickForm::createElement('text', '', null, 'size=5 maxlength=4');
$form->addGroup(array($areaCode, $phoneNo1, $phoneNo2), 'phone', 'Telephone:', '-');
// Company information
$form->addElement('header', 'company_info', 'Company Information');
$form->addElement('text', 'company', 'Company:', 'size=20');
$str[] = &HTML_QuickForm::createElement('text', '', null, 'size=20');
$str[] = &HTML_QuickForm::createElement('text', '', null, 'size=20');
$form->addGroup($str, 'street', 'Street:', '<br />');
$addr['zip'] = &HTML_QuickForm::createElement('text', 'zip', 'Zip', 'size=6 maxlength=10');
$addr['city'] = &HTML_QuickForm::createElement('text', 'city', 'City', 'size=15');
$form->addGroup($addr, 'address', 'Zip, city:');
$select = array('' => 'Please select...', 'AU' => 'Australia', 'FR' => 'France', 'DE' => 'Germany', 'IT' => 'Italy');
$form->addElement('select', 'country', 'Country:', $select);
// Other elements
$form->addElement('checkbox', 'news', '', " Check this box if you don't want to receive our newsletter.");
$form->addElement('reset', 'reset', 'Reset');
$form->addElement('submit', 'submit', 'Register');
// Tries to validate the form
if ($form->validate()) {
// Form is validated, then freezes the data
$form->freeze();
}
// setup a template object
$options = &PEAR::getStaticProperty('HTML_Template_Flexy','options');
$options = array(
'templateDir' => './templates',
'compileDir' => './templates/build',
'forceCompile' => 1,
'debug' => 0,
'local' => 'en'
);
$template = new HTML_Template_Flexy($options);
$renderer =& new HTML_QuickForm_Renderer_ObjectFlexy($template);
$renderer->setLabelTemplate("label.html");
$renderer->setHtmlTemplate("html.html");
$form->accept($renderer);
$view = new StdClass;
$view->form = $renderer->toObject();
$template->compile("flexy-static.html");
$template->outputObject($view);
?>
The template files used above are the following:
label.html
A template for the labels
html.html
A template for the overall html
flexy-static.html
A template for the form
For more information on Flexy templating, see the package homepage.
void HTML_QuickForm_Renderer_ObjectFlexy::HTML_QuickForm_Renderer_ObjectFlexy (
object &$flexy
)
This package is not documented yet.
&$flexy
HTML_Template_Flexy or other compatible Template object to use
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_ObjectFlexy::setHtmlTemplate (
string $template
)
In your template, {html} is replaced by the unmodified html. If the element is required, {required} will be true. Eg.
$template
Filename of template
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_ObjectFlexy::setLabelTemplate (
string $template
)
Set the filename of the template to render form labels. In your template, {label} is replaced by the unmodified label, {error} will be set to the error, if any. {required} will be true if this is a required field Eg.
$template
Filename of template
throws no exceptions thrown
This function can not be called statically.
This renderer has three main distinctives: an easy way to create custom-looking forms, the ability to separate the creation of form elements from their display, and being able to use QuickForm in widget-based template systems.
All of the renderers allow you to create a custom-looking form. However, unlike the default renderer, QuickHtml is solely for creating custom forms and has the additional benefits discussed below.
It is often desirable to separate the creation of form elements and their accompanying rules from their actual display on a page. For example, in the MVC design pattern it may be useful to create the form elements and their rules in the Model classes which control how the submitted data will be saved (field lengths, allowed characters, etc.), but leave the rendering of those form elements to the View classes which only care about how to make the page look a certain way.
A widget is a chunk of re-usable html which can be used with other widgets to create a webpage. Any template system which supports widgets (and nearly all do) can be used with QuickHtml. An example widget may consist of a table with reserved places for the form elements. The form elements would be rendered into those places and then the form tags, any remaining form elements (such as hidden elements), and any accompanying javascript for validation would be wrapped around the table widget.
The following is an example usage of the QuickHtml renderer. The
"template" system we use is just a simple html widget in which the form
elements are placed, but it could be replaced with a more complex
template system. A more complex usage example is in
docs/renderers/QuickHtml_example.php
QuickHtml renderer usage
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/Renderer/QuickHtml.php';
// Create form elements and renderer
$form =& new HTML_QuickForm('tmp_form','POST');
$renderer =& new HTML_QuickForm_Renderer_QuickHtml();
// Create a hidden element. Note how we don't render it explicitly,
// rather we let QuickHtml render it for us at the beginning of the form.
$form->addElement('hidden','tmp_hidden');
// Create 2 radio elements. Note how we have to give a value when rendering
// them since that is the only way to tell them apart.
$form->addElement('radio','tmp_radio',null,null,'Y');
$form->addElement('radio','tmp_radio',null,null,'N');
// Create a group with a rule
$text = array();
$text[] =& HTML_QuickForm::createElement('text','',null,array('size' => 3));
$text[] =& HTML_QuickForm::createElement('text','',null,array('size' => 4));
$text[] =& HTML_QuickForm::createElement('text','',null,array('size' => 3));
$form->addGroup($text, 'tmp_phone', null, '-');
$form->addRule('tmp_phone','Phone number cannot be empty.','required',null,'client');
// Do the magic of creating the form. NOTE: order is important here: this must
// be called after creating the form elements, but before rendering them.
$form->accept($renderer);
// Create a very basic template for our form
$html_template = '
<div style="text-align: center; font-weight: bold;">QuickHtml - Basic Example</div>
<div style="text-align: center; background-color: #ccc; margin: 1px;">Phone: %phoneNumber%</div>
<div style="text-align: left; background-color: #ccc; margin: 1px;">Yes: %radioElementYes% No: %radioElementNo%</div>';
// Interpolate the form elements into our template
$html_template = str_replace('%phoneNumber%', $renderer->elementToHtml('tmp_phone'), $html_template);
$html_template = str_replace('%radioElementYes%', $renderer->elementToHtml('tmp_radio', 'Y'), $html_template);
$html_template = str_replace('%radioElementNo%', $renderer->elementToHtml('tmp_radio', 'N'), $html_template);
// The form tags, javascript, and hidden element will be wrapped around
// our html template
echo $renderer->toHtml($html_template);
?>
void HTML_QuickForm_Renderer_QuickHtml::HTML_QuickForm_Renderer_QuickHtml (
void
)
The constructor for HTML_QuickForm_Renderer_QuickHtml().
throws no exceptions thrown
string HTML_QuickForm_Renderer_QuickHtml::toHtml (
string $data
)
Returns the HTML generated for the form.
$data
Any html to put inside the form tags. This would normally be the html template into which you have rendered the form elements.
throws no exceptions thrown
string HTML_QuickForm_Renderer_QuickHtml::elementToHtml (
string $elementName
, string $elementValue
)
Returns the HTML generated for a specific form element and marks that element as rendered.
$elementName
The name of the form element that is being rendered.
$elementValue
The value given to the form element. This is only useful for elements that have the same name (such as a radio button), and can only be told apart based on the value assigned to them.
throws no exceptions thrown
If you want to modify an existing renderer or write a new one, this section will be of interest to you.
Defines the abstract methods that should be implemented by child classes.
QuickForm renderers implement the Visitor design pattern. That means that each form element has an accept() method that is called with a renderer instance as a parameter. This method does the following:
If the element contains other elements (HTML_QuickForm_group and HTML_QuickForm itself) then it iterates over them calling each element's accept() method and calls the methods for rendering the container itself (e.g. startForm() and finishForm()).
If the element is simple, then it calls the renderer's method for rendering itself (e.g. renderHeader()).
It may seem that renderer object has to have a renderConcreteElement() method for each HTML_QuickForm_concreteElement it may visit, but fortunately most of the elements have fairly similar rendering needs, so instead of separate renderTextarea() and renderCheckbox() we have a generic renderElement() method.
The renderer should take care of "accumulating" the information passed to its methods and of generating some type of output based on it. How this is implemented internally depends on the renderer type.
The first thing you have to decide upon is how your renderer will accumulate the information. You'll probably have to create some data structures to keep it (unless you are going to pass the information to e.g. a template object at once).
Then you have to make concrete implementations for all the abstract methods defined in HTML_QuickForm_Renderer. One notable exception is renderHtml() method that is implemented only in the Default renderer and will probably stay this way.
You'll probably also want to add some public methods for customizing the output and getting the results of the renderer's work. But this depends on the type of the renderer very much.
If you want to contribute the renderer you made to QuickForm, consider the following
Your code should be useful to other people (e.g. renderer for some obscure private template engine is a bad contribution).
Your renderer should have non-trivial usage examples.
And last but not least, you code will have to conform to PEAR coding standards.
If you have examples for additional uses of an existing renderer, that will be a welcome contribution, too.
void HTML_QuickForm_Renderer::startForm (
object &$form
)
Called when visiting a form, before processing any form elements
&$form
HTML_QuickForm object being visited
throws no exceptions thrown
abstract
This function can not be called statically.
void HTML_QuickForm_Renderer::finishForm (
object &$form
)
Called when visiting a form, after processing all form elements
&$form
HTML_QuickForm object being visited
throws no exceptions thrown
abstract
This function can not be called statically.
void HTML_QuickForm_Renderer::startGroup (
object &$group
, bool $required
, string $error
)
Called when visiting a group, before processing any group elements
&$group
HTML_QuickForm_group object being visited
$required
Whether a group is required
$error
An error message associated with a group
throws no exceptions thrown
abstract
This function can not be called statically.
void HTML_QuickForm_Renderer::finishGroup (
object &$group
)
Called when visiting a group, after processing all group elements
&$group
HTML_QuickForm_group object being visited
throws no exceptions thrown
abstract
This function can not be called statically.
void HTML_QuickForm_Renderer::renderElement (
object &$element
, bool $required
, string $error
)
Called when visiting an element
&$element
HTML_QuickForm_element object being visited
$required
Whether an element is required
$error
An error message associated with an element
throws no exceptions thrown
abstract
This function can not be called statically.
void HTML_QuickForm_Renderer::renderHeader (
object &$header
)
Called when visiting a header element
&$header
HTML_QuickForm_header element being visited
throws no exceptions thrown
abstract
This function can not be called statically.
void HTML_QuickForm_Renderer::renderHtml (
object &$data
)
Called when visiting a raw HTML/text pseudo-element
Implementation of the methodHTML_QuickForm_html elements are used to directly add HTML to the form output. Thus they are useful with the Default renderer, but not with template-based one. Default renderer is currently the only one actually implementing this method, all others silently ignore HTML_QuickForm_html elements.
&$data
HTML_QuickForm_html element being visited
throws no exceptions thrown
abstract
This function can not be called statically.
This is an add-on to HTML_QuickForm package that allows (among other things) to build multipage forms.
Cool features:
This package implements a PageController design pattern, which essentially means that there is a single page processing requests and actions this page performs depend on parameters passed in GET or POST data. The pattern is described in more detail on Martin Fowler's website and WACT project website.
What does this mean in application to QuickForm: we have a single script which shows and validates different forms depending on data in request. This allows to fairly easy build very complex forms consisting of several pages (think wizards and such).
The most basic implementation of the PageController pattern would look like
<?php
switch ($_REQUEST['action']) {
case 'foo':
doFoo();
break;
case 'bar':
doBar();
break;
default:
echo 'Hello, world!';
}
?>
HTML_QuickForm_Controller is a bit more complex. There are three base classes:
The action sent in request consists of a page name and an action name, it defines which Page will process the request (e.g. display or validate the form) and what exactly this Page should do.
Session initializationThis simple example does not use sessions since there is no need to pass data between pages. You'll need to use sessions when dealing with a real multipage form, though. HTML_QuickForm_Controller does not start a session automatically, you should explicitly call session_start() before instantiating the controller class.
To ease understanding of this package's features, lets take an example form from HTML_QuickForm tutorial and redo it using HTML_QuickForm_Controller:
Basic Controller usage
<?php
// Load the controller
require_once 'HTML/QuickForm/Controller.php';
// Load the base Action class (we will subclass it later)
require_once 'HTML/QuickForm/Action.php';
// Class representing a form
class FirstPage extends HTML_QuickForm_Page
{
function buildForm()
{
$this->_formBuilt = true;
// Add some elements to the form
$this->addElement('header', null, 'QuickForm tutorial example');
$this->addElement('text', 'name', 'Enter your name:', array('size' => 50, 'maxlength' => 255));
// Note how we set the name of the submit button
$this->addElement('submit', $this->getButtonName('submit'), 'Send');
// Define filters and validation rules
$this->applyFilter('name', 'trim');
$this->addRule('name', 'Please enter your name', 'required', null, 'client');
$this->setDefaultAction('submit');
}
}
// Action to process the form
class ActionProcess extends HTML_QuickForm_Action
{
function perform(&$page, $actionName)
{
echo '<h1>Hello, ' . htmlspecialchars($page->exportValue('name')) . '!</h1>';
}
}
$page =& new FirstPage('firstForm');
// We only add the 'process' handler, Controller will care for default ones
$page->addAction('process', new ActionProcess());
// Instantiate the Controller
$controller =& new HTML_QuickForm_Controller('tutorial');
// Set defaults for the form elements
$controller->setDefaults(array(
'name' => 'Joe User'
));
// Add the page to Controller
$controller->addPage($page);
// Process the request
$controller->run();
?>
You may note that the code is more verbose than the original. That is true, but to make a three page
wizard-type form you'll only need to create three subclasses of
HTML_QuickForm_Page
and 'process'
event handler based on HTML_QuickForm_Action
and add them to Controller, while without the Controller infrastructure it will require a non-trivial amount
of programming.
You need to subclass HTML_QuickForm_Page and override its buildForm() method. Its contents are pretty self-explanatory (if you are familiar with QuickForm), except for a few things:
<?php
$this->_formBuilt = true;
?>
Form building is a "heavy" operation, thus we call buildForm() only when
necessary. To prevent calling it twice (and getting two sets of elements instead of one) we set $_formBuilt
property.
The second notable line is
<?php
$this->addElement('submit', $this->getButtonName('submit'), 'Send');
?>
We use getButtonName()
method to set the submit button's name and thus to trigger a 'submit'
action when the button
is clicked.
The third thing is
<?php
$this->setDefaultAction('submit');
?>
The user can submit the form by pressing Enter button, not by clicking on any of the form buttons. Most browsers will not consider any submit button as clicked in this case and will not send its name. setDefaultAction() sets the action (by adding a special hidden element to the form) that will be called in this case.
You'll usually need to create handlers for two actions: 'process'
and 'display'
.
While it is difficult to say anything about the former, as only you know how to process your form, for the
latter you'll need to subclass
HTML_QuickForm_Action_Display
and override its _renderForm() method to call the appropriate
Renderer and do form output customization.
Next we instantiate the page class defined above
<?php
$page =& new FirstPage('firstForm');
?>
and add our custom action handler to it
<?php
$page->addAction('process', new ActionProcess());
?>
the 'process'
action will be called by the default 'submit'
action
handler if the form is valid.
Then we instantiate the controller
<?php
$controller =& new HTML_QuickForm_Controller('tutorial');
?>
Note that the name is a required parameter, and if you will have several Controllers in your application they should have different names, as the names are used to store values in sessions.
Then we set the defaults for the form and add the page to it
<?php
$controller->setDefaults(array(
'name' => 'Joe User'
));
$controller->addPage($page);
?>
It is perfectly legal to do call Page's setDefaults() from within buildForm(), but the former approach allows to set the defaults for the form as a whole, while the latter only for the page in question.
Finally we call the Controller's run() method
<?php
$controller->run();
?>
which will take care of finding the name of the current action and calling the necessary handler. That's all.
...are available in the package archive. Along with the example similar to the provided above, there are two multipage forms:
'Next'
and 'Back'
buttons and you can't go to the next page unless
the current page is valid.
'Submit'
button is pressed.
This document is based on questions asked on PEAR general mailing list. You are encouraged to search the list archives to find more verbose answers and examples.
action
attribute of the <form>
tag?
The only reason constructor of HTML_QuickForm_Page does not provide a means to set the action
attribute is to make it harder to shoot oneself in the foot. That being said, the attribute can still be changed by setAttribute() or updateAttributes() method of HTML_QuickForm_Page (these are inherited from HTML_Common, and you should really consider learning its API).
To avoid confusion lets use the terms "action" for the string containing the name of the action and "action handler" for the subclass of HTML_QuickForm_Action.
While you can invent your own action names and create custom handlers for them, you'll most certainly have to deal with the default actions and their handlers first.
'display'
This action has a default handler, HTML_QuickForm_Action_Display, which displays the form using the Default renderer. You should subclass this handler if you want to customize the form output. This action is called automatically when the page needs to be displayed and should not be bound to buttons via getButtonName().
'submit'
This action should be bound to a "global" submit button for a form. It can be the "submit" button of a single-page or tabbed multi-page form, "finish" button of a wizard. This action has a default handler, HTML_QuickForm_Action_Submit, which checks whether all the pages of the form are valid, then either calls the 'process'
handler or displays the invalid page.
'next'
This action should be bound to the "Next" button of (usually) a modal multipage form (AKA wizard). This action has a default handler, HTML_QuickForm_Action_Next, which checks the validity of the current page and redirects to the next one if the current is valid (or if the form is not modal). On the last page of a modal multipage form this behaves like the default 'submit'
handler.
'back'
This action should be bound to the "Back" button of (usually) a modal multipage form (AKA wizard). This action has a default handler, HTML_QuickForm_Action_Back, which redirects to the previous page if one exists. As of QFC 0.9.3, no form validation takes place on 'back'
action.
'jump'
This action has a default handler, HTML_QuickForm_Action_Jump, which just makes HTTP redirect to the given page of the form. This action should not be bound to buttons via getButtonName().
'process'
This is the action called by default 'submit'
and 'next'
(on the last page of the wizard only) handlers after successful (i.e. without validation errors) form submit. This action doesn't have a default handler, you should define the custom one yourself and implement all the necessary logic to process the form's values in it.
There is also a default handler, HTML_QuickForm_Action_Direct, that does not have a default action name. It is used to go to the specific page of the form and should be explicitly added via Page::addAction() or Controller::addAction() with the name of the target page as $actionName
and bound to buttons via getButtonName() using the same name.
You should use Page's handle() method:
<?php
$page->handle('action');
?>
Controller's handle() method will be called automatically if needed.
Create a subclass of HTML_QuickForm_Action, add the necessary logic to its perform() method. Add it to the Page or to the Controller via addAction() with the appropriate name.
If you intend to bind this action to some button via getButtonName(), you should care about storing values in the container():
Build the form.
Get a reference to container.
Fill the container's ['values'][$pageName]
and ['valid'][$pageName]
elements.
As usual, see the default action handlers' source for the examples.
Controller is able to properly handle actions bound to <input type="image" />
controls, too. You don't have to do anything special, just set the control's name via getButtonName().
If you want to bind an action to something like a hyperlink, you must consider the following: the form must be submitted to be able to get its values, thus you need to write some javascript that will submit the form and somehow pass the action name to the controller.
Controller keeps the form data and validation status in session in so-called container, accessible via container() method. To reset the form you need to clear this container, which is achieved by passing TRUE to container().
The answer is quite simple: you need to use the container() method of HTML_QuickForm_Controller, like this:
<?php
// Note the reference
$data =& $page->controller->container();
$data['_my_stuff'] = $stuff;
?>
On the target page you do
<?php
$data =& $page->controller->container();
$stuff = $data['_my_stuff'];
?>
Please use some prefix for your fields or start their names with underscore to prevent possible name conflicts with the future versions of QFC.
Please note that Controller knows nothing about your additional data, so do not expect it to return it via exportValues() method or the like. You'll have to use the container() method and extract the data yourself. However, your data will be deleted when the container is reset.
First of all, please understand that HTML_QuickForm_Page is a subclass of HTML_QuickForm, thus everything that applies to the parent class will apply to the child. You are encouraged to read the section about renderers in HTML_QuickForm and the docs to the renderer you are trying to use. The usage examples are available in docs/renderers/
directory of QuickForm distribution.
As was already pointed out, you should subclass the HTML_QuickForm_Action_Display class if you want to customize the form output and add an instance of this subclass as a handler for Page's 'display'
action. Its _renderForm() will contain all renderer-specific code.
The only new problem Controller introduces is the buttons bound to specific actions via getButtonName(). If you are using some kind of Dynamic renderer this will not be a problem, as you need not care about the elements names, but you need these names to output the form via Static renderer.
We'll assume using ITStatic renderer but the same can be applied to other Static renderers as well. You basically have 3 options:
Manually add the buttons/placeholders with the names that should be autogenerated via getButtonName() to the form. This is not recommended, as the names should not be of interest to anyone except Controller itself and should not be used directly.
Put the auto-named buttons into group. You can name the group any way you like, just be sure to set $appendName
to false. When ITStatic sees {form_element_html}
placeholder, it'll assign group's HTML to it.
Call getButtonName() from within template. Add the following to the template
and the following to the _renderForm() method of your custom HTML_QuickForm_Action_Display subclass
<?php
$tpl->setCallbackFunction('buttonName', array(&$page, 'getButtonName'));
?>
This code will work with HTML_Template_Sigma, HTML_Template_ITX (note the X) will require some more tweaking.
Thanks to Donald Lobo for this contribution.
Lets assume we have a three-page wizard:
You need to create several action handlers based on QFC's default HTML_QuickForm_Action_Next and HTML_QuickForm_Action_Back. You can do this either at the Page level or at the Controller level. The Page level subclassing is simple, but if you have a complex StateMachine you will end up having multiple subclasses.
For the above example, the 'next'
action on page 1 sends the 'user' to either page 2 or page 3 based on input. The handler also sets the 'valid'
flag of the page that will not be visited.
The 'back'
action on pages 2A and 2B sends the user back to page 1, while the 'next'
action sends him to page 3.
Finally, the 'back'
action handler on page 3 should examine the input on page 1 and send the user to either 2A or 2B.
The working example is available in statemachine.php
file.
'Back'
button?
Client-side validation is called by form's onSubmit
handler, if you remove
the handler, the validation will not run. Therefore you need to remove it if user clicks on
'Back'
button. This can be achieved f.e. by adding the following as
the button's onClick
handler:
this.form.onsubmit = null; return true;
This class keeps track of pages and (default) action handlers for the form, it manages keeping the form values in session, setting defaults and constants for the form as a whole and getting its submit values.
Generally you don't need to subclass this.
HTML_QuickForm_Controller
void constructor HTML_QuickForm_Controller::HTML_QuickForm_Controller (
string $name
, bool $modal
= true
)
Sets the form name and modal/non-modal behaviour. Different multipage forms should have different names, as they are used to store form values in session. Modal forms allow passing to the next page only when all of the previous pages are valid.
$name
form name
$modal
whether the form is modal
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Controller::addAction (
string $actionName
, object HTML_QuickForm_Action &$action
)
This handler will be used if the Page that has to handle some action does not have a handler itself.
$actionName
name of the action
&$action
the handler for the action
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Controller::addPage (
object HTML_QuickForm_Page &$page
)
This package is not documented yet.
&$page
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Controller::applyDefaults (
string $pageName
)
This is used to load Page's default and constant values from the container.
$pageName
Name of a page
throws no exceptions thrown
This function can not be called statically.
array &HTML_QuickForm_Controller::container (
bool $reset
= false
)
Session initializationThe package does store the data in session, but it does not start the session automatically. You must call session_start() function yourself before instantiating HTML_QuickForm_Controller.
Returns a reference to a session variable containing the form-page values and pages' validation status. This is a "low-level" method, use exportValues() if you want just to get the form's values.
The structure of the container is the following
$reset
If true, then reset the container: clear all default, constant and submitted values
throws no exceptions thrown
This function can not be called statically.
mixed HTML_QuickForm_Controller::exportValue (
string $pageName
, string $elementName
)
This package is not documented yet.
$pageName
name of the page
$elementName
name of the element in the page
returns value for the element
throws no exceptions thrown
This function can not be called statically.
array HTML_QuickForm_Controller::exportValues (
string $pageName
= null
)
This package is not documented yet.
$pageName
name of the page, if not set then returns values for all pages
throws no exceptions thrown
This function can not be called statically.
string HTML_QuickForm_Controller::findInvalid (
)
This package is not documented yet.
returns Name of an invalid page
throws no exceptions thrown
This function can not be called statically.
array HTML_QuickForm_Controller::getActionName (
)
This method searches the $_REQUEST
array for an element with a
special name
and splits the name into page name and action. If such an element is not found, the first page will
be default and 'display'
the default action.
returns first element is page name, second is action name
throws no exceptions thrown
This function can not be called statically.
string HTML_QuickForm_Controller::getNextName (
string $pageName
)
This package is not documented yet.
$pageName
throws no exceptions thrown
This function can not be called statically.
object HTML_QuickForm_Page &HTML_QuickForm_Controller::getPage (
string $pageName
)
This package is not documented yet.
$pageName
Name of a page
returns A reference to the page
throws PEAR_Error
This function can not be called statically.
string HTML_QuickForm_Controller::getPrevName (
string $pageName
)
This package is not documented yet.
$pageName
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Controller::handle (
object HTML_QuickForm_Page &$page
, string $actionName
)
This will be called if the page itself does not have a handler to a specific action. The method also loads and uses default handlers for common actions, if specific ones were not added.
&$page
The page that failed to handle the action
$actionName
Name of the action
throws no exceptions thrown
This function can not be called statically.
bool HTML_QuickForm_Controller::isModal (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
bool HTML_QuickForm_Controller::isValid (
string $pageName
= null
)
This package is not documented yet.
$pageName
If set, check only the pages before (not including) that page
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Controller::run (
)
This finds the current page, the current action and passes the action to the page's handle() method.
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Controller::setConstants (
array $constantValues
= null
)
These values won't get overridden by POST or GET vars
$constantValues
constant values
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Controller::setDefaults (
array $defaultValues
= null
)
This package is not documented yet.
$defaultValues
default values
throws no exceptions thrown
This function can not be called statically.
Generally you'll need to subclass this and define your buildForm() method that will build the form. While it is also possible to instantiate this class and build the form manually, this is not the recommended way.
HTML_QuickForm
HTML_QuickForm_Page
void constructor HTML_QuickForm_Page::HTML_QuickForm_Page (
string $formName
, string $method = 'post'
, string $target = '_self'
, mixed $attributes
= null
)
Note that unlike constructor of HTML_QuickForm there is no
$action
, as the form is always submitted to the current page. Note also
that $formName
is not optional.
$formName
Form's name
$method
Form's method
$target
Form's target
$attributes
Extra attributes for <form> tag
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Page::addAction (
string $actionName
, object HTML_QuickForm_Action &$action
)
This package is not documented yet.
$actionName
name of the action
&$action
the handler for the action
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Page::buildForm (
)
Builds a form. You should override this method when you subclass HTML_QuickForm_Page,
it should contain all the necessary
addElement(),
applyFilter(),
addRule() and possibly
setDefaults() and
setConstants() calls.
The method will be called on demand, so please be sure to set $_formBuilt
property to TRUE
to assure that the method works only once.
throws no exceptions thrown
This function can not be called statically.
string HTML_QuickForm_Page::getButtonName (
string $actionName
)
Returns a name for a submit button (or an <input type="image"> control) that will invoke a specific action. This means the following: if you do
<?php
$this->addElement('submit', $this->getButtonName('foo'), 'Foo');
?>
inside the buildForm()
method and the user later uses this button to submit the form, then the 'foo'
handler (added
via addAction()
will be called.
$actionName
Name of the action
returns "name" attribute for a submit button
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Page::handle (
string $actionName
)
This method simply calls the perform() method of an Action object registered for this action name. If an Action object for it was not registered here, controller's handle() method will be called.
$actionName
Name of the action
throws no exceptions thrown
This function can not be called statically.
bool HTML_QuickForm_Page::isFormBuilt (
)
This is used to check whether it is necessary to call buildForm() or not.
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Page::loadValues (
array $values
)
The method is NOT intended for general usage. It should be used to assign form values from the container instead of from actual request data.
$values
'submit' values
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Page::setDefaultAction (
string $actionName
)
This is necessary as the user may just press Enter instead of clicking one of the named submit buttons and then no action name will be passed to the script.
This method adds a special hidden element to the form the contents of which will be used by Controller if no action name is found in the submitted values.
$actionName
default action name
throws no exceptions thrown
This function can not be called statically.
The Controller will select the appropriate Action to call on the request and call its perform() method. The subclasses of this class should implement all the necessary business logic.
The default action handlers are described in the FAQ section.
HTML_QuickForm_Action
Class | Summary |
---|---|
HTML_QuickForm_Action_Back | The action for a 'back' button of wizard-type multipage form. |
HTML_QuickForm_Action_Direct | This action allows to go to a specific page of a multipage form. |
HTML_QuickForm_Action_Display | This action handles the output of the form. |
HTML_QuickForm_Action_Jump | The action handles the HTTP redirect to a specific page. |
HTML_QuickForm_Action_Next | The action for a 'next' button of wizard-type multipage form. |
HTML_QuickForm_Action_Submit | The action for a 'submit' button. |
void HTML_QuickForm_Action::perform (
object HTML_QuickForm_Page &$page
, string $actionName
)
This method should be overriden by child classes to provide the necessary logic.
&$page
the current form-page
$actionName
Current action name, as one Action object can serve multiple actions
throws no exceptions thrown
This function can not be called statically.
This package is not documented yet.
Please note that the name for this action in addAction()
should NOT be 'direct'
, but the name of the page
you wish to go to.
If you want to customize the form display, subclass this class and override the _renderForm() method, you don't need to change the perform() method itself.
This package is not documented yet.
This package is not documented yet.
This package is not documented yet.
Replacement for the default renderer of HTML_QuickForm that uses only XHTML and CSS but no table tags, and generates fully valid XHTML output.
HTML_QuickForm_Renderer_Tableless is a replacement of the default renderer of HTML_QuickForm. It has two main goals:
Accessibility: Because of the abandonment of table tags
and the addition of for
attributes to the
label
tags and id
attributes to the
form elements, the generated output provides good accessibility. Challenged
person might use screen readers, and unhindered persons benefit from the
possibility to click on the labels to set the focus to the belonging form
element.
Validity: The generated output of the renderer is fully XHTML 1.1 valid.
To use this renderer, you just need to copy (and modify if you want) the stylesheet and do something like this:
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/Renderer/Tableless.php';
$form =& new HTML_QuickForm();
$renderer =& new HTML_QuickForm_Renderer_Tableless();
// usual code, e.g. new form fields, rules, ...
$form->accept($renderer);
echo $renderer->toHtml();
?>
For full XHTML validity, you need to add the following line to your code:
<?php
$form->removeAttribute('name');
?>
This document is based on questions asked on PEAR general mailing list and other mailing lists and forums.
div
tag in the element template is broken, a
closing quote mark is missing! Can you please fix this?
No, it is not missing and there is no need to fix the element template.
HTML_QuickForm_Renderer_Tableless requires that HTML_QuickForm >= 3.2.6 is installed. Previous versions don't work correctly for element templates that have two error blocks.
Please use the PEAR installer to avoid such problems in the future. The installer takes care of the dependencies between packages.
div
with
style display: none
?
This might sound funny because a hidden element is obviously already hidden, but the simple reason for this is the XHTML validity.
XHTML allows input
elements only within block elements.
And as form
is not a block element, but
div
is a block element, this trick is used to make the
output XHTML valid.
Release 0.4.3 contains a fix for this problem. The solution is to remove the "height: 1px;" style from "form fieldset li" block in the stylesheet.
Warning: This breaks layout compatibility with Firefox 1.x browsers (Firefox 2.0 still works as expected). The next question contains a solution for Firefox 1.x compatibility.
You need to add a "float: left;" style to the "form fieldset li" block in the stylesheet. In addition, you need to add the following two blocks to your stylesheet:
* html form fieldset li { float: none; } *+html form fieldset li { float: none; }
If you don't need compatibility with Windows Internet Explorer 7, you don't need the mentioned CSS hacks, but can just (re-)add the "height: 1px;" style to the "form fieldset li" block in the stylesheet.
This is explained here: Howto: Adding explainations next to form fields with Tableless QuickForm renderer.
Such problems are most likely caused by the style of the fieldsets. You can try two possible solutions.
At first, you can try to add
overflow: hidden;
to the "form fieldset"
block.
If this does not solve the problems, then try to remove the following two
styles from the "form fieldset"
block.
clear: both; float: left;
You have to tell HTML_QuickForm_Controller about the usage of another renderer. A short tutorial is available: Using the tableless renderer together with HTML_QuickForm_Controller
void HTML_QuickForm_Renderer_Tableless::addStopFieldsetElements (
mixed $element
, string $class = ''
)
Adds one or more element names that indicate the end of a fieldset (a new one will be opened when the next header element occurs).
If a class name is given, it will be added to each (hidden) fieldset that is generated before each of the specified element. For example, if you specify an array with five element names, all five fieldsets with get this additional class name.
$element
Element name(s) (as array or string)
$class
(optional) Class name for the fieldset(s)
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_tableless::clearAllTemplates (
)
Useful when you want to use addData() to create a completely custom form look
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Tableless::setElementTemplate (
string $html
, string $element
= null
)
Sets the template for elements.
$html
The HTML surrounding an element
$element
(optional) Name(s) of the element to apply template for (either single element name as string or multiple element names as an array)
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Tableless::setCloseFieldsetTemplate (
string $html
)
Sets the template used when closing a fieldset.
$html
The HTML used when closing a fieldset
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Tableless::setFormTemplate (
string $html
)
Sets the form template.
$html
The HTML surrounding the form tags
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_tableless::setGroupElementTemplate (
string $html
, string $group
)
Sets element template for elements within a group.
$html
The HTML surrounding an element
$group
Name of the group to apply template for
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Tableless::setGroupTemplate (
string $html
, string $group
)
This template is contained within a group-as-element template set via setTemplate() and contains group's element templates, set via setGroupElementTemplate()
$html
The HTML surrounding group elements
$group
Name of the group to apply template for
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Tableless::setHeaderTemplate (
string $html
)
Sets the header template.
$html
The HTML surrounding the header
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Tableless::setOpenFieldsetTemplate (
string $html
)
Sets the template used when opening a fieldset.
$html
The HTML used when opening a fieldset
throws no exceptions thrown
This function can not be called statically.
void HTML_QuickForm_Renderer_Tableless::setRequiredNoteTemplate (
string $html
)
Sets the note indicating required fields template.
$html
The HTML surrounding the required note
throws no exceptions thrown
This function can not be called statically.
string HTML_QuickForm_Renderer_Tableless::toHtml (
)
Returns the HTML generated for the form.
throws no exceptions thrown
This function can not be called statically.
HTML_QuickForm_DHTMLRulesTableless is a DHTML replacement for the standard JavaScript alert window for client-side validation of forms built with HTML_QuickForm when using the HTML_QuickForm_Renderer_Tableless renderer.
In addition to the standard behaviour of HTML_QuickForm (i.e. showing the errors from client-side validation on the "onSubmit" event), this package can also show the errors on the "onBlur" and "onChange" events (see below for information on how to enable this).
To use this package you just need to do something like this:
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/DHTMLRulesTableless.php';
require_once 'HTML/QuickForm/Renderer/Tableless.php';
$form = new HTML_QuickForm_DHTMLRulesTableless(...);
$renderer = new HTML_QuickForm_Renderer_Tableless();
// usual code, e.g. new form fields, rules, ...
$form->accept($renderer);
echo $renderer->toHtml();
?>
As already said for the tableless renderer, you need to add the following line to your code to get full XHTML validity:
<?php
$form->removeAttribute('name');
?>
$form
can be used the same way as with the standard HTML_QuickForm package, there is no difference.
To enable validation on "onBlur" and "onChange" events,
you need to add the following line before the
$form->accept($renderer);
call:
<?php
$form->getValidationScript();
?>
Element for HTML_QuickForm that emulate a multi-select.
I am sure we have all been there, dealing with HTML_QuickForm because we need to allow users to select multiple options. With a standard html select box, you can select multiple options if the multiple attribute is set, but you have to hold down the appropriate modifier key (ctrl on windows) in order to do so. It is not a problem for end-user experts, but it is not true for all of us.
A quick list of great features
Mandatory resources :
PHP 4.3.10 or newer.
PEAR 1.5.4 or newer.
PEAR::HTML_Common 1.2.4 or newer.
PEAR::HTML_Quickform 3.2.10 or newer.
pcre extension.
Optional resources :
You can download and use it for free. But don't delete the copyright notice. You can read terms of the license.
YES if there is no answer in this Guide and if you are ready to share some informations such as : your configuration (platform Win *nix mac, PHP version, PEAR packages installed) and perharps your script.
You can report it with the bug tracker at PEAR.
HTML_QuickForm is a PEAR package that provides methods for creating, validating and processing HTML forms.
The purpose of Keith Edmunds tutorial is to give the new users of QuickForm an overview of its features and usage patterns. It describes a small subset of available functionality.
Don't forget to read also the PEAR Manual, HTML_QuickForm related part.
PEAR (an acronym for PHP Extension and Application Repository) is a framework and distribution system for reusable PHP components.
Don't forget to read also the PEAR Manual and PEAR FAQ.
The dual multi-select won't work, but you can display a single multi select box witch checkboxes. To do so, you have to remove {unselected} placeholder in the advmultiselect template element.
You must use the HTML_QuickForm addGroupRule() method rather than HTML_QuickForm addRule() method.
Following answers can be applied only for HTML_QuickForm_advmultiselect version 1.3.0 or better.
Use only once reference of javascript source code. If you have more than one advmultiselect element on your html page, then keep only one call and remove others.
<script type="text/javascript">
<?php
echo $ams1->getElementJs(); // keep one
//echo $ams2->getElementJs(); // remove others
?>
</script>
Better solution is to use link to external resource and then reduce amount of javascript source code embedded. Add line of code below between <head> tags of your generated page.
<script type="text/javascript" src="qfamsHandler.js"></script>
Fix path to javascript resource if necessary.
qfamsHandler.js
file can be found in PEAR/data/HTML_QuickForm_advmultiselect directory.
At least remove {javascript}
placeholder if you use default
template. You have to set this new one $deftpl
,
with code something like :
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('ams130');
// ....
$ams =& $form->addElement('advmultiselect', 'cars', null, $car_array);
$deftpl = '
<table{class}>
<!-- BEGIN label_2 --><tr><th>{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th> </th><th>{label_3}</th></tr><!-- END label_3 -->
<tr>
<td valign="top">{unselected}</td>
<td align="center">{add}{remove}</td>
<td valign="top">{selected}</td>
</tr>
</table>
';
$ams->setElementTemplate($deftpl);
//...
?>
You should have forgotten to add package ressource itself. This operation is mandatory for all external Quickform elements and not necessary for internal elements such as "radio", "checkbox", "text", "button" ...
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php'; // <-- DO NOT forget it
?>
With HTML_QuickForm_advmultiselect version 1.3.0 or better you need to add a chunk of javascript code to initialize onclick event handler of each checkboxes.
<script type="text/javascript" src="qfamsHandler.js"></script> <script type="text/javascript"> window.qfamsName = new Array(); window.qfamsName[0] = 'cars'; window.qfamsName[1] = 'fruit'; window.addEventListener('load', qfamsInit, false); </script>
window.qfamsName
is an array what identify each advmultiselect element used
on your html page.
This problem comes when you set the selected list (see HTML_QuickForm::setDefaults method) with a wrong data array.
Remember that the available list contains all datas (selected and unselected values). This list is an associative array of "key-code" => "display-value". While selected list is only an array of "key-code".
Suppose we have to retrieve information from a database (with PEAR::DB), and have a simple table for holding user info. This SQL statement creates a table usable under the default database scheme using MySQL:
CREATE TABLE user ( userid VARCHAR(5) NOT NULL, gid INT NOT NULL, affect INT NOT NULL, lastname VARCHAR(50)NOT NULL, firstname VARCHAR(50) NOT NULL, PRIMARY KEY (userid) );
with values :
INSERT INTO user VALUES ('MJ001', 1, 0, 'Martin', 'Jansen'); INSERT INTO user VALUES ('BG001', 1, 1, 'Greg', 'Beaver'); INSERT INTO user VALUES ('CD001', 1, 0, 'Daniel', 'Convissor'); INSERT INTO user VALUES ('LL001', 2, 1, 'Laurent', 'Laville');
Column gid identify a user group, while userid identify a single and unique user.
We will initialize AVAILABLE list (on left side if default template) by a db query something like that:
<?php
$queryAll = 'SELECT userid, CONCAT(lastname, " ", firstname) AS useridentity '
. 'FROM user WHERE gid = 1';
?>
and get this array:
Array ( [MJ001] => Jansen Martin [BG001] => Beaver Greg [CD001] => Convissor Daniel )
We will initialize SELECTED list (on right side if default template) by a db query something like that:
<?php
// Once you have a valid DataBase object named $db ...
$querySel = 'SELECT userid FROM user WHERE gid = 1 AND affect = 1';
$affected_user =& $db->getCol($querySel);
?>
and get this array:
Array ( [0] => BG001 )
Remember that only a key-code array is necessary, other data will make blank line into select box.
Remains stuff is basic, create the QFAMS element, and load options (available and selected)
with the load()
method.
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsDB');
// Once you have a valid QuickForm advmultiselect object named $ams in a QuickForm named $form ...
$ams =& $form->addElement('advmultiselect', 'user',
array('Users:', 'Available', 'Affected'), // labels
null, // datas: "key-code" => "display-value"
array('style' => 'width:200px;') // custom layout
);
$ams->load($db, $queryAll, 'useridentity', 'userid', $affected_user);
// ...
?>
You may use the
load()
method to set options from :<?php
// ...
// 1. a php array
$all_user = array('MJ001' => 'Martin Jansen',
'BG001' => 'Greg Beaver',
'CD001' => 'Daniel Convissor'
);
$affected_user = array('BG001');
$ams->load($all_user, $affected_user);
// queries to get data
$queryAll = 'SELECT userid, CONCAT(lastname, " ", firstname) AS useridentity '
. 'FROM user WHERE gid = 1';
$querySel = 'SELECT userid FROM user WHERE gid = 1 AND affect = 1';
$affected_user =& $db->getCol($querySel);
// 2. a db result
$all_user =& $db->query($queryAll);
$ams->load($all_user, 'useridentity', 'userid', $affected_user);
// 3. a db query
$ams->load($db, $queryAll, 'useridentity', 'userid', $affected_user);
// ...
?>
This example is available in bundle (see examples/qfams_basic_2.php)
add auto arrange feature (developer issue) asked by Jamie Alessio.
ability to manage (sort) list with two buttons (Up and Down)
Add new example Basic 2 to demonstrate the db options loading feature with PEAR::DB and a MySQL database.
Include the first html version of a complete english guide for both beginners and expert users.
Ability to select/unselect all items (options) in one stroke. You may also toggle a selection.
Add toggle button ability to a dual multi-select boxes.
Ability to toggle selection was only available for single select box shape since version 1.1.0
New placeholders to display live counters (unselected, selected items).
remove dreprecated function (setJsElement) since version 1.3.0
rewrites JS in object literal notation (uses namespace)
add two new buttons to move up to top or down to bottom a selected item
add a minimized/compressed version of Javascript code
may now load options (class constructor, or load functions) with fancy attributes without additional code
options with fancy attributes are kept even if you move them (left right, up down)
setElementTemplate() function signature changed (but keep BC) : allow to use only one instance of javascript code
add or remove persistant options (disabled attribute) at run-time, with HTML_QuickForm_advmultiselect::setPersistantOptions() .
PEAR_Error instance throws have now a level (exception or error) and a code identified by HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT constant
The purpose of this tutorial is to give the new users of HTML_QuickForm_advmultiselect an overview of its features and usage patterns. It describes a small subset of available functionality, but points to the parts of the documentation that give a more in-depth overview.
In a usability test with non-information workers in an intranet application, we could be surprised that none (or few) of the users successfully selected multiple responses when presented in a HTML select element with multiple selection enabled. They simply did not know that control click exist.
To solve this problem, the QuickForm advmultiselect element provides an interface that allow user-end to do their selection very easily. In this interface, there are:
For each new item to add to your selection, select it from the unselected list (source), then clic on the add button to move it to the selection list (destination). It does not exists in the source list after transfer.
For each item to remove from your selection, select it in the selected list (source), then clic on the remove button to move it to the unselected list (destination). It does not exists in the source list after transfer.
We will start by creating a very simple form. Our goals are :
Copy the following code to a file, give it a .php extension, and display it in your browser:
<?php
/**
* Custom advMultiSelect HTML_QuickForm element
* using stylesheet rules selectors and a template.
*
* The template allows to add label as headers of dual select box
* and moves the button to another location (below each select box).
*
* @version $Id: firstform.xml,v 1.2 2009-02-09 17:10:32 farell Exp $
* @author Laurent Laville <pear@laurent-laville.org>
* @package HTML_QuickForm_advmultiselect
* @subpackage Examples
* @access public
* @example examples/qfams_custom_1.php
* qfams_custom_1 source code
* @link http://www.laurent-laville.org/img/qfams/screenshot/custom1.png
* screenshot (Image PNG, 677x197 pixels) 4.80 Kb
*/
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsCustom1');
$form->removeAttribute('name'); // XHTML compliance
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
// rendering with QF renderer engine and template system
$form->addElement('header', null, 'Advanced Multiple Select: custom layout ');
$ams =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array,
array('size' => 5,
'class' => 'pool', 'style' => 'width:300px;'
)
);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams->setButtonAttributes('add', array('value' => 'Add >>',
'class' => 'inputCommand'
));
$ams->setButtonAttributes('remove', array('value' => '<< Remove',
'class' => 'inputCommand'
));
$template = '
<table{class}>
<!-- BEGIN label_2 --><tr><th align="center">{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th align="center">{label_3}</th></tr><!-- END label_3 -->
<tr>
<td>{unselected}</td>
<td>{selected}</td>
</tr>
<tr>
<td align="center">{add}</td>
<td align="center">{remove}</td>
</tr>
</table>';
$ams->setElementTemplate($template);
if (isset($_POST['fruit'])) {
$form->setDefaults(array('fruit' => $_POST['fruit']));
}
$form->addElement('submit', 'send', 'Send');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect custom example 1</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
table.pool {
border: 0;
background-color: lightyellow;
}
table.pool th {
font-size: 80%;
font-style: italic;
text-align: center;
}
table.pool select {
background-color: lightblue;
}
.inputCommand {
background-color: #d0d0d0;
border: 1px solid #7B7B88;
width: 7em;
margin-bottom: 2px;
}
-->
</style>
<?php echo $ams->getElementJs(false); ?>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
</body>
</html>
Lets review this example step by step :
At the beginning we creates a HTML_Quickform object that will contain the objects representing elements and all the other necessary information. We only pass the form's name to the constructor, which means that default values will be used for other parameters.
The first one is not the "real" element, it is just a heading
to improve presentation.
The second one is our advmultiselect element. Note that parameters for
HTML_Quickform::addElement
method have different meanings
for different elements. That is so because they are actually passed to
these elements' constructors.
The $fruit_array
variable sets the default values (code, label)
for the fruit advmultiselect element.
It's time to define the fruit element attributes:
To put headers on each list (wherever you want: at top, or bottom), you need first to set these values. Then second, defines the placeholder in the template (as any other multi-label element).
Placeholders {label_2}, {label_3} are used, in the same way, for all HTML_Quickform renderers , and defines: unselected list (
label_2
), and selected list (label_3
).
Last step to complete definition of a advmultiselect element is to set the add and remove buttons.
Here we gave names Add >>
and << Remove
,
with a skin handled by the inputCommand
CSS class.
Placeholders {add}, {remove} must exists into the template. Without them you won't see the move buttons.
User's input overrides default values of the fruit advmultiselect element.
Before to validate and process the form, the building form step need one more thing. Don't forget, that to manage swaps between both list, we need some javascript code. It's now time to include into our HTML stream/template.
By given the false value as argument to the
getElementJs
method, we have choosen to build javascript code with its script tags. Default behavior is to get only raw code without surrounding script tags. May be usefull with template integration and existing js code.
This is a simple display example. In your scripts you'll usually want to store the values somewhere or do whatever else. The HTML_Quickform::process method may be of interest here.
You should now have an understanding of basic HTML_QuickForm_advmultiselect functionality, but there are many more features in the package, each of them deserving a separate tutorial. This section will give a short overview of them and point you to the complete documentation.
The advmultiselect element offers a lot of possibilities to customize its layout and appearance. Remember that form output is done via renderers - special classes containing necessary logic. There are QuickForm renderers that directly output HTML and those that use template engines for this.
Headers allow to give caption (label) to one or both list as any other QuickForm element that support multi-labels system.
Buttons allow to swap items from one list to the other, or arrange order of your selection.
Sorts allow to re-arrange order of the selection, by the end-user. Its also allow to set (by programming) a pre-set order (alphabetic or reverse) to each list.
A label is a description text that will be displayed near the element. Some renderers can handle multiple labels for the element. Placeholders used by these renderers are different in naming convention. Nevermind, HTML_QuickForm_advmultiselect used only one standard coding that is equivalent to the QuickForm default renderer.
As {label_2} is the placeholder for the second label (unselected list), {label_3} is the placeholder for the third label (selected list), in the hash setting. See HTML_Quickform::setLabel method.
The first label is always for the advmultiselect element group itself. Its name depend of the QuickForm renderer used. See examples that follow to notice the difference.
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$template =
'<tr>
<td align="right" valign="top">
<b>{label}</b>
</td>
<td valign="top" align="left">
{element}
<!-- BEGIN error --><br/><font color="red">{error}</font><br/><!-- END error -->
</td>
</tr>';
// Create the form, and add a header to it.
$form = new HTML_QuickForm('qfamsLabels');
$form->addElement('header', null, 'QuickForm Labels Example: default renderer');
$cars = array(
'dodge' => 'Dodge',
'chevy' => 'Chevy',
'bmw' => 'BMW',
'audi' => 'Audi',
'porsche' => 'Porsche',
'kia' => 'Kia',
'subaru' => 'Subaru',
'mazda' => 'Mazda',
'isuzu' => 'Isuzu',
);
$labels = array('Cars:', 'Models', 'Your selection');
// Do the magic! Just pass your label to the element as an array!
$form->addElement('advmultiselect', 'cars', $labels, $cars);
// customize the element template
$renderer =& $form->defaultRenderer();
$renderer->setElementTemplate($template);
// output the form
$form->display();
?>
The first label with default renderer, is always named {label}.
In this example, its value will be Cars:
<?php
require_once 'HTML/Template/Sigma.php';
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/Renderer/ITDynamic.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$template = '
<form {qf_attributes}>
<table class="maintable">
<!-- BEGIN qf_hidden_block -->
<div style="display: none;">
<!-- BEGIN qf_hidden_loop -->{qf_hidden}<!-- END qf_hidden_loop -->
</div>
<!-- END qf_hidden_block -->
<!-- BEGIN qf_main_loop -->
<!-- BEGIN qf_header -->
<tr><th class="maintable" colspan="2">{qf_header}</th></tr>
<!-- END qf_header -->
<!-- BEGIN qf_element -->
<tr>
<td align="right" width="30%"><span class="qfLabel">{qf_label}&nbsp;</span></td>
<td>{qf_element}
<!-- BEGIN qf_element_error --><br /><span style="color: #FF0000;">{qf_error}</span><!-- END qf_element_error -->
</td>
</tr>
<!-- END qf_element -->
<!-- END qf_main_loop -->
</table>
</form>
';
// Create the form, and add a header to it.
$form = new HTML_QuickForm('qfamsLabels');
$form->addElement('header', null, 'QuickForm Labels Example: itdynamic renderer');
$cars = array(
'dodge' => 'Dodge',
'chevy' => 'Chevy',
'bmw' => 'BMW',
'audi' => 'Audi',
'porsche' => 'Porsche',
'kia' => 'Kia',
'subaru' => 'Subaru',
'mazda' => 'Mazda',
'isuzu' => 'Isuzu',
);
$labels = array('Cars:', 'Models', 'Your selection');
// Do the magic! Just pass your label to the element as an array!
$form->addElement('advmultiselect', 'cars', $labels, $cars);
// set the form template
$tpl = new HTML_Template_Sigma('.');
$tpl->setTemplate($template);
$renderer = new HTML_QuickForm_Renderer_ITDynamic($tpl);
$form->accept($renderer);
// output the form
$tpl->show();
?>
The first label with ITDynamic renderer, is always named {qf_label}.
In this example, its value is still Cars:
Lets review in details how to set the appearance of headers in one of examples included in the package.
After the labels are set:
<?php
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
?>
Have a special look on the advmultiselect template element. Here is it:
<?php
$template = '
<table{class}>
<!-- BEGIN label_2 --><tr><th align="center">{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th align="center">{label_3}</th></tr><!-- END label_3 -->
<tr>
<td>{unselected}</td>
<td>{selected}</td>
</tr>
<tr>
<td align="center">{add}</td>
<td align="center">{remove}</td>
</tr>
</table>';
?>
Header for the unselected list (named Available
) is
center aligned in bold with the TH tag.
Header for the selected list (named Selected
), is also
center aligned in bold with another TH tag.
To give only a header to the selection list, either you set values as:
<?php
$ams->setLabel(array('Fruit:', null, 'Your selection'));
?>or you remove line beginning by
<!-- BEGIN label_2 -->
into the advmultiselect template element.
There is two categories of button: sort and swap.
The swap buttons are included since origin, while the sort buttons were included in the 0.5.0 version.
With swap buttons, you will do your selection easily. Select item(s) (one or more), from unselected or selected list, then clic on the add or remove button to move your selection from one list to the other.
You can also double-clic on one item to move it, immediately, to the other list without to clic on the add or remove button.
Since version 1.1.0 you have ability to select or unselect all items in one stroke. You may also toggle a previous selection. This new feature is available through buttons all, none and toggle.
Since version 1.2.0 toggle selection feature is available for both single and dual multi-select boxes.
with one multi-select list, buttons available are only: all, none and toggle.
with two multi-select list, buttons available are only: add, remove, moveup, movedown, all, none and toggle.
Since version 1.5.0, you may add to buttons list : movetop and movebottom.
A full example is given in appendices. See the advanced selection
With sort buttons, you have ability to reorder your selection easily. Select one item then clic on the up or down button to move it to the top or the bottom of your selection.
Order is keep in the $_POST super global array, when the form is submit; Even if you've activated the auto-arrange feature (see
$sort
parameter of the HTML_QuickForm_advmultiselect class constructor )
There is two kinds of button: text and image. These are the standard html buttons we can find in a form. Default appearance is text but you can also easily use image layout. Here is how to do :
<?php
$ams->setButtonAttributes('add', array('type' => 'image', 'src' => '/img/add.png'));
$ams->setButtonAttributes('remove', array('type' => 'image', 'src' => '/img/remove.png'));
?>
Of course, all images are supported: PNG, GIF ...
If the image file does not exist, you will get the internal text value as output without nothing else.
Remember that defaults are: "
>>
" for add button, and "<<
" for remove button.
In version 0.4.0, there were only the basic add and remove buttons. In version 0.5.0, two new buttons were added: moveup and movedown. In version 1.1.0, three new buttons were added: all, none, and toggle. In version 1.5.0, two new buttons were added: movetop and movebottom.
Any other button identifier than add, remove, all, none, toggle, moveup, movedown, movetop, movebottom will throws a PEAR_Error.
Only five attributes may be set, with these four button identifiers; they are:
name
The form input button name. Default are: 'add', or 'remove', or 'all, or 'none', or 'toggle', or 'up', or 'down', or 'top', or 'bottom'.
Default values of the name attribute are not equivalent to button identifiers.
value
The form input button text. Default are : ' >> ', or ' << ', or ' Select All ', or ' Select None ', or ' Toggle Selection ', or ' Up ', or ' Down ', or ' Top ', or ' Bottom '.
type
The form input button kind. Default is 'button' (Can be either 'button' or 'image').
class
A CSS class identifier in one of your stylesheets.
src
URL of the image file used.
There are two kind of sort with the advmultiselect element: The first one is only available by programming and allow an auto-arrange (alphabetic or reverse order) of each select list. The second is for end-user and allow them to sort their selection as they want.
These features required HTML_QuickForm_advmultiselect package version 0.5.0 or better.
In some case, it could be interresting to have items lists sort alphabetically when elements are moved between them, rather than gets added to the bottom (default behavior).
To get both list sort alphabetically, you have to set the $sort
parameter of the
HTML_QuickForm_advmultiselect class constructor
with the PHP SORT_ASC constant.
To get both list sort in reverse order, you have to set the $sort
parameter of the
HTML_QuickForm_advmultiselect class constructor
with the PHP SORT_DESC constant.
A full example is given in appendices. See the sort usage
The other way to have your selection sorted is with help of two buttons: moveup to move an item to the top of the list, and movedown to move an item to the bottom of the list.
These buttons works only with the selection list
A full example is given in appendices. See the sort usage
When selection lists have lot of item, we don't expect to count each item one by one.
When it's so easy to count selections on server side, once the form is submitted, there is no ability to do the same in live, until new feature "Live counter".
This feature require HTML_QuickForm_advmultiselect package version 1.3.0 or better.
A full example is given in appendices. See live counter combines with multiple elements
Implements display of live counters (selected, unselected items) is fully independant. Its requires for each counter to add new placeholders.
For item unselected list, these placehodlers are :
{unselected_count} for localisation (where the counter value will be displayed)
{unselected_count_id} for identification by DOM API (ID attribut of any HTML tag)
For item selected list, these placehodlers are :
{selected_count} for localisation (where the counter value will be displayed)
{selected_count_id} for identification by DOM API (ID attribut of any HTML tag)
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
// ...
$form = new HTML_QuickForm('amsLC');
$ams =& $form->addElement('advmultiselect', 'cars', null, $car_array);
// ...
?>Counters identifiers (attribut ID) used by DOM API are identified by string : advmultiselect element name concat with "_selected" or "_unselected" suffix (depending of lists).
Result for example (previous code) :
cars_selected
orcars_unselected
advmultiselect element with single list (with checkboxes) are identified by string : "qfams_" prefix concat with advmultiselect element name
Result for example (previous code) :
qfams_cars
advmultiselect element with double list are identified with string : prefix "__" (two underscores) concat with advmultiselect element name for unselected list. While selection list (selected item) is identified with string : prefix "_" (one underscore) concat with advmultiselect element name.
Result for example (previous code) :
__cars
and_cars
advmultiselect elements with single list (with checkboxes) are identified by DOM API with only
{selected_count}
and{selected_count_id}
placeholders. No need to add{unselected_count}
and{unselected_count_id}
placeholders.See example in appendix Live counter combines with multiple elements, and template definition
$template1
lines 49-57.
This reference describes every method in the HTML_QuickForm_advmultiselect class.
In a two multi-select boxes default shape, there are :
a list with all unselected values (on left side)
a list with all selected values (on right side)
two buttons (add, remove) to swap items from one list to the other (on middle side)
optional headers for each list
In a single multi-select box default shape, there are :
a list with all values (selected: checkbox on, unselected: checkbox off)
none buttons
optional header for the list
Buttons (up, down) to sort item are unavailable in a single multi-select box shape.
In a two multi-select boxes custom shape, you may also have :
two buttons to sort selected item (by user-end)
There are no order between setting button (up, down) attributes and setting placeholders {moveup}, {movedown} into the template.
With the class constructor, you have ability to set the auto-arrange feature. This feature is an option to sort alphabetically (or reverse) when elements are moved between lists. Default behaviour is no sort (add to the bottom).
This feature is only available since version 0.5.0
You may either load options from the class constructor or with the load or loadArray functions.
load and loadArray functions with fancy attributes (disabled, style:color, ...) support are only available since version 1.5.0
void constructor HTML_QuickForm_advmultiselect::HTML_QuickForm_advmultiselect (
string
$elementName
= NULL
,
mixed
$elementLabel
= NULL
,
mixed
$options
= NULL
,
mixed
$attributes
= NULL
,
integer
$sort
= NULL
)
This package is not documented yet.
$elementName
Dual Select name attribute
$elementLabel
Label(s) for the select boxes
$options
Data to be used to populate options
$attributes
Either a typical HTML attribute string or an associative array
$sort
Either SORT_ASC for auto ascending arrange, SORT_DESC for auto descending arrange, or NULL for no sort (append at end: default)
throws no exceptions thrown
since version 0.4.0 (2005-06-25)
This function can not be called statically.
PEAR_Error HTML_QuickForm_advmultiselect::load (
mixed &$options
, mixed $param1
= null
, mixed $param2
= null
, mixed $param3
= null
, mixed $param4
= null
)
This method overloaded parent method of select element, to allow loading options with fancy attributes.
&$options
Options source currently supports assoc array or DB_result
$param1
(optional) See function detail
$param2
(optional) See function detail
$param3
(optional) See function detail
$param4
(optional) See function detail
returns TRUE on success
throws PEAR_Error
since version 1.5.0 (2009-02-15)
This function can not be called statically.
<?php
/**
* Custom advMultiSelect HTML_QuickForm element
* loading values with fancy attributes (disabled, ...)
*
* @version $Id: load.xml,v 1.1 2009-02-01 15:12:40 farell Exp $
* @author Laurent Laville <pear@laurent-laville.org>
* @package HTML_QuickForm_advmultiselect
* @subpackage Examples
* @access public
* @example examples/qfams_custom_3.php
* qfams_custom_3 source code
* @link http://www.laurent-laville.org/img/qfams/screenshot/custom3.png
* screenshot (Image PNG, 374x275 pixels) 4.96 Kb
*/
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsCustom3');
$form->removeAttribute('name'); // XHTML compliance
// same as default element template but wihtout the label (in first td cell)
$withoutLabel = <<<_HTML
<tr valign="top">
<td align="right">
</td>
<td align="left">
<!-- BEGIN error --><span style="color: #ff0000;">{error}</span><br /><!-- END error -->{element}
</td>
</tr>
_HTML;
// more XHTML compliant
// replace default element template with label, because submit button have no label
$renderer =& $form->defaultRenderer();
$renderer->setElementTemplate($withoutLabel, 'send');
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => array('Pear', array('disabled' => 'disabled')),
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
// rendering with QF renderer engine and template system
$form->addElement('header', null, 'Advanced Multiple Select: custom layout ');
$ams =& $form->createElement('advmultiselect', 'fruit', null, null,
array('class' => 'pool')
);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams->setButtonAttributes('add', array('value' => 'Add >>',
'class' => 'inputCommand'
));
$ams->setButtonAttributes('remove', array('value' => '<< Remove',
'class' => 'inputCommand'
));
$template = '
<table{class}>
<!-- BEGIN label_2 --><tr><th align="center">{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th align="center">{label_3}</th></tr><!-- END label_3 -->
<tr>
<td>{unselected}</td>
<td>{selected}</td>
</tr>
<tr>
<td>{add}</td>
<td>{remove}</td>
</tr>
</table>';
$ams->setElementTemplate($template);
$ams->load($fruit_array, 'apple,pear');
$form->addElement($ams);
$form->addElement('submit', 'send', 'Send');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect custom example 3</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
table.pool {
border: 0;
background-color: lightyellow;
}
table.pool td {
padding-left: 1em;
}
table.pool th {
font-size: 80%;
font-style: italic;
text-align: center;
}
table.pool select {
background-color: lightblue;
}
.inputCommand {
background-color: #d0d0d0;
border: 1px solid #7B7B88;
width: 7em;
margin-bottom: 2px;
}
-->
</style>
<?php echo $ams->getElementJs(false); ?>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
</body>
</html>
mixed HTML_QuickForm_advmultiselect::loadArray (
array $arr
, mixed $values
= null
)
This method overloaded parent method of select element, to allow to load array of options with fancy attributes.
$arr
Associative array of options
$values
(optional) Array or comma delimited string of selected values
returns TRUE on success
throws PEAR_Error
since version 1.5.0 (2009-02-15)
This function can not be called statically.
See HTML_QuickForm_advmultiselect::load() example.
array HTML_QuickForm_advmultiselect::getPersistantOptions (
)
Returns list of persistant options (key-values) that could not be selected or unselected.
throws no exceptions thrown
since version 1.5.0 (2009-02-15)
This function can not be called statically.
mixed HTML_QuickForm_advmultiselect::setPersistantOptions (
mixed
$optionValues
,
boolean
$persistant
= true
)
Sets which items should have the disabled attribute to keep it persistant
$optionValues
Options (key-values) that should be persistant
$persistant
(optional) TRUE if persistant, FALSE otherwise
Error message | Reason | Solution |
---|---|---|
Argument 1 of HTML_QuickForm_advmultiselect::setPersistantOptions is not a valid array | Tried to give array or comma delimited string of selected values | Check the $optionValues argument data type and content |
Argument 2 of HTML_QuickForm_advmultiselect::setPersistantOptions is not a boolean | Tried to give a TRUE or FALSE value | Check the $persistant argument data type |
since version 1.5.0 (2009-02-15)
This function can not be called statically.
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('ams');
$form->removeAttribute('name'); // XHTML compliance
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'tangerine' => 'Tangerine'
);
$ams =& $form->createElement('advmultiselect', 'fruit', null, null,
array('class' => 'pool', 'style' => 'width:200px;')
);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams->setButtonAttributes('add' , 'class=inputCommand');
$ams->setButtonAttributes('remove' , 'class=inputCommand');
$ams->setButtonAttributes('all' , 'class=inputCommand');
$ams->setButtonAttributes('none' , 'class=inputCommand');
$ams->setButtonAttributes('toggle' , 'class=inputCommand');
$ams->setButtonAttributes('moveup' , 'class=inputCommand');
$ams->setButtonAttributes('movedown' , 'class=inputCommand');
$ams->setButtonAttributes('movetop' , 'class=inputCommand');
$ams->setButtonAttributes('movebottom', 'class=inputCommand');
$templateDual = '
<table{class}>
<!-- BEGIN label_2 --><tr><th>{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th> </th><th>{label_3}</th></tr><!-- END label_3 -->
<tr>
<td>{unselected}</td>
<td align="center">
{add}<br />{remove}<br /><br />
{all}<br />{none}<br />{toggle}<br /><br />
{moveup}<br />{movedown}<br />{movetop}<br />{movebottom}
</td>
<td>{selected}</td>
</tr>
</table>
';
$ams->load($fruit_array, 'pear');
$ams->setPersistantOptions(array('pear', 'tangerine'));
$ams->setElementTemplate($templateDual);
$form->addElement($ams);
$buttons[] =& $form->createElement('submit', null, 'Submit');
$buttons[] =& $form->createElement('reset', null, 'Reset');
$form->addGroup($buttons, null, ' ');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect persistant options example</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
table.pool {
border: 0;
background-color: #787878;
}
table.pool td {
padding-left: 1em;
}
table.pool th {
font-size: 80%;
font-style: italic;
text-align: center;
}
table.pool select {
color: #242424;
background-color: #eee;
}
.inputCommand {
width: 120px;
}
<?php echo $ams->getElementCss(); ?>
-->
</style>
<?php echo $ams->getElementJs(false, true); ?>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
var_dump($clean);
// if apple fruit is selected, then pear may be remove from next selection
if (in_array('apple', $clean['fruit'])) {
$ams->setPersistantOptions('pear', false);
} else {
$ams->setPersistantOptions('pear', true);
$selection = $ams->getSelected();
if (!in_array('pear', $selection)) {
array_push($selection, 'pear');
$ams->setSelected($selection);
}
}
}
$form->display();
?>
</body>
</html>
Here, you can choose between standard form text or image button. You can also set the CSS class used to change the look and feel of buttons.
void HTML_QuickForm_advmultiselect::setButtonAttributes (
string $button
,
mixed
$attributes
= NULL
)
The basic settings gave standard form input buttons. You may change the look with some CSS
if you set the class attribute in the $attributes
hash (second parameter). See example section.
Only five attributes may be set, they are:
name
The form input button name. Default are: 'add', or 'remove', or 'all', or 'none', or 'toggle', or 'up', or 'down', or 'top', or 'bottom'.
value
The form input button text. Default are ' >> ', or ' << ', or ' Select All ', or ' Select None ', or ' Toggle Selection ', or ' Up ', or ' Down ', or ' Top ', or ' Bottom '.
type
The form input button kind. Default is 'button' (Can be either 'button' or 'image').
class
A CSS class identifier in one of your stylesheets.
src
URL of the image file used.
$button
Button identifier, either 'add', 'remove', 'all', 'none', 'toggle', 'moveup' 'movedown' 'movetop' or 'movebottom'
$attributes
(optional) Either a typical HTML attribute string or an associative array
Error message | Reason | Solution |
---|---|---|
Argument 1 of HTML_QuickForm_advmultiselect::setButtonAttributes is not a string | Tried to give a button identifier of unknown type | Check the $button argument data type |
Argument 1 of HTML_QuickForm_advmultiselect::setButtonAttributes has unexpected value | Tried to give a button identifier of unknown value | Check the $button argument data range |
since version 0.4.0 (2005-06-25)
This function can not be called statically.
In this example, the 'add' and 'remove' buttons have look set by the css class 'inputCommand'.
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('ams');
$form->removeAttribute('name'); // XHTML compliance
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
// rendering with QF renderer engine and template system
$form->addElement('header', null, 'Advanced Multiple Select: custom layout ');
$ams =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams->setButtonAttributes('add', array('value' => 'Add >>',
'class' => 'inputCommand'
));
$ams->setButtonAttributes('remove', array('value' => '<< Remove',
'class' => 'inputCommand'
));
$template = '
<table{class}>
<!-- BEGIN label_2 --><tr><th align="center">{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th align="center">{label_3}</th></tr><!-- END label_3 -->
<tr>
<td>{unselected}</td>
<td>{selected}</td>
</tr>
<tr>
<td>{add}</td>
<td>{remove}</td>
</tr>
</table>';
$ams->setElementTemplate($template);
if (isset($_POST['fruit'])) {
$form->setDefaults(array('fruit' => $_POST['fruit']));
}
$form->addElement('submit', 'send', 'Send');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect example </title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
.inputCommand {
background-color: #d0d0d0;
border: 1px solid #7B7B88;
width: 7em;
margin-bottom: 2px;
}
// -->
</style>
<?php echo $ams->getElementJs(false); ?>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
</body>
</html>
You may also use images rather than standard form input button. Replaces lines :
<?php
$ams->setButtonAttributes('add', array('value' => 'Add >>',
'class' => 'inputCommand'
));
$ams->setButtonAttributes('remove', array('value' => '<< Remove',
'class' => 'inputCommand'
));
?>
by lines that should like :
<?php
$ams->setButtonAttributes('add', array('type' => 'image',
'src' => '/img/add.png'
));
$ams->setButtonAttributes('remove', array('type' => 'image',
'src' => '/img/remove.png'
));
?>
This renderer is based on HTML_QuickForm_advmultiselect code and require no external classes to do its job. It's very similar to HTML_Quickform default renderer .
string HTML_QuickForm_advmultiselect::toHtml (
)
Returns the advmultiselect element structure as HTML.
throws no exceptions thrown
since version 0.4.0 (2005-06-25)
This function can not be called statically.
Here is the core of customization of this QuickForm element. With the three methods below, you can easily include it in an existing template page with all parts (CSS, JS, lists, buttons) at the right place.
string HTML_QuickForm_advmultiselect::getElementCss (
boolean
$raw
= TRUE
)
If your browser does not support javascript, or for any reasons you have choosen to disable it, then your only possibility is to use the single checkboxes multi-select shape of HTML_QuickForm_advmultiselect element.
You get a more or less decent result with just the basic settings, but it is also highly configurable, so you can almost get what you want.
The basic settings can be applied with just the placeholder {stylesheet} into your template element. It can also be applied with getElementCss() method. If you does not have existing style tags in your html page, you must call the method with FALSE argument. Default behavior returns only the raw css data without style tags. Useful when using with template engine.
$raw
(optional) html output with style tags or just raw data
throws no exceptions thrown
since version 0.4.0 (2005-06-25)
This function can not be called statically.
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('ams');
$form->removeAttribute('name'); // XHTML compliance
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine'
);
// rendering with QF renderer engine and template system
$form->addElement('header', null, 'Advanced Multiple Select: custom layout ');
$ams =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
// template for a single checkboxes multi-select element shape
$template = '
<table{class}>
<!-- BEGIN label_3 --><tr><th>{label_3}</th></tr><!-- END label_3 -->
<tr>
<td>{selected}</td>
</tr>
</table>
';
$ams->setElementTemplate($template);
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
// fruit default values already selected without any end-user actions
$form->setDefaults(array('fruit' => array('kiwi','lime')));
} elseif (isset($_POST['fruit'])) {
// fruit end-user selection
$form->setDefaults(array('fruit' => $_POST['fruit']));
}
$buttons[] =& $form->createElement('submit', null, 'Submit');
$buttons[] =& $form->createElement('reset', null, 'Reset');
$form->addGroup($buttons);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect custom example 4b</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
<?php echo $ams->getElementCss(); ?>
// -->
</style>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
</body>
</html>
string HTML_QuickForm_advmultiselect::getElementJs (
boolean
$raw
= TRUE
,
boolean
$min
= FALSE
)
The basic (and default) settings can be applied with just the placeholder {javascript} into your template element. If you does not have existing script tags in your html page, you must call the method with FALSE argument. Default behaviour returns only the raw js data without script tags. Useful when using with template engine.
$raw
(optional) html output with script tags or just raw data
$min
(optional) uses javascript compressed version
throws no exceptions thrown
since version 0.4.0 (2005-06-25)
$min
parameter is available since version 1.5.0
This function can not be called statically.
void HTML_QuickForm_advmultiselect::setElementTemplate (
string
$html
= NULL
,
boolean
$js
= TRUE
)
It is so easy to re-arrange select boxes, headers and buttons position. The template is a bit of html code with reserved placeholders words. They are:
stylesheet
The CSS delimited by the style tags required only for the single multi-select with checkboxes shape. Not included in the default template. You can use instead, the getElementCss() method.
javascript
The JavaScript delimited by the script tags required to manage item of both multi-select boxes. If it is not included, you can use instead, the getElementJs() method.
class
A CSS class identifier in one of your stylesheets that is the default table layout. Default values are:
label_2
The unselected list header
label_3
The selected list header
unselected
The unselected list.
selected
The selected list.
add
The add button to swap one (or more) item from the unselected to the selected list.
remove
The remove button to swap one (or more) item back from the selected to the unselected list.
moveup
The button to move one item of the selected list to the top.
movedown
The button to move one item of the selected list to the bottom.
movetop
The button to move one item of the selected list directly at the top of the list
movebottom
The button to move one item of the selected list directly at the bottom of the list
$html
(optional) The HTML surrounding select boxes and buttons
$js
(optional) if we need to include qfams javascript handler
throws no exceptions thrown
since version 0.4.0 (2005-06-25)
This function can not be called statically.
In this partial example, the fruit list select boxes are set to vertical alignement with images for the 'add' and 'remove' buttons. Default presentation are horizontal alignment with input text buttons in the middle side.
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('ams');
$form->removeAttribute('name'); // XHTML compliance
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
$ams =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams->setButtonAttributes('add', array('type' => 'image', 'src' => '/img/down.png'));
$ams->setButtonAttributes('remove', array('type' => 'image', 'src' => '/img/up.png'));
// vertical select box with image buttons as selector
$template = '
<table{class}>
<!-- BEGIN label_2 --><tr><th align="center">{label_2}</th></tr><!-- END label_2 -->
<tr>
<td>{unselected}</td>
</tr>
<tr>
<td align="center">{add}{remove}</td>
</tr>
<tr>
<td>{selected}</td>
</tr>
<!-- BEGIN label_3 --><tr><th align="center">{label_3}</th></tr><!-- END label_3 -->
</table>';
$ams->setElementTemplate($template);
// ....
?>
This example has no particular goals. It should be your first run to show and handle a advmultiselect element that will emulate a multi-select.
Without any customization
<?php
/**
* Basic advMultiSelect HTML_QuickForm element
* without any customization.
*
* @version $Id: examples-basic1.xml,v 1.5 2009-02-11 08:51:16 farell Exp $
* @author Laurent Laville <pear@laurent-laville.org>
* @package HTML_QuickForm_advmultiselect
* @subpackage Examples
* @access public
* @example examples/qfams_basic_1.php
* qfams_basic_1 source code
* @link http://www.laurent-laville.org/img/qfams/screenshot/basic1.png
* screenshot (Image PNG, 406x247 pixels) 4.95 Kb
*/
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsBasic1');
$form->removeAttribute('name'); // XHTML compliance
// same as default element template but wihtout the label (in first td cell)
$withoutLabel = <<<_HTML
<tr valign="top">
<td align="right">
</td>
<td align="left">
<!-- BEGIN error --><span style="color: #ff0000;">{error}</span><br /><!-- END error -->{element}
</td>
</tr>
_HTML;
// more XHTML compliant
// replace default element template with label, because submit button have no label
$renderer =& $form->defaultRenderer();
$renderer->setElementTemplate($withoutLabel, 'send');
$car_array = array(
'dodge' => 'Dodge',
'chevy' => 'Chevy',
'bmw' => 'BMW',
'audi' => 'Audi',
'porsche' => 'Porsche',
'kia' => 'Kia',
'subaru' => 'Subaru',
'mazda' => 'Mazda',
'isuzu' => 'Isuzu',
);
// rendering with all default options
$form->addElement('header', null, 'Advanced Multiple Select: default layout ');
$form->addElement('advmultiselect', 'cars', 'Cars:', $car_array);
$form->addElement('submit', 'send', 'Send');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect basic example 1</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
-->
</style>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
</body>
</html>
In this example, here are our goals :
Your first template element
<?php
/**
* Custom advMultiSelect HTML_QuickForm element
* using stylesheet rules selectors and a template.
*
* The template allows to add label as headers of dual select box
* and moves the button to another location (below each select box).
*
* @version $Id: examples-custom1.xml,v 1.5 2009-02-11 08:51:16 farell Exp $
* @author Laurent Laville <pear@laurent-laville.org>
* @package HTML_QuickForm_advmultiselect
* @subpackage Examples
* @access public
* @example examples/qfams_custom_1.php
* qfams_custom_1 source code
* @link http://www.laurent-laville.org/img/qfams/screenshot/custom1.png
* screenshot (Image PNG, 677x197 pixels) 4.80 Kb
*/
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsCustom1');
$form->removeAttribute('name'); // XHTML compliance
// same as default element template but without the label (in first td cell)
$withoutLabel = <<<_HTML
<tr valign="top">
<td align="right">
</td>
<td align="left">
<!-- BEGIN error --><span style="color: #ff0000;">{error}</span><br /><!-- END error -->{element}
</td>
</tr>
_HTML;
// more XHTML compliant
// replace default element template with label, because submit button have no label
$renderer =& $form->defaultRenderer();
$renderer->setElementTemplate($withoutLabel, 'send');
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
// rendering with QF renderer engine and template system
$form->addElement('header', null, 'Advanced Multiple Select: custom layout ');
$ams =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array,
array('size' => 5,
'class' => 'pool', 'style' => 'width:300px;'
)
);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams->setButtonAttributes('add', array('value' => 'Add >>',
'class' => 'inputCommand'
));
$ams->setButtonAttributes('remove', array('value' => '<< Remove',
'class' => 'inputCommand'
));
$template = '
<table{class}>
<!-- BEGIN label_2 --><tr><th align="center">{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th align="center">{label_3}</th></tr><!-- END label_3 -->
<tr>
<td>{unselected}</td>
<td>{selected}</td>
</tr>
<tr>
<td align="center">{add}</td>
<td align="center">{remove}</td>
</tr>
</table>';
$ams->setElementTemplate($template);
$form->addElement('submit', 'send', 'Send');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect custom example 1</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
table.pool {
border: 0;
background-color: lightyellow;
}
table.pool th {
font-size: 80%;
font-style: italic;
text-align: center;
}
table.pool select {
background-color: lightblue;
}
.inputCommand {
background-color: #d0d0d0;
border: 1px solid #7B7B88;
width: 7em;
margin-bottom: 2px;
}
-->
</style>
<?php echo $ams->getElementJs(false); ?>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
</body>
</html>
In this example, here are our goals :
How to do a global selection
<?php
/**
* Custom advMultiSelect HTML_QuickForm element
* with extended buttons (select all, select none, toggle selection)
*
* @version $Id: examples-custom7.xml,v 1.5 2009-02-11 08:51:16 farell Exp $
* @author Laurent Laville <pear@laurent-laville.org>
* @package HTML_QuickForm_advmultiselect
* @subpackage Examples
* @access public
* @example examples/qfams_custom_7.php
* qfams_custom_7 source code
* @link http://www.laurent-laville.org/img/qfams/screenshot/custom7.png
* screenshot (Image PNG, 640x525 pixels) 160 Kb
*/
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsCustom7');
$form->removeAttribute('name'); // XHTML compliance
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
// rendering with QF renderer engine and template system
$form->addElement('header', null, 'Advanced Multiple Select: custom layout ');
$ams =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array,
array('class' => 'pool', 'style' => 'width:200px;')
);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams->setButtonAttributes('add' , 'class=inputCommand');
$ams->setButtonAttributes('remove' , 'class=inputCommand');
$ams->setButtonAttributes('all' , 'class=inputCommand');
$ams->setButtonAttributes('none' , 'class=inputCommand');
$ams->setButtonAttributes('toggle' , 'class=inputCommand');
$ams->setButtonAttributes('moveup' , 'class=inputCommand');
$ams->setButtonAttributes('movedown', 'class=inputCommand');
// template for a single checkboxes multi-select element shape
$template1 = '
<table{class}>
<!-- BEGIN label_3 --><tr><th>{label_3}</th><th> </th></tr><!-- END label_3 -->
<tr>
<td>{selected}</td>
<td>{all}<br />{none}<br />{toggle}</td>
</tr>
</table>
';
// template for a dual multi-select element shape
$template2 = '
<table{class}>
<!-- BEGIN label_2 --><tr><th>{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th> </th><th>{label_3}</th></tr><!-- END label_3 -->
<tr>
<td>{unselected}</td>
<td align="center">
{add}<br />{remove}<br /><br />{all}<br />{none}<br /><br />{moveup}<br />{movedown}<br />
</td>
<td>{selected}</td>
</tr>
</table>
';
if (isset($_POST['multiselect'])) {
$ams->setElementTemplate($template2);
} else {
$ams->setElementTemplate($template1);
}
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
// fruit default values already selected without any end-user actions
$form->setDefaults(array('fruit' => array('kiwi','lime')));
}
$buttons[] =& $form->createElement('submit', null, 'Submit');
$buttons[] =& $form->createElement('reset', null, 'Reset');
$buttons[] =& $form->createElement('checkbox', 'multiselect', null,
'use dual select boxes layout');
$form->addGroup($buttons, null, ' ');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect custom example 7</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
table.pool {
border: 0;
background-color: cyan;
}
table.pool td {
padding-left: 1em;
}
table.pool th {
font-size: 80%;
font-style: italic;
text-align: center;
}
table.pool select {
color: gray;
background-color: #eee;
}
.inputCommand {
width: 120px;
}
<?php
if (!isset($_POST['multiselect'])) {
echo $ams->getElementCss();
}
?>
-->
</style>
<?php echo $ams->getElementJs(false); ?>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
</body>
</html>
In this example, here are our goals :
How to sort the select boxes
<?php
/**
* Custom advMultiSelect HTML_QuickForm element
* that allows to manage sort of select boxes.
*
* @version $Id: examples-custom5.xml,v 1.5 2009-02-11 08:51:16 farell Exp $
* @author Laurent Laville <pear@laurent-laville.org>
* @package HTML_QuickForm_advmultiselect
* @subpackage Examples
* @access public
* @example examples/qfams_custom_5.php
* qfams_custom_5 source code
* @link http://www.laurent-laville.org/img/qfams/screenshot/custom5.png
* screenshot (Image PNG, 609x318 pixels) 9.94 Kb
*/
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsCustom5');
$form->removeAttribute('name'); // XHTML compliance
// same as default element template but wihtout the label (in first td cell)
$withoutLabel = <<<_HTML
<tr valign="top">
<td align="right">
</td>
<td align="left">
<!-- BEGIN error --><span style="color: #ff0000;">{error}</span><br /><!-- END error -->{element}
</td>
</tr>
_HTML;
// more XHTML compliant
// replace default element template with label, because submit button have no label
$renderer =& $form->defaultRenderer();
$renderer->setElementTemplate($withoutLabel, 'validate');
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
// decides either both select list will have their elements be arranged or not
if (isset($_POST['autoArrange'])) {
if ($_POST['autoArrange'] == 'A') {
$sort = SORT_ASC;
} elseif ($_POST['autoArrange'] == 'D') {
$sort = SORT_DESC;
} else {
$sort = false;
}
} else {
$sort = false;
}
// rendering with QF renderer engine and template system
$form->addElement('header', null,
'For demo purpose only: must be validate to be active');
$arrange[] =& $form->createElement('radio', null, null, 'Auto arrange asc.', 'A');
$arrange[] =& $form->createElement('radio', null, null, 'Auto arrange desc.', 'D');
$arrange[] =& $form->createElement('radio', null, null, 'No auto arrange', 'N');
$form->addGroup($arrange, 'autoArrange', 'Sort list:');
$form->setDefaults(array('autoArrange' => 'N'));
$form->addElement('submit', 'validate', 'Validate');
$form->addElement('header', null, 'Advanced Multiple Select: custom layout ');
$ams =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array,
array('class' => 'pool', 'style' => 'width:200px;'),
$sort
);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams->setButtonAttributes('add' , 'class=inputCommand');
$ams->setButtonAttributes('remove' , 'class=inputCommand');
$ams->setButtonAttributes('moveup' , 'class=inputCommand');
$ams->setButtonAttributes('movedown', 'class=inputCommand');
// template for a dual multi-select element shape
$template = '
<table{class}>
<!-- BEGIN label_2 --><tr><th>{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th> </th><th>{label_3}</th></tr><!-- END label_3 -->
<tr>
<td>{unselected}</td>
<td align="center">
{add}<br />{remove}<br /><br />{moveup}<br />{movedown}<br />
</td>
<td>{selected}</td>
</tr>
</table>
';
$ams->setElementTemplate($template);
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
// fruit default values already selected without any end-user actions
$form->setDefaults(array('fruit' => array('kiwi','lime')));
}
$buttons[] =& $form->createElement('submit', null, 'Submit');
$buttons[] =& $form->createElement('reset', null, 'Reset');
$form->addGroup($buttons, null, ' ');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect custom example 5</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
table.pool {
border: 0;
background-color: #339900;
}
table.pool td {
padding-left: 1em;
}
table.pool th {
font-size: 80%;
font-style: italic;
text-align: center;
}
table.pool select {
color: white;
background-color: #006600;
}
.inputCommand {
width: 60px;
}
-->
</style>
<?php echo $ams->getElementJs(false); ?>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
</body>
</html>
In this example, here are our goals :
Two different layouts on the same page
<?php
/**
* Mixed advMultiSelect HTML_QuickForm elements.
* Two widgets on the same page/form with one javascript code instance
*
* @version $Id: examples-multiple1.xml,v 1.5 2009-02-11 08:51:16 farell Exp $
* @author Laurent Laville <pear@laurent-laville.org>
* @package HTML_QuickForm_advmultiselect
* @subpackage Examples
* @access public
* @example examples/qfams_multiple_1.php
* qfams_multiple_1 source code
* @link http://www.laurent-laville.org/img/qfams/screenshot/multiple1.png
* screenshot (Image PNG, 566x392 pixels) 8.82 Kb
*/
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsMultiple1');
$form->removeAttribute('name'); // XHTML compliance
// same as default element template but wihtout the label (in first td cell)
$withoutLabel = <<<_HTML
<tr valign="top">
<td align="right">
</td>
<td align="left">
<!-- BEGIN error --><span style="color: #ff0000;">{error}</span><br /><!-- END error -->{element}
</td>
</tr>
_HTML;
// more XHTML compliant
// replace default element template with label, because submit button have no label
$renderer =& $form->defaultRenderer();
$renderer->setElementTemplate($withoutLabel, 'send');
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
$car_array = array(
'dodge' => 'Dodge',
'chevy' => 'Chevy',
'bmw' => 'BMW',
'audi' => 'Audi',
'porsche' => 'Porsche',
'kia' => 'Kia',
'subaru' => 'Subaru',
'mazda' => 'Mazda',
'isuzu' => 'Isuzu',
);
// rendering with all default options
$form->addElement('header', null, 'Advanced Multiple Select: default layout ');
$ams1 =& $form->addElement('advmultiselect', 'cars', 'Cars:', $car_array);
$ams1->setElementTemplate(null, false);
// rendering with css selectors and API selLabel(), setButtonAttributes()
$form->addElement('header', null, 'Advanced Multiple Select: custom layout ');
$ams2 =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array,
array('size' => 5,
'class' => 'pool', 'style' => 'width:200px;'
)
);
$ams2->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams2->setButtonAttributes('add', array('value' => 'Add', 'name' => 'add1',
'class' => 'inputCommand'
));
$ams2->setButtonAttributes('remove', array('value' => 'Remove', 'name' => 'remove1',
'class' => 'inputCommand'
));
$ams2->setElementTemplate(null, false);
$form->addElement('submit', 'send', 'Send');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect multiple example 1</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
table.pool {
border: 0;
background-color: #339900;
width:450px;
}
table.pool th {
font-size: 80%;
font-style: italic;
text-align: left;
}
table.pool select {
color: white;
background-color: #006600;
}
.inputCommand {
background-color: #d0d0d0;
border: 1px solid #7B7B88;
width: 7em;
margin-bottom: 2px;
}
-->
</style>
<?php
echo $ams1->getElementJs(false, true);
?>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
</body>
</html>
In this example, here are our goals :
ITDynamic renderer with Sigma
<?php
/**
* Custom advMultiSelect HTML_QuickForm element
* embedded into a Sigma template and using the QF dynamic renderer.
*
* @version $Id: examples-template1.xml,v 1.4 2009-02-11 08:51:16 farell Exp $
* @author Laurent Laville <pear@laurent-laville.org>
* @package HTML_QuickForm_advmultiselect
* @subpackage Examples
* @access public
* @example examples/qfams_template_1.php
* qfams_template_1 source code
* @link http://www.laurent-laville.org/img/qfams/screenshot/template1.png
* screenshot (Image PNG, 665x376 pixels) 23.3 Kb
*/
require_once 'HTML/Template/Sigma.php';
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/Renderer/ITDynamic.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsTemplate1');
$form->removeAttribute('name'); // XHTML compliance
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
// rendering with css selectors and API selLabel(), setButtonAttributes()
$form->addElement('header', null, 'Advanced Multiple Select: custom layout ');
$form->addElement('text', 'name', 'Name:', array('size' => 40, 'maxlength' => 80));
$ams =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array,
array('size' => 15,
'class' => 'pool', 'style' => 'width:150px;'
)
);
$ams->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams->setButtonAttributes('add', array('value' => 'Add >>',
'class' => 'inputCommand'
));
$ams->setButtonAttributes('remove', array('value' => '<< Remove',
'class' => 'inputCommand'
));
$template = '
<table{class}>
<!-- BEGIN label_2 --><tr><th>{label_2}</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th> </th><th>{label_3}</th></tr><!-- END label_3 -->
<tr>
<td valign="top">{unselected}</td>
<td align="center">{add}{remove}</td>
<td valign="top">{selected}</td>
</tr>
</table>
';
$ams->setElementTemplate($template);
$form->addElement('submit', 'send', 'Send', array('class' => 'inputCommand'));
$form->addRule('name', 'Your name is required', 'required');
$form->addGroupRule('fruit', 'At least one fruit is required', 'required', null, 1);
$form->applyFilter('__ALL__', 'trim');
$form->applyFilter('__ALL__', 'strip_tags');
$valid = $form->validate();
$tpl = new HTML_Template_Sigma('.');
$tpl->loadTemplateFile('itdynamic.html');
$tpl->setVariable('ams_javascript', $ams->getElementJs(false));
$renderer = new HTML_QuickForm_Renderer_ITDynamic($tpl);
$form->accept($renderer);
if ($valid) {
$clean = $form->getSubmitValues();
$msg = sprintf("<p>Welcome <b>%s</b> you've selected these fruits:<br />%s</p>",
$clean['name'], implode(', ', $clean['fruit']));
$tpl->setVariable('message_form_validate', $msg);
}
$tpl->show();
?>
In this example, here are our goals :
Multiple live counters on same page
<?php
/**
* Two advMultiSelect HTML_QuickForm elements with all properties
* that can be display in one or two select box mode.
* This example demonstrate the new feature of version 1.3.0 : Live Counter
*
* @version $Id: examples-multiple2.xml,v 1.6 2009-02-11 08:51:16 farell Exp $
* @author Laurent Laville <pear@laurent-laville.org>
* @package HTML_QuickForm_advmultiselect
* @subpackage Examples
* @access public
* @example examples/qfams_multiple_2.php
* qfams_multiple_2 source code
* @link http://www.laurent-laville.org/img/qfams/screenshot/multiple2.png
* screenshot (Image PNG, 595x511 pixels) 11.9 Kb
*/
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/advmultiselect.php';
$form = new HTML_QuickForm('amsMultiple2');
$form->removeAttribute('name'); // XHTML compliance
$fruit_array = array(
'apple' => 'Apple',
'orange' => 'Orange',
'pear' => 'Pear',
'banana' => 'Banana',
'cherry' => 'Cherry',
'kiwi' => 'Kiwi',
'lemon' => 'Lemon',
'lime' => 'Lime',
'tangerine' => 'Tangerine',
);
$car_array = array(
'dodge' => 'Dodge',
'chevy' => 'Chevy',
'bmw' => 'BMW',
'audi' => 'Audi',
'porsche' => 'Porsche',
'kia' => 'Kia',
'subaru' => 'Subaru',
'mazda' => 'Mazda',
'isuzu' => 'Isuzu',
);
// template for a single checkboxes multi-select element shape with live counter
$template1 = '
<table{class}>
<!-- BEGIN label_3 --><tr><th>{label_3}(<span id="{selected_count_id}">{selected_count}</span>)</th><th> </th></tr><!-- END label_3 -->
<tr>
<td>{selected}</td>
<td>{all}<br />{none}<br />{toggle}</td>
</tr>
</table>
';
// template for a dual multi-select element shape with live counter
$template2 = '
<table{class}>
<!-- BEGIN label_2 --><tr><th>{label_2}(<span id="{unselected_count_id}">{unselected_count}</span>)</th><!-- END label_2 -->
<!-- BEGIN label_3 --><th> </th><th>{label_3}(<span id="{selected_count_id}">{selected_count}</span>)</th></tr><!-- END label_3 -->
<tr>
<td>{unselected}</td>
<td align="center">
{add}<br />{remove}<br /><br />{all}<br />{none}<br />{toggle}<br /><br />{moveup}<br />{movedown}<br />
</td>
<td>{selected}</td>
</tr>
</table>
';
$defaults = array();
// first QF ams element
$form->addElement('header', null, 'Advanced Multiple Select: Live Counter - pool1 style ');
$ams1 =& $form->addElement('advmultiselect', 'cars', null, $car_array,
array('size' => 10, 'class' => 'pool1', 'style' => 'width:200px;')
);
$ams1->setLabel(array('Cars:', 'Available', 'Selected'));
$ams1->setButtonAttributes('add', array('name' => 'add1', 'class' => 'inputCommand'));
$ams1->setButtonAttributes('remove', array('name' => 'remove1', 'class' => 'inputCommand'));
$ams1->setButtonAttributes('all', array('name' => 'all1', 'class' => 'inputCommand'));
$ams1->setButtonAttributes('none', array('name' => 'none1', 'class' => 'inputCommand'));
$ams1->setButtonAttributes('toggle', array('name' => 'toggle1', 'class' => 'inputCommand'));
$ams1->setButtonAttributes('moveup', array('name' => 'moveup1', 'class' => 'inputCommand'));
$ams1->setButtonAttributes('movedown', array('name' => 'movedown1', 'class' => 'inputCommand'));
if (isset($_POST['multiselect1'])) {
$ams1->setElementTemplate($template2);
} else {
$ams1->setElementTemplate($template1);
}
// second QF ams element
$form->addElement('header', null, 'Advanced Multiple Select: Live Counter - pool2 style ');
$ams2 =& $form->addElement('advmultiselect', 'fruit', null, $fruit_array,
array('size' => 5, 'class' => 'pool2', 'style' => 'width:300px;')
);
$ams2->setLabel(array('Fruit:', 'Available', 'Selected'));
$ams2->setButtonAttributes('add', array('name' => 'add2', 'class' => 'inputCommand'));
$ams2->setButtonAttributes('remove', array('name' => 'remove2', 'class' => 'inputCommand'));
$ams2->setButtonAttributes('all', array('name' => 'all2', 'class' => 'inputCommand'));
$ams2->setButtonAttributes('none', array('name' => 'none2', 'class' => 'inputCommand'));
$ams2->setButtonAttributes('toggle', array('name' => 'toggle2', 'class' => 'inputCommand'));
$ams2->setButtonAttributes('moveup', array('name' => 'moveup2', 'class' => 'inputCommand'));
$ams2->setButtonAttributes('movedown', array('name' => 'movedown2', 'class' => 'inputCommand'));
if (isset($_POST['multiselect2'])) {
$ams2->setElementTemplate($template2);
} else {
$ams2->setElementTemplate($template1);
}
$buttons[] =& $form->createElement('submit', null, 'Submit');
$buttons[] =& $form->createElement('reset', null, 'Reset');
$buttons[] =& $form->createElement('checkbox', 'multiselect1', null,
'cars list dual select');
$buttons[] =& $form->createElement('checkbox', 'multiselect2', null,
'fruit list dual select');
$form->addGroup($buttons, null, ' ');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML_QuickForm::advMultiSelect multiple example 2</title>
<style type="text/css">
<!--
body {
background-color: #FFF;
font-family: Verdana, Arial, helvetica;
font-size: 10pt;
}
table.pool1, table.pool2 {
border: 0;
background-color: #339900;
width:450px;
}
table.pool2 {
background-color: #CFC;
}
table.pool1 th, table.pool2 th {
font-size: 80%;
font-style: italic;
text-align: left;
}
table.pool1 td, table.pool2 td {
vertical-align: top;
}
table.pool1 select, table.pool2 select {
color: white;
background-color: #006600;
}
.inputCommand {
background-color: #d0d0d0;
border: 1px solid white;
width: 9em;
margin-bottom: 2px;
}
<?php
if (!isset($_POST['multiselect1'])) {
echo $ams1->getElementCss();
}
if (!isset($_POST['multiselect2'])) {
echo $ams2->getElementCss();
}
?>
-->
</style>
<?php echo $ams1->getElementJs(false); ?>
</head>
<body>
<?php
if ($form->validate()) {
$clean = $form->getSubmitValues();
echo '<pre>';
print_r($clean);
echo '</pre>';
}
$form->display();
?>
<script type="text/javascript">
//
function init() {
QFAMS.init(['cars','fruit']);
}
window.addEventListener('load', init, false);
//
</script>
</body>
</html>
HTML_QuickForm2 is a PHP5 rewrite of HTML_QuickForm and HTML_QuickForm_Controller packages. It provides methods to create, validate and render HTML forms.
Major advantages over PHP4 versions:
alert()
(similar to HTML_QuickForm_DHTMLRulesTableless).
This chapter is intended both for those completely unfamiliar with HTML_QuickForm2 and for users of PHP4 version of HTML_QuickForm who need either an overview of package features or tips for migrating their scripts. The chapter also includes a list of examples installed with the package: those are longer and more complex than examples within documentation.
Builds and processes a form with a single input field
<?php
// Load the main class
require_once 'HTML/QuickForm2.php';
// Instantiate the HTML_QuickForm2 object
$form = new HTML_QuickForm2('tutorial');
// Set defaults for the form elements
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'name' => 'Joe User'
)));
// Add some elements to the form
$fieldset = $form->addElement('fieldset')->setLabel('QuickForm2 tutorial example');
$name = $fieldset->addElement('text', 'name', array('size' => 50, 'maxlength' => 255))
->setLabel('Enter your name:');
$fieldset->addElement('submit', null, array('value' => 'Send!'));
// Define filters and validation rules
$name->addFilter('trim');
$name->addRule('required', 'Please enter your name');
// Try to validate a form
if ($form->validate()) {
echo '<h1>Hello, ' . htmlspecialchars($name->getValue()) . '!</h1>';
exit;
}
// Output the form
echo $form;
?>
Lets review the above example step by step.
The line
<?php
$form = new HTML_QuickForm2('tutorial');
?>
creates an instance of HTML_QuickForm2
that will contain objects representing
form elements and other necessary information. We only pass the form's id to the constructor, which means that default
values will be used for other parameters. In particular, the form's method will default to
POST
and the form's action to the current file. When using QuickForm2, it is
easier to keep all the form related logic in one file.
Next we add a DataSource
<?php
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'name' => 'Joe User'
)));
?>
containing the default value 'Joe User'
for name
element.
DataSources are objects that provide elements' incoming values, they are searched in the order
they were added to the form. When constructor of HTML_QuickForm2
considers the form submitted, it will automatically add a DataSource with submit values, so
search will not reach the DataSource added above. However, when submit DataSource is not there,
default value will be taken from the above DataSource.
To improve the presentation, we are adding a fieldset element to the form first:
<?php
$fieldset = $form->addElement('fieldset')->setLabel('QuickForm2 tutorial example');
?>
and set its label (it will be output as <legend>
, actually).
We then add a text input box and a submit button to the fieldset:
<?php
$name = $fieldset->addElement('text', 'name', array('size' => 50, 'maxlength' => 255))
->setLabel('Enter your name:');
$fieldset->addElement('submit', null, array('value' => 'Send!'));
?>
Note that most of the setter methods are returning $this
, so it is
possible to chain method calls. Note also that we can arbitrarily nest fieldsets and other
containers (you can't nest another HTML_QuickForm2 instance, however).
The line
<?php
$name->addFilter('trim');
?>
adds a filter for the element's value - the function that will be applied to it when getValue() is called. In this case it is a builtin trim() function, but can be any valid callback. Thus we will strip all leading and trailing whitespace from the name, as we do not need it and as we want to be sure that a name was entered, not just some spaces.
Next we define a rule for the field:
<?php
$name->addRule('required', 'Please enter your name');
?>
This means that QuickForm2 will display an error message if the name was not entered. Note also that QuickForm2 will automatically mark required fields in the form.
We now have the form built and rules defined and need to decide whether to process it or display:
<?php
if ($form->validate()) {
// Do some stuff
}
?>
HTML_QuickForm2::validate() method
will consider the form valid (i.e. return TRUE) if some data was actually submitted and all
the rules defined for the form were
satisfied. In our case this means that 'name'
element was not left
empty.
If the form is validated we need to process the values
<?php
echo '<h1>Hello, ' . htmlspecialchars($name->getValue()) . '!</h1>';
exit;
?>
This is an example, in your scripts you'll usually want to store the values somewhere and to redirect to some other page to prevent a duplicate submit.
The last line is pretty easy:
<?php
echo $form;
?>
If the form is not valid, which means that it either was not yet submitted or that there were errors, it will be displayed. Error messages (if any) will be displayed near the corresponding elements.
Constructors of all the elements now have the same list of parameters:
$name
Element name
$attributes
HTML attributes
$data
Additional element-specific data. A 'label'
key in this
array is understood by every element and is used for element's label.
Consequently HTML_QuickForm2_Factory::createElement() and HTML_QuickForm2_Container::addElement() that pass their arguments to the element's constructor have fixed signatures, too.
For example, the following HTML_QuickForm code which adds a radio button and a select element to the form
<?php
$form->addElement('radio', 'iradTest', 'Test Radio Buttons:', 'Check the radio button #1', 1,
array('class' => 'fancyRadio'));
$form->addElement('select', 'iselTest', 'Test Select:', array('A'=>'A', 'B'=>'B', 'C'=>'C', 'D'=>'D'),
array('size' => 3, 'multiple' => 'multiple'));
?>
in HTML_QuickForm2 will be transformed to
<?php
$form->addElement('radio', 'iradTest', array('class' => 'fancyRadio', 'value' => 1),
array('label' => 'Test Radio Buttons:', 'content' => 'Check the radio button #1'));
$form->addElement('select', 'iselTest', array('size' => 3, 'multiple' => 'multiple'),
array('label' => 'Test Select:', 'options' => array('A'=>'A', 'B'=>'B', 'C'=>'C', 'D'=>'D'));
?>
Another possibility is using fluent interfaces
<?php
$form->addElement('radio', 'iradTest', array('value' => 1))
->addClass('fancyRadio')
->setLabel('Test Radio Buttons:')
->setContent('Check the radio button #1');
$form->addElement('select', 'iselTest', array('size' => 3, 'multiple' => 'multiple'))
->setLabel('Test Select:')
->loadOptions(array('A'=>'A', 'B'=>'B', 'C'=>'C', 'D'=>'D'));
?>
which has the benefits of improved readability and code completion in IDEs.
Unlike HTML_QuickForm, where nesting of form elements was limited (elements could be added either
directly to the form or to a group added directly to the form) HTML_QuickForm2 supports unlimited
nesting. Elements that can contain other elements are subclasses of HTML_QuickForm2_Container
which implements DOM-like API: appendChild(), insertBefore(), removeChild(), getElementById(), getElementsByName().
Convenience method addElement() that creates an
element of the given type and adds it to the Container is still available, too. Thanks to
method overloading you can also perform addEltype() calls where
'eltype'
is an element type known to HTML_QuickForm2_Factory:
<?php
$form->addText('testTextBox', array('style' => 'width: 20ex;'));
?>
Note that HTML_QuickForm2 does not use HTML tables for rendering the form, so there is no equivalent to 'header' element of HTML_QuickForm, fieldsets should be used to group form elements. Also there is no special HTML_QuickForm::addGroup(), groups are treated as any other element (addGroup() will work, though, thanks to abovementioned method overloading). Thus code from HTML_QuickForm
<?php
$form->addElement('header', 'personal', 'Personal Information');
$form->addGroup(array(
HTML_QuickForm::createElement('text', 'first', 'First', 'size=10'),
HTML_QuickForm::createElement('text', 'last', 'Last', 'size=10')
), 'name', 'Name:', ', ');
?>
can be changed to the following in HTML_QuickForm2
<?php
$fieldset = $form->addFieldset('personal')->setLabel('Personal Information');
$group = $fieldset->addGroup('name')->setLabel('Name:')->setSeparator(', ');
$group->addText('first', 'size=10', array('label' => 'First'));
$group->addText('last', 'size=10', array('label' => 'Last'));
?>
Elements' incoming values in HTML_QuickForm2 are kept in an array of DataSources, which are searched for a value in the order they were added to the form. A DataSource containing submit values will be added automatically to that list if the form was submitted. The equivalent to HTML_QuickForm::setDefaults() call
<?php
$form->setDefaults(array(
'foo' => 'default foo value',
'bar' => 'default bar value'
));
?>
is the following call to HTML_QuickForm2::addDataSource():
<?php
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'foo' => 'default foo value',
'bar' => 'default bar value'
)));
?>
It is possible to replace the whole array of form's DataSources using HTML_QuickForm2::setDataSources(), so you can change HTML_QuickForm::setConstants() call
<?php
$form->setConstants(array(
'const' => 'this will override submitted value'
));
?>
to
<?php
$datasources = $form->getDataSources();
array_unshift($datasources, new HTML_QuickForm2_DataSource_Array(array(
'const' => 'this will override submitted value'
)));
$form->setDataSources($datasources);
?>
Here we use the fact that new DataSource will be searched before one containing submitted values, so its values will override submitted ones.
While HTML_QuickForm contained several methods for getting form's and elements' values, HTML_QuickForm2 only has getValue() and getRawValue(), the difference being that the former applies filters to the value and the latter does not. There is no longer a special "submit value" stored separately from form element and returned by HTML_QuickForm::getSubmitValue() / HTML_QuickForm::getSubmitValues().
Calling getValue() on an element returns the element's submit value or NULL for elements that do not currently have a submit value or can not have such a value at all. The value also passes "intrinsic validation" to make sure that it could possibly originate from that element (e.g. select will only return values for options that were added to select). This behaviour is closest to that of old HTML_QuickForm::exportValue().
Calling getValue() on a HTML_QuickForm2 object is equivalent to old HTML_QuickForm::exportValues().
Main differences to filters in HTML_QuickForm:
The following HTML_QuickForm::applyFilter() calls in HTML_QuickForm
<?php
$form->applyFilter('__ALL__', 'trim');
$form->applyFilter('amount', 'intval');
?>
can be converted to the following in HTML_QuickForm2
<?php
$form->addRecursiveFilter('trim');
$amount->addFilter('intval');
?>
Main differences to HTML_QuickForm:
Simple addRule() call in HTML_QuickForm
<?php
$form->addRule('username', 'Username should be at least 5 symbols long', 'minlength', 5, 'client');
?>
can be changed to the following in HTML_QuickForm2
<?php
$username->addRule('minlength', 'Username should be at least 5 symbols long', 5,
HTML_QuickForm2_Rule::CLIENT_SERVER);
?>
There is no longer a special HTML_QuickForm::addGroupRule() method. 'nonempty'
/
'required'
Rule can directly validate a group or other Container making sure
that it contains a given number of nonempty elements, there is also a special
'each'
Rule that applies a given template Rule to each element in a Container.
Thus the following addGroupRule() calls
<?php
$form->addGroupRule('phoneNo', 'Please fill all phone fields', 'required', null, 3, 'client');
$form->addGroupRule('phoneNo', 'Values must be numeric', 'regex', '/^\\d+$/', 3, 'client');
?>
can be changed to
<?php
$phoneNo->addRule('required', 'Please fill all phone fields', 3,
HTML_QuickForm2_Rule::CLIENT_SERVER);
$phoneNo->addRule('each', 'Values must be numeric',
$phoneNo->createRule('regex', '', '/^\\d+$/'),
HTML_QuickForm2_Rule::CLIENT_SERVER);
?>
There is no longer a special addFormRule() method. You can achieve similar behaviour by creating a subclass of HTML_QuickForm2_Rule, implementing its validateOwner() and setOwnerError() methods and adding an instance of that Rule directly to the form:
<?php
class FormRule extends HTML_QuickForm2_Rule
{
protected function validateOwner()
{
$fooValue = $this->owner->getElementById('foo')->getValue();
$barValue = $this->owner->getElementById('bar')->getValue();
return someComplexCheck($fooValue, $barValue);
}
protected function setOwnerError()
{
$this->owner->getElementById('foo')->setError('foo error');
$this->owner->getElementById('bar')->setError('bar error');
}
}
$form->addRule(new FormRule($form));
?>
Note, though, that it may be easier to use Rule chaining instead in most cases.
File
elements no longer require special 'uploadedfile'
and
'filename'
rules, standard 'required'
and
'regex'
can be used (client-side as well).
There is now a javascript library provided with the package which contains code necessary for client-side validation to run. This library should be included in the page if you intend to use client-side validation.
Basic form output in HTML_QuickForm2 is quite easy thanks to a magic __toString() method:
<?php
echo $form;
?>
Under the hood the package uses Renderer setup similar to that of HTML_QuickForm. Currently HTML_QuickForm2 contains ports of Default and Array renderers from HTML_QuickForm, renderers for specific template engines are unlikely to be added to the base package.
Method that accepts a Renderer instance to output the element is now called render() rather than accept() as in HTML_QuickForm.
Default renderer in HTML_QuickForm2 is different from that in HTML_QuickForm:
Consider reviewing the examples installed with the package, they contain useful bits on styling the form.
It is also quite easy to output the form manually by iterating over its elements,
<?php
foreach ($form as $element) {
echo '<label for="' . $element->getId() . '">' . $element->getLabel()
. '</label> ' . $element->__toString() . '<br />';
}
?>
but client-side validation rules are built in the course of the render() call, so there is a special Stub renderer to assist with manual output.
If you are using client-side validation or javascript-backed elements like hierselect or repeat, you should take care that necessary javascript libraries are included in the page before the form. Consult the section on Javascript support.
If you installed HTML_QuickForm2 with PEAR installer, then its usage examples
are in HTML_QuickForm2/examples
directory under PEAR's
doc_dir
. If you have trouble finding where doc_dir
is, you can use config-show command of PEAR
installer.
basic-elements.php
Form showing all built-in elements.
builtin-rules.php
Form showing all built-in rules with server-side and client-side validation.
default-renderer.php
Shows how to customize output of Default Renderer.
dualselect.php
A custom element with a Renderer plugin and additional Javascript library.
hierselect-ajax.php
Shows how to use AJAX requests to load additional options for a Hierselect element.
repeat.php
Demonstrates the Repeat element.
controller/simple.php
Single-page form taking advantage of HTML_QuickForm2_Controller infrastructure.
controller/tabbed.php
Tabbed multipage form: contains buttons that allow going to any page of the form whether
current one is valid or not. The global 'Submit'
button will not
allow form processing, however, unless all pages are valid.
controller/wizard.php
Wizard-like multipage form: pages contain 'Next'
and
'Back'
buttons and you can't go to the next page unless the current page is
valid.
renderers/array-twig.php
Shows how to use Array Renderer together with Twig template engine.
This chapter lists all element classes available in HTML_QuickForm2 and describes their base and element-specific API.
Element classes in HTML_QuickForm2, including HTML_QuickForm2 itself, are descended from either HTML_QuickForm2_Container or HTML_QuickForm2_Element, class tree for those two abstract classes given below. Common API for elements is mostly defined in HTML_QuickForm2_Node.
HTML_QuickForm2_Container - a common parent class for form elements that can contain other elements (e.g. fieldsets, groups).
HTML_QuickForm2_Element - a common parent class for "scalar" form elements.
As can be seen from the above tree, all elements are descended from HTML_Common2, so you have access to attribute-handling methods defined in that class. Please refer to HTML_Common2 documentation for more info.
Many elements in HTML_QuickForm2 take advantage of $watchedAttributes
feature of HTML_Common2 to prevent e.g. changing method
attribute of <form>
tag and type
attribute of
<input>
tag or to update the element's value when its name changes.
As all elements need an id
attribute for proper
<label>
output and for client-side validation, such an attribute will be
automatically generated based on element's name if not given explicitly.
Automatic generation of id
attributes
<?php
echo HTML_QuickForm2_Factory::createElement(
'text', 'name', array('id' => 'explicitId')
)->getAttribute('id') . "\n";
echo HTML_QuickForm2_Factory::createElement('text', 'name')
->getAttribute('id') . "\n";
echo HTML_QuickForm2_Factory::createElement('text', 'name')
->getAttribute('id') . "\n";
echo HTML_QuickForm2_Factory::createElement('text', 'name[key]')
->getAttribute('id') . "\n";
echo HTML_QuickForm2_Factory::createElement('submit')
->getAttribute('id') . "\n";
?>
The above code will output:
explicitId
name-0
name-1
name-key-0
qfauto-0
Note that unique ids are generated and that by default an index is appended to a generated
id
. If you want to generate an id
attribute equal to
name
by default, you can set an option named
'id_force_append_index'
to FALSE
Automatic generation of id
attributes without mandatory indexes
<?php
HTML_Common2::setOption('id_force_append_index', false);
echo HTML_QuickForm2_Factory::createElement(
'text', 'name', array('id' => 'explicitId')
)->getAttribute('id') . "\n";
echo HTML_QuickForm2_Factory::createElement('text', 'name')
->getAttribute('id') . "\n";
echo HTML_QuickForm2_Factory::createElement('text', 'name')
->getAttribute('id') . "\n";
echo HTML_QuickForm2_Factory::createElement('text', 'name[key]')
->getAttribute('id') . "\n";
echo HTML_QuickForm2_Factory::createElement('submit')
->getAttribute('id') . "\n";
?>
The above code will output:
explicitId
name
name-0
name-key
qfauto-0
Constructors of all the elements have the same list of parameters, as defined in HTML_QuickForm2_Node:
$name
Element name
$attributes
HTML attributes
$data
Additional element-specific data. A 'label'
key in this array is understood
by every element and is used for element's label. Other possible keys are described in the
list of built-in elements.
As a result, HTML_QuickForm2_Factory::createElement() and HTML_QuickForm2_Container::addElement() that pass their arguments to the element's constructor also have constant list of parameters, the first being element type and subsequent ones corresponding to constructor parameters.
Getters and setters for miscellaneous element properties: setName() / getName(), setId() / getId(), setLabel() / getLabel(), getContainer(), getData(), getType().
Setter methods in HTML_QuickForm2 tend to return
$this
, so that it is possible to chain their invocations:<?php
$text->setName('aText')
->setLabel('Text input field:');
?>
Methods for getting and changing elements' values: setValue(), getValue(), getRawValue(). Their detailed description is in the section on values and data sources.
Filtering the elements' values is done by addFilter() and addRecursiveFilter() methods. The former is applied directly to element's value on getValue() call and the latter is propagated to contained elements if the element is a Container or applied recursively to the value if element's value is an array.
Validation-related methods: setError() / getError(), createRule(), addRule() / removeRule(), isRequired(). Described in the section on validation.
The elements implement magic __toString() method so they can be used in string contexts:
<?php
$element = new HTML_QuickForm2_Element_InputText(
'textBox', array('size' => 20, 'id' => 'textBoxId')
);
echo $element;
?>
with output being
<input type="text" id="textBoxId" size="20" name="textBox" />
Complex output needs of form elements (including HTML_QuickForm2 itself) are covered by render() method accepting a specialized Renderer object.
A helpful feature of HTML_QuickForm2 is the ability to "freeze" form elements, displaying their values without HTML input tags. This may be used for an additional confirmation step after form submit or for sending a filled form via email, among other things. Two methods deal with this: toggleFrozen() and persistentFreeze(), the former toggling the "frozen" status and the latter "persistent freeze" behaiour. If persistent freeze is on, element's value will be kept (and possibly submitted) in a hidden field.
Freezing the element
<?php
$box = new HTML_QuickForm2_Element_InputCheckbox(
'aBox', array('value' => 'boxValue', 'checked' => 'checked')
);
echo $box . "\n\n";
$box->toggleFrozen(true);
echo $box . "\n\n";
$box->persistentFreeze(false);
echo $box;
?>
the above code results in
<input type="checkbox" value="boxValue" checked="checked" name="aBox" id="aBox-0" />
<code>[x]</code><input type="hidden" name="aBox" value="boxValue" id="aBox-0" />
<code>[x]</code>
In addition to methods defined in HTML_QuickForm2_Node, Container defines DOM-like API for handling of child elements: appendChild(), insertBefore(), removeChild(), getElementById(), getElementsByName(). Those who have worked with Javascript or PHP's DOM extension should find these familiar.
DOM-like API for Container
<?php
$fieldset = new HTML_QuickForm2_Container_Fieldset();
$radioOne = $fieldset->appendChild(
new HTML_QuickForm2_Element_InputRadio('aRadio', array('id' => 'radioOne'))
);
$radioThree = $fieldset->appendChild(
new HTML_QuickForm2_Element_InputRadio('aRadio', array('id' => 'radioThree'))
);
$radioTwo = $fieldset->insertBefore(
new HTML_QuickForm2_Element_InputRadio('aRadio', array('id' => 'radioTwo')),
$radioThree
);
echo $fieldset->getElementById('radioOne') . "\n\n";
$fieldset->removeChild($radioOne);
foreach ($fieldset->getElementsByName('aRadio') as $radio) {
echo $radio . "\n";
}
?>
will output
<input type="radio" value="on" id="radioOne" name="aRadio" />
<input type="radio" value="on" id="radioTwo" name="aRadio" />
<input type="radio" value="on" id="radioThree" name="aRadio" />
A few convenience methods are also available: getElements() returns an
array with all the Container's elements and addElement() creates an
element of a given type and adds it to the Container. Thanks to method overloading you can also
perform addEltype() calls where 'eltype'
is an element type
known to HTML_QuickForm2_Factory
(the next section contains the
list of such types and relevant examples).
Container also implements Countable and IteratorAggregate SPL interfaces, allowing to easily count the immediate children and iterate over them. It also has getRecursiveIterator() method which returns an instance of HTML_QuickForm2_ContainerIterator for recursive iteration over all child elements.
SPL interfaces support
<?php
$outer = new HTML_QuickForm2_Container_Fieldset();
$inner = $outer->addElement('fieldset')->setId('inner');
$inner->addElement('text', 'textName')->setId('textId');
echo count($outer) . "\n";
foreach ($outer as $child) {
echo $child->getId() . "\n";
}
echo "\n";
foreach ($outer->getRecursiveIterator() as $child) {
echo $child->getId() . "\n";
}
?>
The above code will output
1
inner
inner
textId
HTML_QuickForm2 is a class representing HTML form. It is a subclass of HTML_QuickForm2_Container and thus inherits the API of Container and its ancestors. Only a few additional form-specific methods are defined (and a few previously protected methods are declared public).
Those familiar with HTML_QuickForm package should
notice that this approach is quite different from that of HTML_QuickForm
class which defined dozens of custom methods. As a result QuickForm2.php
file containing HTML_QuickForm2 class is an order of magnitude smaller
than QuickForm.php
containing HTML_QuickForm.
HTML_QuickForm2::__construct() accepts the following parameters:
$id
id
attribute for <form>
tag
$method
= 'post'
HTTP method used to submit the form.
$attributes
= NULLAdditional attributes for <form>
tag.
$trackSubmit
= TRUEWhether to track if the form was submitted.
The only way to set id
and method
attributes is through the
constructor. Attempts to change them afterwards will result in an Exception.
When $trackSubmit
is on, a hidden field with a specially crafted name is
added to the form. If that name is present in $_REQUEST
then the form is
considered submitted. This is especially useful if you have several
HTML_QuickForm2-backed forms on one page or if the form uses
GET
submit method and the page may receive other $_GET
parameters.
When $trackSubmit
is off the form only checks that either
$_GET
or $_POST
array (depending on form submit method) is
not empty.
Element behind $trackSubmit
<?php
$form = new HTML_QuickForm2('trackMe');
// iterating over supposedly empty form
foreach ($form as $element) {
echo $element;
}
?>
output:
<input type="hidden" id="qf:trackMe" name="_qf__trackMe" />
Unlike simpler elements, form's values cannot be set via setValue()
method, data
sources should be used. getDataSources() returns the list
of form's data sources, setDataSources() replaces that list
with a new one and addDataSource() adds a data source to
the list. Data source containing submit values (based on a superglobal $_GET
or $_POST
array) is added automatically if the form is considered submitted.
Coincidentally isSubmitted() method checks whether the list of form data sources contains an instance of HTML_QuickForm2_DataSource_Submit, which usually happens when the form was submitted.
validate() method performs server-side validation.
<?php
if ($form->isSubmitted() && $form->validate()) {
// form is valid, process its values
doSomeProcessing($form->getValue());
}
?>is redundant, validate() checks submit status and will return FALSE if the form wasn't submitted.
The following is a list of non-abstract descendants of HTML_QuickForm2_Node. These elements are pre-registered with HTML_QuickForm2_Factory and thus can be instantiated with HTML_QuickForm2_Factory::createElement() and added to a Container with either HTML_QuickForm2_Container::addElement() or its overloaded addEltype() method using "Type name" from tables below.
<?php
// will create an instance of HTML_QuickForm2_Element_Textarea
$area = HTML_QuickForm2_Factory::createElement('textarea', 'areaName');
// will add a new instance of HTML_QuickForm2_Element_Select to $container
$select = $container->addElement('select', 'selectName');
// will add a new instance of HTML_QuickForm2_Element_InputText to $container
$text = $container->addText('textName');
?>
Type name | Class | Description, extra $data keys, extra methods |
---|---|---|
'button' |
HTML_QuickForm2_Element_Button |
<button></button> elements. $data may
contain 'content' key with HTML to add between
<button></button> tags. Implements setContent()
/ getContent() methods.
|
'checkbox' |
HTML_QuickForm2_Element_InputCheckbox |
<input type="checkbox" /> elements.
$data may contain 'content' key with a label that
should be "glued" to checkbox. Implements setContent()
/ getContent() methods.
|
'fieldset' |
HTML_QuickForm2_Container_Fieldset | <fieldset></fieldset> elements, labels for them will be
rendered as <legend></legend> . |
'file' |
HTML_QuickForm2_Element_InputFile |
<input type="file" /> elements. This element validates
itself by checking a relevant 'error' field in $_FILES
array. $data may contain 'messageProvider'
and 'language' keys for setting up localized error messages.
|
'hidden' |
HTML_QuickForm2_Element_InputHidden | <input type="hidden" /> elements |
'image' |
HTML_QuickForm2_Element_InputImage |
<input type="image" /> elements
|
'inputbutton' |
HTML_QuickForm2_Element_InputButton | <input type="button" /> elements |
'password' |
HTML_QuickForm2_Element_InputPassword | <input type="password" /> elements |
'radio' |
HTML_QuickForm2_Element_InputRadio |
<input type="radio" /> elements. $data
may contain 'content' key with a label that should be "glued" to
radiobutton. Implements setContent()
/ getContent() methods.
|
'reset' |
HTML_QuickForm2_Element_InputReset | <input type="reset" /> elements |
'select' |
HTML_QuickForm2_Element_Select |
|
'submit' |
HTML_QuickForm2_Element_InputSubmit | <input type="submit" /> elements |
'text' |
HTML_QuickForm2_Element_InputText | <input type="text" /> elements |
'textarea' |
HTML_QuickForm2_Element_Textarea | <textarea></textarea> elements |
Type name | Class | Description, extra $data keys, extra methods |
---|---|---|
'date' |
HTML_QuickForm2_Element_Date | Group of selects used to input dates (and times). Described in a separate section. |
'group' |
HTML_QuickForm2_Container_Group | Group of form elements. Several elements may be grouped into a single entity and this entity used as a single element. Date and Hierselect are based on Group. |
'hierselect' |
HTML_QuickForm2_Element_Hierselect | Two or more select elements, selecting the value in the first changes options in the second and so on. Described in a separate section. |
'repeat' |
HTML_QuickForm2_Container_Repeat | Repeats a given 'prototype' Container multiple times. Described in a separate section. |
'script' |
HTML_QuickForm2_Element_Script | Class for adding inline javascript to the form, based on Static. |
'static' |
HTML_QuickForm2_Element_Static |
A pseudo-element used to add text or markup to the form, when that text should be
output similar to form element.
Implements setContent() / getContent() methods and setTagName(). |
basic-elements.php
example installed with the package shows all built-in elements.
New element types can be registered by HTML_QuickForm2_Factory::registerElement(), HTML_QuickForm2_Factory::isElementRegistered() checks whether an element is known to Factory.
<?php
HTML_QuickForm2_Factory::registerElement(
'dualselect', 'HTML_QuickForm2_Element_DualSelect'
);
// ...
if (HTML_QuickForm2_Factory::isElementRegistered('dualselect')) {
$form->addElement('dualselect', 'dualselectDemo', $attributes, $config);
}
?>
"Content" these methods deal with differs from element to element. For Button
elements it is the text to output between <button></button>
tags,
for Static elements this content is
essentially the element itself, it may or may not be wrapped in a tag. For
Checkbox and Radio elements this is the label
that is returned "glued" to the element by its __toString() method,
as opposed to a regular label that is only output by a Renderer.
Setting elements' content
<?php
echo HTML_QuickForm2_Factory::createElement(
'button', 'aButton', array('type' => 'submit', 'id' => 'buttonId')
)->setContent('Button text') . "\n";
echo HTML_QuickForm2_Factory::createElement('static')
->setContent('A static element') . "\n";
echo HTML_QuickForm2_Factory::createElement('static')
->setId('staticId')
->setTagName('div')
->setContent('Another static element') . "\n";
echo HTML_QuickForm2_Factory::createElement(
'radio', 'aRadio', array('id' => 'radioId', 'value' => 'yes')
)->setContent('Click the radio') . "\n";
echo HTML_QuickForm2_Factory::createElement(
'checkbox', 'aBox', array('id' => 'boxId', 'value' => 'yes')
)->setContent('Click the checkbox')
->setLabel('Will not be printed here') . "\n";
?>
output:
<button id="buttonId" type="submit" name="aButton">Button text</button>
A static element
<div id="staticId">Another static element</div>
<input type="radio" value="yes" id="radioId" name="aRadio" /><label for="radioId">Click the radio</label>
<input type="checkbox" id="boxId" value="yes" name="aBox" /><label for="boxId">Click the checkbox</label>
<option>
s and <optgroup>
s can be added either
one by one using addOption() and
addOptgroup() method or
loaded from an associative array using loadOptions().
Two ways to add options
<?php
$selectSlow = new HTML_QuickForm2_Element_Select('slow');
$selectSlow->addOption('PEAR', 1);
$groupOne = $selectSlow->addOptgroup('HTML');
$groupOne->addOption('HTML_AJAX', 2);
$groupOne->addOption('HTML_Common2', 3);
$groupOne->addOption('HTML_QuickForm2', 4, array('style' => 'background: #808080'));
$groupTwo = $selectSlow->addOptgroup('HTTP');
$groupTwo->addOption('HTTP_Download', 5);
$groupTwo->addOption('HTTP_Request2', 6);
$selectFast = new HTML_QuickForm2_Element_Select('fast');
$selectFast->loadOptions(array(
1 => 'PEAR',
'HTML' => array(
2 => 'HTML_AJAX',
3 => 'HTML_Common2',
4 => 'HTML_QuickForm2'
),
'HTTP' => array(
5 => 'HTTP_Download',
6 => 'HTTP_Request2'
)
));
echo $selectSlow . "\n" . $selectFast;
?>
output:
<select name="slow" id="slow-0">
<option value="1">PEAR</option>
<optgroup label="HTML">
<option value="2">HTML_AJAX</option>
<option value="3">HTML_Common2</option>
<option style="background: #808080" value="4">HTML_QuickForm2</option>
</optgroup>
<optgroup label="HTTP">
<option value="5">HTTP_Download</option>
<option value="6">HTTP_Request2</option>
</optgroup>
</select>
<select name="fast" id="fast-0">
<option value="1">PEAR</option>
<optgroup label="HTML">
<option value="2">HTML_AJAX</option>
<option value="3">HTML_Common2</option>
<option value="4">HTML_QuickForm2</option>
</optgroup>
<optgroup label="HTTP">
<option value="5">HTTP_Download</option>
<option value="6">HTTP_Request2</option>
</optgroup>
</select>
Note, however, that only the first way allows passing additional attributes.
Static elements are used to add text or markup to the form that will be later output as if it was a form element. Unlike standard form elements it obviosly cannot have a submit value so will only consider getting its value (setValue() is equivalent to setContent() for these elements) from data sources that do not implement HTML_QuickForm2_DataSource_Submit.
Being descended from HTML_Common2, Static elements can have attributes as do all other elements. It is also possible to use setTagName() to provide a name for a tag that will use these attributes and wrap around element's content.
<?php
if ($pic = loadPictureDataFromSomewhere()) {
$form->addStatic('picture', array(
'src' => $pic['url'], 'width' => $pic['width'],
'height' => $pic['height'], 'alt' => $pic['title']
))
->setTagName('img', false)
->setLabel('Existing picture:');
} else {
$form->addFile('picture_upload')
->setLabel('Upload new picture');
}
?>
Static elements will prevent setting tag name to a name of form-related tag (i.e.
'div'
will work,'input'
will not).
HTML_QuickForm2_Container_Group is a specialized subclass of Container which allows to combine several form elements into an entity behaving like a single form element. Key features:
A named group prepends its name to the contained elements' names;
HTML_QuickForm2_Container_Group implements setValue() method;
Grouped elements are processed in a different way than ungrouped ones by renderers.
Groups can be used for
Keeping together elements having the same name (checkboxes and radios).
Visual grouping of elements (e.g. 'Back', 'Next' and 'Cancel' buttons of a wizard).
Logical grouping of elements (e.g. fields to input first and last name of a person).
Groups are also used as a base for custom elements like Date and Hierselect.
If you add an element to a group having a name itself, group's name will be prepended to the element's name. If an element is added to a group without a name, its name will not be changed:
<?php
$named = $form->addGroup('groupName');
$innerNamed = $named->addText('elementName');
$innerComplex = $named->addText('elementOuter[elementInner]');
$nameless = $form->addGroup();
$innerNameless = $nameless->addText('elementName');
echo $innerNamed->getName() . ', ' . $innerComplex->getName()
. ', ' . $innerNameless->getName();
?>
results in the following output
groupName[elementName], groupName[elementOuter][elementInner], elementName
Note also the difference between a nameless element in a named group and vice versa:
<?php
$namedGroup = $form->addGroup('groupName');
$namelessElement = $namedGroup->addText();
$namelessGroup = $form->addGroup();
$namedElement = $namelessGroup->addText('elementName');
echo $namelessElement->getName() . ', ' . $namedElement->getName();
?>
which results in
groupName[], elementName
The only elements which will work reliably if given a name like
foo[]
are checkboxes (assuming they have uniquevalue
attributes). Do not give such names to any other elements, use explicit indexes:foo[0]
,foo[1]
.
Unlike other Container-based elements, Group
implements a working setValue() method. Values for the
Group should be given as an associative array having a structure similar
to what $_GET
/ $_POST
will contain on form submit:
<?php
$foo = $form->addGroup('foo');
$foo->addText('bar');
$foo->addText('baz[quux]');
$foo->setValue(array(
'bar' => 'bar value',
'baz' => array('quux' => 'baz[quux] value')
));
print_r($foo->getValue());
?>
getValue() will return array having the same structure, output of the above code being
Array
(
[bar] => bar value
[baz] => Array
(
[quux] => baz[quux] value
)
)
$data
parameter for group's constructor may
contain the custom 'separator'
key. It is either a string or an array of
strings that will be used to separate elements' HTML in output. setSeparator() / getSeparator() methods
are also available:
<?php
$group = new HTML_QuickForm2_Container_Group(
'foo', null, array('separator' => "<br />\n")
);
$group->addText('first');
$group->addText('second');
$group->addText('third');
echo $group;
echo "\n\n";
$group->setSeparator(array(' ', "<br />\n"));
echo $group;
?>
results in output
<input type="text" name="foo[first]" id="first-0" /><br />
<input type="text" name="foo[second]" id="second-0" /><br />
<input type="text" name="foo[third]" id="third-0" />
<input type="text" name="foo[first]" id="first-0" /> <input type="text" name="foo[second]" id="second-0" /><br />
<input type="text" name="foo[third]" id="third-0" />
The default output for groups is quite simple, containing only elements' HTML and separators. You will need to use render() instead of __toString() to customize the output (the latter uses Default Renderer under the hood, but does not allow output customization). Consult the section on Default Renderer for additional info.
HTML_QuickForm2_Element_Date is a
group of <select></select>
elements used to input dates (and
times). Child selects are created according to 'format'
parameter.
$data
parameter for element's
constructor may contain the following custom keys:
'messageProvider'
'language'
'locale'
will display month / weekday names
according to the current locale.
'format'
'D'
, 'l'
, 'd'
,
'M'
, 'F'
, 'm'
,
'Y'
, 'y'
, 'h'
,
'H'
, 'i'
, 's'
,
'a'
, 'A'
.
'minYear'
'maxYear'
'addEmptyOption'
array('Y' => false, 'd' => true);
'emptyOptionValue'
'emptyOptionText'
array('Y' => 'Choose year', 'd' => 'Choose day');
'optionIncrement'
'i'
and
's'
)
'minHour'
'maxHour'
'minMonth'
'maxMonth'
Child selects of Date element are named according to
'format'
parameter, so Date's value can be set using the standard
approach for groups:
<?php
$date = $form->addElement(
'date', 'testDate', null,
array('format' => 'd-F-Y', 'minYear' => date('Y'), 'maxYear' => 2001)
);
// sets the date to January 1st, 2012
$date->setValue(array('d' => 1, 'F' => 1, 'Y' => 2012));
?>
Additionally Date's setValue() method may accept a string containing a date or a number representing Unix timestamp:
<?php
// also sets the date to January 1st, 2012
$date->setValue('2012-01-01');
// once again sets the date to January 1st, 2012
$date->setValue(1325408400);
?>
String representation of a date is processed by strtotime() function, so consider its limitations.
Hierselect requires
quickform.js
andquickform-hierselect.js
files being included in the page to work.
HTML_QuickForm2_Element_Hierselect is
a group of two or more chained <select></select>
elements.
Selecting a value in the first select changes options in the second and so on.
$data
parameter for element's
constructor may contain the following custom keys:
'options'
'size'
Everything else except 'label'
is passed on to created selects.
Options for select elements are added using HTML_QuickForm2_Element_Hierselect::loadOptions() method:
<?php
$categories = array(
1 => 'HTML',
2 => 'HTTP'
);
// first keys are needed to find related options
$packages = array(
1 => array(
1 => 'HTML_AJAX',
2 => 'HTML_Common2',
3 => 'HTML_QuickForm2'
),
2 => array(
1 => 'HTTP_Download',
2 => 'HTTP_Request2'
)
);
$hierselect->loadOptions(array($categories, $packages));
?>
Unlike old hierselect element from HTML_QuickForm you don't need to provide all possible options. You may instead give server-side and client-side callbacks that will receive values of previous selects and return additional options as needed:
<?php
$hierselect->loadOptions(
array($categories, array()),
array($loader, 'getPackages'), 'loadPackagesSync'
);
?>
hierselect-ajax.php
example file installed with the package shows how to use AJAX requests to load additional options.
Repeat requires
quickform.js
andquickform-repeat.js
files being included in the page to work.
HTML_QuickForm2_Container_Repeat is an element that accepts a 'prototype' Container (a Fieldset or a Group, but not another Repeat) and repeats it several times in the form. Repeated items can be dynamically added / removed via Javascript, server-side part automatically understands these changes. Server-side and client-side validation can be easily leveraged: rules added to prototype Container and its children are repeated as well.
repeat.php
example file installed with the package shows how to use repeat elements with Fieldsets and Groups.
HTML_QuickForm2_Container_Repeat has only one immediate child element: a
prototype Container, either set using setPrototype() method
or passed as 'prototype'
key of $data
parameter to
Repeat's constructor.
appendChild(), insertBefore(), removeChild() are available for Repeat element as usual, but these are proxies for prototype's methods working with its children and will throw exceptions if prototype was not yet set. An exception will also be thrown if you attempt to add another Repeat element to a prototype.
Adding elements to Repeat
<?php
$fieldset = new HTML_QuickForm2_Container_Fieldset();
$repeat = new HTML_QuickForm2_Container_Repeat();
$repeat->setPrototype($fieldset);
echo "repeat: " . count($repeat) . "; fieldset: " . count($fieldset) . "\n";
$repeat->addText('title');
echo "repeat: " . count($repeat) . "; fieldset: " . count($fieldset) . "\n";
?>
the above code outputs
repeat: 1; fieldset: 0
repeat: 1; fieldset: 1
As is the case with groups, repeat element will change names of its children, additionally
id
attributes will be changed. Instead of prepending the repeat's name,
however, a special "index template" '[:idx:]'
will be appended to
the name and _:idx:
to the id
.
':idx:'
string in these will later be replaced by an actual index of the
repeated item to produce unique names / id
s.
Default names for repeated elements
<?php
$form = new HTML_QuickForm2('repeatFieldset');
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'title' => array('foo', 'bar', 'key' => 'baz')
)));
$fieldset = new HTML_QuickForm2_Container_Fieldset();
$repeat = $form->addRepeat()->setPrototype($fieldset);
$repeat->addText('title', array('id' => 'title'));
$renderer = $repeat->render(
HTML_QuickForm2_Renderer::factory('array')
);
$array = $renderer->toArray();
foreach ($array['elements'] as $fsArray) {
echo $fsArray['attributes'] . "\n";
echo " " . $fsArray['elements'][0]['html'] . "\n";
}
?>
outputs
id="qfauto-0_:idx:" class="repeatItem repeatPrototype"
<input type="text" id="title_:idx:" name="title[:idx:]" />
id="qfauto-0_0" class="repeatItem"
<input type="text" id="title_0" name="title[0]" value="foo" />
id="qfauto-0_1" class="repeatItem"
<input type="text" id="title_1" name="title[1]" value="bar" />
id="qfauto-0_key" class="repeatItem"
<input type="text" id="title_key" name="title[key]" value="baz" />
There are a few things worth noting in this output:
'title'
field. Sometimes it is better to explicitly set the field to use
for discovering indexes using setIndexField().
'/^[a-zA-Z0-9_]+$/'
.
repeatPrototype
CSS class and will be used by Javascript
code to add new visible repeated items.
If you are using a named group as a repeat prototype, you may want to use another name structure:
group[index][element]
instead of group[element][index]
. It
is possible to do so if you manually put [:idx:]
into group's name, Repeat will
not mangle names further if this string is already present in them.
Custom names for repeated elements
<?php
$form = new HTML_QuickForm2('repeatGroup');
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'tags' => array(
array('title' => 'foo'),
array('title' => 'bar'),
'key' => array('title' => 'baz')
)
)));
$group = new HTML_QuickForm2_Container_Group('tags[:idx:]');
$repeat = $form->addRepeat()->setPrototype($group);
// custom id as well
$group->addText('title', array('id' => 'tags-:idx:-title'));
$renderer = $repeat->render(
HTML_QuickForm2_Renderer::factory('array')
);
$array = $renderer->toArray();
foreach ($array['elements'] as $groupArray) {
echo $groupArray['elements'][0]['html'] . "\n";
}
?>
outputs
<input type="text" id="tags-:idx:-title" name="tags[:idx:][title]" />
<input type="text" id="tags-0-title" name="tags[0][title]" value="foo" />
<input type="text" id="tags-1-title" name="tags[1][title]" value="bar" />
<input type="text" id="tags-key-title" name="tags[key][title]" value="baz" />
For radios and checkboxes it is also possible to put :idx:
into
value
attribute, this way their names will not be changed, essentially
allowing to use one set of radios or checkboxes for all repeated items.
As was noted in the above example, indexes for repeated items can be automatically discovered from data sources. The field name to use for discovering indexes can be either set explicitly with setIndexField() or left for the Repeat to choose automatically using names of prototype's child elements. When guessing, it will only consider elements that are expected to always have a submit value, so elements like buttons, checkboxes and multiple selects will not be used.
Indexes can be also set manually using setIndexes(), the corresponding getIndexes() is also available. Duplicate indexes and those not matching HTML_QuickForm2_Container_Repeat::INDEX_REGEXP will be ignored by setIndexes().
Working with indexes
<?php
$form = new HTML_QuickForm2('repeatIndexes');
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'title' => array('foo', 'bar', 'key' => 'baz')
)));
$fieldset = new HTML_QuickForm2_Container_Fieldset();
$repeat = $form->addRepeat()->setPrototype($fieldset);
// no element to guess indexes present
var_dump($repeat->getIndexes());
// explicitly set field to discover indexes
$repeat->setIndexField('title');
var_dump($repeat->getIndexes());
// explicit setIndexes() with a few errors
$repeat->setIndexes(array('foo', 'bar', 'baz', 'qu\'ux', 'baz'));
var_dump($repeat->getIndexes());
?>
outputs
array(0) {
}
array(3) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
string(3) "key"
}
array(3) {
[0]=>
string(3) "foo"
[1]=>
string(3) "bar"
[2]=>
string(3) "baz"
}
Setting indexes for repeat elements works quite similar to setting values for other elements, so keeping correct order really helps.
Some of the form elements need to automatically obtain (possibly localized) messages:
'error'
field within $_FILES
array) fails.
Instead of giving all possible messages to each element, an object implementing HTML_QuickForm2_MessageProvider interface (or a callback with a signature similar to HTML_QuickForm2_MessageProvider::get()) is given which returns messages on-demand.
Default message provider will be used by 'date'
and 'file'
elements if another one is not explicitly given. It contains an array of pre-translated messages
and allows overriding them and setting additional translations for new languages. As this message
provider is a Singleton, the updated translations will be available throughout the application.
Adding a new "translation"
<?php
HTML_QuickForm2_MessageProvider_Default::getInstance()->set(
array('date', 'months_long'),
'elderscrolls',
array("Morning Star", "Sun's Dawn", "First Seed", "Rain's Hand",
"Second Seed", "Mid Year", "Sun's Height", "Last Seed",
"Heartfire", "Frostfall", "Sun's Dusk", "Evening Star")
);
$date = HTML_QuickForm2_Factory::createElement(
'date', 'test', array(),
array('format' => 'd F Y', 'language' => 'elderscrolls')
)->setValue(
'2012-04-01'
);
// remove all tags from the output
$date->toggleFrozen(true);
$date->persistentFreeze(false);
echo $date;
?>
the output of the above code being
01 Rain's Hand 2012
If 'language'
field is not explicitly given to element's constructor,
'language'
option set with
<?php
HTML_Common2::setOption('language', '...');
?>
will be used, defaulting to 'en'
.
This message provider will only work for 'date'
elements and relies on strftime() function to generate
lists of months and weekdays. You will need to properly set LC_TIME locale
category for it to work.
Using Strftime message provider
<?php
setlocale(LC_TIME, 'ru_RU.CP1251', 'Russian_Russia.1251');
// Strftime message provider will be used if 'locale' is given as 'language' value
$date = HTML_QuickForm2_Factory::createElement(
'date', 'test', array(), array('format' => 'd F Y', 'language' => 'locale')
)->setValue(
'2012-04-01'
);
// remove all tags from the output
$date->toggleFrozen(true);
$date->persistentFreeze(false);
echo $date;
?>
which outputs (in CP1251 encoding, actually):
01 Апрель 2012
Each element in HTML_QuickForm2 implements getRawValue() and getValue() methods that return its unfiltered and filtered submit value, respectively, and setValue() that sets the element's display value. These are not always the same, consider
<?php
$text = new HTML_QuickForm2_Element_InputText(
'testText', array('disabled' => 'disabled')
);
$text->setValue('a value');
echo $text . "\n";
var_dump($text->getValue());
?>
which produces
<input type="text" disabled="disabled" name="testText" id="testText-0" value="a value" />
NULL
as disabled elements cannot have a submit value.
If an element can not have a submit value (e.g. reset button, static element) or does not currently have one (e.g. disabled element, unchecked checkbox) then getValue() will return NULL. The value also passes "intrinsic validation" which ensures that it could possibly come from that element:
<?php
$select = HTML_QuickForm2_Factory::createElement('select', 'testSelect',
array('multiple' => 'multiple'))
->loadOptions(array('a' => 'letter A', 'b' => 'letter B', 'c' => 'letter C'));
$select->setValue(array('a', 'z'));
// select will only return values for options that were present in it
var_dump($select->getValue());
?>
will output
array(1) {
[0]=>
string(1) "a"
}
On the other hand, some of the elements cannot have a display value (<input
type="image" />
, file uploads), setValue() will be a
no-op for such elements and they will only get their values (click coordinates and file upload
data, respectively) from submit data sources.
getValue() / getRawValue() also work for Containers and return an array with values of contained elements. setValue() is only implemented for groups, data sources should be used to set the values for the whole form.
Instance of HTML_QuickForm2 contains an array of Data sources - objects storing elements' incoming values. These values may either originate from HTTP request data (submit values) or be provided by the programmer (default values). Data sources implement HTML_QuickForm2_DataSource interface defining a single getValue() method, which receives element name and returns either element value or NULL if such value is not present.
There is also a HTML_QuickForm2_DataSource_Submit interface that defines an additional
getUpload() method that receives file upload name and returns either file
upload data from $_FILES
array or NULL if it doesn't contain this data.
A data source containing submit values will be added to the list automatically if constructor of HTML_QuickForm2 considers the form submitted. You can add another data source to the list using HTML_QuickForm2::addDataSource() and completely replace the list using HTML_QuickForm2::setDataSources().
An element will try to update its value from data sources in the following cases:
To perform that update it gets the data sources array from the form and iterates over it calling getValue() until a non-NULL value is returned. This value is then used as element's value.
Some of the elements (submit buttons, obviously file uploads) will only consider getting their values from instances of HTML_QuickForm2_DataSource_Submit. Some (e.g. checkboxes) will stop iterating over the array as soon as an instance of HTML_QuickForm2_DataSource_Submit is found, even if its getValue() method returns NULL. Static elements will only consider a datasource if it is not an instance of HTML_QuickForm2_DataSource_Submit.
The package contains several classes implementing the HTML_QuickForm2_DataSource interface, but the only one that should be used directly is HTML_QuickForm2_DataSource_Array, other classes are used internally by the package.
An instance of HTML_QuickForm2_DataSource_Array wraps around an array
with a structure similar to that of superglobal $_GET
/
$_POST
arrays. This wrapper allows searching for values of elements with
complex names:
<?php
$ds = new HTML_QuickForm2_DataSource_Array(array(
'foo' => 'foo value',
'bar' => array('bar 1', 'bar 2'),
'baz' => array('first' => array('second' => 'found a value'))
));
echo $ds->getValue('foo') . "\n";
echo $ds->getValue('bar[1]') . "\n";
echo $ds->getValue('baz[first][second]');
?>
outputs
foo value
bar 2
found a value
Instead of loading data from a database unconditionally and passing it to this data source in an array, it may make sense to create your own data source implementation, which will only perform a query when asked for a value. This way you will probably save a query on form submit, as all values will be found in a submit data source.
A most popular value-related error happens when a programmer uses an element's setValue() method before adding that element to the form:
<?php
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'testDefault' => 'duh!'
)));
$element = HTML_QuickForm2_Factory::createElement('text', 'testDefault')
->setValue('my carefully prepared value');
$form->appendChild($element);
echo $element->getValue();
?>
As described above, the element will immediately try to update its value from form's data sources and overwrite whatever was set on the previous step, so the above results in
duh!
A less obvious problem is adding data sources after elements:
<?php
// an element updates its value from existing (e.g. submit) data sources
$form->addElement('text', 'testDefault');
// it updates its value again
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'testDefault' => 'my carefully prepared value'
)));
// ...and yet again
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'unrelated' => 'a completely unrelated value'
)));
?>
That code behaves as expected, but performs lots of unneeded work.
To sum it up, a correct order is:
Server-side validation in HTML_QuickForm2 is performed by HTML_QuickForm2::validate() method. Validation rules doing actual checks on element values are implemented as subclasses of HTML_QuickForm2_Rule, they are added to elements via HTML_QuickForm2_Node::addRule().
Basically, the form is invalid if it contains at least one invalid element. The element is considered invalid if it has an error message (accessible by HTML_QuickForm2_Node::getError()) set and valid otherwise. That error can appear in two different ways:
The latter happens in the course of executing HTML_QuickForm2::validate(). It iterates over all form's elements, for each element calling validate() methods of all its rules in the order they were added. As soon as an error is set on an element, its validation stops.
Do not forget to provide an error message to the rule, otherwise the element will be considered valid even if rule's validation routine returns FALSE. Not setting an error message is only useful when chaining (see below).
Some of the elements may perform additional hardcoded validation. For example, file uploads will check the value of
'error'
field in$_FILES
and assign a relevant error message when file upload was attempted but failed.
Instantiating Rule objects directly
<?php
require_once 'HTML/QuickForm2.php';
require_once 'HTML/QuickForm2/Rule/Required.php';
require_once 'HTML/QuickForm2/Rule/Regex.php';
$form = new HTML_QuickForm2('tutorial');
$username = $form->addElement('text', 'username');
$form->addElement('submit', null, array('value' => 'Send!'));
$username->addRule(new HTML_QuickForm2_Rule_Required(
$username, 'Username is required!'
));
$username->addRule(new HTML_QuickForm2_Rule_Regex(
$username, 'Username should contain only letters, digits and underscores', '/^[a-zA-Z0-9_]+$/'
));
if ($form->validate()) {
// process form
}
echo $form;
?>
Of course, you will rarely need to instantiate Rule subclasses directly, Rule objects can be created by HTML_QuickForm2_Node::createRule() or automatically by addRule() if first parameter is a string representing registered rule type.
Automatic creation of Rule objects
<?php
require_once 'HTML/QuickForm2.php';
$form = new HTML_QuickForm2('tutorial');
$username = $form->addElement('text', 'username');
$form->addElement('submit', null, array('value' => 'Send!'));
$username->addRule('required', 'Username is required!');
$username->addRule('regex', 'Username should contain only letters, digits and underscores',
'/^[a-zA-Z0-9_]+$/');
if ($form->validate()) {
// process form
}
echo $form;
?>
Most of the built-in rules are designed to check scalar values and will not work properly if
added to a Container (this includes Groups and Group-based elements like Date and Hierselect), as
Containers return their values in an associative array. One notable exception is
nonempty
/ required
rule that can validate a container (or
<select multiple="multiple" />
):
Checks that at least two checkboxes in a group are selected
<?php
$boxGroup = $form->addElement('group', 'boxes')->setLabel('Check at least two:');
$boxGroup->addElement('checkbox', null, array('value' => 'first'))->setContent('First');
$boxGroup->addElement('checkbox', null, array('value' => 'second'))->setContent('Second');
$boxGroup->addElement('checkbox', null, array('value' => 'third'))->setContent('Third');
$boxGroup->addRule('required', 'Check at least two boxes', 2);
?>
It is of course possible to implement a custom rule that will properly handle an associative
array as the element's value. It is also possible to leverage existing "scalar" rules
to validate Containers by using each
rule, it applies a template rule to all the
elements in a Container and considers Container valid if its validation routine returns TRUE
for all of them:
Checks that all phone fields in a group contain numeric data
<?php
$phones = $form->addElement('group', 'phones')->setLabel('Phones (numeric):')
->setSeparator('<br />');
$phones->addElement('text', '0');
$phones->addElement('text', '1');
$phones->addRule('each', 'Phones should be numeric',
$phones->createRule('regex', '', '/^\\d+([ -]\\d+)*$/'));
?>
More specific rules are run first: rules added to container will be checked after rules added to its contained elements.
HTML_QuickForm2 allows validation of elements based on values and validation status of other elements. This is done by building a "chain" of validation rules using HTML_QuickForm2_Rule::and_() and HTML_QuickForm2_Rule::or_() methods. Execution of the chain starts with a rule that was added to an element, then results of other rules' validation routines are combined using corresponding logical operators. Error is only set on the element if the whole chain returned FALSE.
Behaviour of and_() and or_() is similar to PHP's
and
and or
operators:
Rules that are added to the chain behave the same way as the rules that are added directly to the
element they validate (this is not necessarily the same element the chain is added to), they will
set an error if the rule itself returns FALSE, not the chain. Thus it is often needed
not to provide error messages to the rules. It may also make sense to add a
chain of rules to a chain (this is similar to adding parentheses to a PHP expression with
and
and or
).
Skips checking email field if "receive email" box is not checked
<?php
$emailPresent = $email->createRule('nonempty', 'Supply a valid email if you want to receive our spam');
// note lack of error message here, error should only be set by previous rule
$emailValid = $email->createRule('callback', '', array('callback' => 'filter_var',
'arguments' => array(FILTER_VALIDATE_EMAIL)));
// note lack of error message for 'empty' rule, we don't want error on a checkbox
$spamCheck->addRule('empty')
->or_($emailPresent->and_($emailValid));
?>
Checks password fields in password change form
<?php
$newPassword->addRule('empty')
->and_($repPassword->createRule('empty'))
->or_($newPassword->createRule('minlength', 'The password is too short', 6))
->and_($repPassword->createRule('eq', 'The passwords do not match', $newPassword))
->and_($oldPassword->createRule('nonempty', 'Supply old password if you want to change it'));
?>
Validation library
Client-side validation depends on a JS library residing in
quickform.js
file. Neither a link to that file nor its contents is automatically included in the output generated by a renderer, the next section describes how you can properly handle including it.
You can tell a rule to also generate Javascript necessary for client-side validation. This is
done by passing a $runAt
parameter with
HTML_QuickForm2_Rule::CLIENT flag set to addRule():
<?php
// if first parameter to addRule() is a string:
$username->addRule('required', 'Username is required', null,
HTML_QuickForm2_Rule::SERVER | HTML_QuickForm2_Rule::CLIENT);
// if first parameter to addRule() is a Rule instance:
$username->addRule($username->createRule('required', 'Username is required'),
HTML_QuickForm2_Rule::CLIENT_SERVER); // using a shorthand for above constants
?>
If more rules were chained to the added one with and_() and or_(), Javascript will be generated for the whole chain.
Since release 0.6.0 it is possible to run client-side rules for an element on changing its value
or on it losing input focus ('onchange'
and 'onblur'
events) in addition to form submit ('onsubmit'
event). This is triggered by
passing a $runAt
parameter with
HTML_QuickForm2_Rule::ONBLUR_CLIENT flag set to
addRule(). If a rule has chained rules, then validation will be triggered by
all elements appearing in a chain.
<?php
// here validation will be run onchange / onblur of both $newPassword and $repPassword fields
$newPassword->addRule('empty', '', null, HTML_QuickForm2_Rule::ONBLUR_CLIENT_SERVER)
->and_($repPassword->createRule('empty'))
->or_($repPassword->createRule('eq', 'The passwords do not match', $newPassword));
?>
Another change introduced in 0.6.0 is that validation errors are now output near the elements instead of being shown in Javascript alert(). In a nuthshell, client-side validation behaviour in HTML_QuickForm2 0.6.0+ is more similar to that of HTML_QuickForm_DHTMLRulesTableless than to that of old HTML_QuickForm.
Most of the built-in rules are able to run client-side, the only exceptions are
maxfilesize
and mimetype
rules specific for file uploads.
If you want to run callback
rule client-side, you will obviously need to
implement a callback in Javascript as well as in PHP. If you don't explicitly set
'js_callback'
configuration parameter, callback
rule
will try to run Javascript function having the same name as provided PHP
'callback'
. This may be especially useful if you use
HTML_AJAX to proxy PHP classes or callbacks in
Javascript.
When running regex
rules client-side, you should stick to regular expression
syntax common in PHP and Javascript:
/
as a delimiter.
i
, m
, u
pattern modifiers. If
u
modifier is used, PHP's Unicode escapes
\x{NNNN} are automatically converted to Javascript's Unicode escapes
\uNNNN when creating a client-side rule.
While it is possible to add a client-side only rule
<?php
$username->addRule('minlength', 'Username should be at least 4 characters long', 4,
HTML_QuickForm2_Rule::CLIENT);
?>it is not recommended unless you perform the same validation server-side using some other rule.
For your convenience, all rules included in the package are already registered with HTML_QuickForm2_Factory and can be easily created with createRule() / addRule(). Some of the rule classes are registered under several names with different configuration data to save keystrokes and improve readability:
<?php
// these calls are identical
$username->addRule('minlength', 'Username should be at least 4 characters long', 4);
$username->addRule('length', 'Username should be at least 4 characters long', array('min' => 4));
// as are these
$start->addRule('lt', 'Start should be less than finish', $finish);
$start->addRule('compare', 'Start should be less than finish',
array('operator' => '<', 'operand' => $finish));
?>
Rule name | Class name | Description | Configuration |
---|---|---|---|
nonempty |
HTML_QuickForm2_Rule_Nonempty | Checks that the field is not empty | Minimum number of nonempty values for Containers / arrays, integer |
empty |
HTML_QuickForm2_Rule_Empty | Checks that the field is empty | |
required |
HTML_QuickForm2_Rule_Required | Like nonempty , but the field is marked as required in output |
Like nonempty |
compare |
HTML_QuickForm2_Rule_Compare | Compares the value of the field with some other value using the given operator |
Either of the following:
'===' . Operand can either be a
literal value or another form element.
|
eq |
As compare rule with hardcoded '===' operator |
Operand. The values are compared as strings. | |
neq |
As compare rule with hardcoded '!==' operator |
||
lt |
As compare rule with hardcoded '<' operator |
Operand. The values are compared as numbers. | |
lte |
As compare rule with hardcoded '<=' operator |
||
gt |
As compare rule with hardcoded '>' operator |
||
gte |
As compare rule with hardcoded '>=' operator |
||
regex |
HTML_QuickForm2_Rule_Regex | Checks that the field value matches the given regular expression. | Regular expression, string. Use slashes for delimiters if you intend to do client-side validation. |
email |
HTML_QuickForm2_Rule_Email | Checks that the field value is a valid email address. Currently the most common address format is recognised, support for additional RFC 5321 features and DNS checks is planned. | |
callback |
HTML_QuickForm2_Rule_Callback | Checks the value using a provided callback function (method). It is expected to return TRUE if the element is valid | Either of
|
length |
HTML_QuickForm2_Rule_Length | Checks that the value's length is within the given limits |
Either of
|
minlength |
Checks that the value's length is at least the given number of characters | Minimal length, integer | |
maxlength |
Checks that the value's length is at most the given number of characters | Maximal length, integer | |
notcallback |
HTML_QuickForm2_Rule_NotCallback | Checks the value using a provided callback function (method). It is expected to return FALSE if the element is valid. | Like callback |
notregex |
HTML_QuickForm2_Rule_NotRegex | Checks that the field value does not match the given regular expression. | Like regex |
Rules specific for file uploads | |||
maxfilesize |
HTML_QuickForm2_Rule_MaxFileSize | Checks that uploaded file size does not exceed the given limit | Maximum allowed file size, integer |
mimetype |
HTML_QuickForm2_Rule_MimeType | Checks that uploaded file is of the correct MIME type | Allowed MIME type or an array of types |
Rules specific for containers | |||
each |
HTML_QuickForm2_Rule_Each | Validates all elements in a Container using a template Rule | Template Rule, instance of HTML_QuickForm2_Rule |
Usage of builtin rules is covered in
builtin-rules.php
example installed with the package.
New rule types are registered by HTML_QuickForm2_Factory::registerRule() which accepts rule type name, corresponding class name and optionally file name containing that class and default configuration data for all rules of the given type.
<?php
// registers a callback rule with a specific callback
HTML_QuickForm2_Factory::registerRule(
'inarray', 'HTML_QuickForm2_Rule_Callback', null,
array('callback' => 'in_array')
);
$field->addRule('inarray', 'wrong variable name!', array('foo', 'bar', 'baz'));
?>
Javascript code has API documentation comments that can be extracted with JsDoc toolkit. SVN checkout of HTML_QuickForm2 contains Phing build file for this.
All Javascript code defined in HTML_QuickForm2 lives in qf
"namespace". Deeper namespaces are used to group related functionality.
qf.Map
is a class for Hash Map data structure. It is used internally to store
validation errors and Container values.
var map = new qf.Map(); map.set('a key', 'a value'); alert(map.get('a key')); // a value alert(map.hasKey('another key') ? 'true' : 'false'); // false alert(map.length()); // 1 var map2 = new qf.Map({foo: 'foo value', bar: 'bar value'}); map.merge(map2); map.remove('foo'); alert(map.getKeys().join(', ')); // a key, bar alert(map.getValues().join(', ')); // a value, bar value map.clear(); alert(map.isEmpty() ? 'true' : 'false'); // true
qf.form
namespace contains methods for getting and setting elements' values:
qf.form.getValue(), qf.form.getSubmitValue() (shorthand
qf.$v()), qf.form.getContainerSubmitValue() (shorthand
qf.$cv()), qf.form.setValue().
Methods for handling CSS classes on elements live in qf.classes
namespace:
qf.classes.add(), qf.classes.remove(),
qf.classes.has().
qf.events
namespace contains helper methods for crossbrowser events support:
qf.events.addListener(), qf.events.removeListener(),
qf.events.fixEvent(). The latter is expected to be used in event handlers in
the following way
qf.Validator.submitHandler = function(event) { event = qf.events.fixEvent(event); // ... };
HTML_QuickForm2 does not contain a full-blown crossbrowser event library because it does not need one, the above are simple convenience methods. There are numerous well-known JS frameworks with good crossbrowser events support, use one if needed.
qf.rules
and qf.elements
namespaces are intended for rule
implementations and element support methods, respectively. For example, Hierselect element puts
its code into qf.elements.hierselect
.
Client-side validation is performed by an instance of qf.Validator
. Its
constructor accepts DOM object of a form it needs to validate, sets up
necessary event handlers on that object and adds itself as a validator
property of it.
It is easiest to disable client-side validation for a form by clearing
validator
property from form's DOM object:document.getElementById(formId).validator = null;
qf.Validator
exposes several methods which can be overriden to change the way
validation results are presented to user:
this.errors
property, which is an instance of qf.Map
.
These can be overriden either for a particular instance of qf.Validator
or for
all its instances if qf.Validator.prototype
is changed.
For example, if you want to display a list of validation errors in an alert() (as was done in HTML_QuickForm and pre-0.6.0 HTML_QuickForm2) you can do the following:
var form = document.getElementById(formId); form.validator.onFormError = function() { alert('Invalid information entered:\n - ' + this.errors.getValues().join('\n - ') + '\nPlease correct these fields.'); }; // don't set errors on elements form.validator.onFieldError = function() {}; form.validator.onFieldValid = function() {};
If you want to disable submit button to prevent duplicate form submits:
document.getElementById(formId).validator.onFormValid = function() { document.getElementById(submitId).disabled = true; };
Renderers in HTML_QuickForm2 are classes that output the form. They either generate the resultant HTML themselves or represent the form in some intermediate format that can later be used by e.g. a template engine.
Renderers also contain a Javascript builder object that aggregates code needed for client-side validation and Javascript-backed elements. This means that rendering step is mandatory if you are using any of these.
A new feature in HTML_QuickForm2 compared to HTML_QuickForm is the ability to extend Renderer functionality by plugins.
Typical form output workflow:
<?php
require_once 'HTML/QuickForm2/Renderer.php';
$renderer = HTML_QuickForm2_Renderer::factory('custom');
// common options
$renderer->setOption(array(
'group_errors' => true,
'required_note' => 'Fields labeled in 36pt bold red font are required'
));
// renderer-specific setup
$renderer->doSomeOutputCustomization();
// ...
$renderer->doAnotherOutputCustomization();
// process the form
$form->render($renderer);
// Output the links to JS libraries, if used
foreach ($renderer->getJavascriptBuilder()->getLibraries() as $link) {
echo $link . "\n";
}
// Assuming the renderer implements __toString()
echo $renderer;
?>
The following renderers are installed with the package:
setOption() method is used to set the values of configuration parameters and getOption() method to read them. Passing an unknown parameter name to these methods will result in an Exception. The following parameters are defined in the base class and known to all Renderers:
Parameter name | Description | Expected type | Default value |
---|---|---|---|
group_hiddens |
Whether to group hidden elements together or render them where they were added. | boolean | TRUE |
group_errors |
Whether to group error messages or render them alongside elements they apply to. | boolean | FALSE |
errors_prefix |
Leading message for grouped errors. | string | 'Invalid information entered:' |
errors_suffix |
Trailing message for grouped errors. | string | 'Please correct these fields.' |
required_note |
Note displayed if the form contains required elements. | string | '<em>*</em> denotes required fields.' |
setJavascriptBuilder() sets a Javascript builder object used during form rendering and getJavascriptBuilder() returns that object (if an object was not set previosly a new instance is created). Package users will mostly need to interact with this object to properly include Javascript library files.
reset() method clears all accumulated data. It is called automatically when rendering a HTML_QuickForm2 object, but must be called manually when rendering form elements separately.
It is only possible to instantiate built-in Renderers through HTML_QuickForm2_Renderer::factory() as their __construct() methods are declared protected. Moreover, factory() returns an instance of HTML_QuickForm2_Renderer_Proxy wrapping around a specific Renderer instance. This setup is needed for plugin support and serves as a workaround for PHP shortcomings:
Proxy and factory() are used to aggregate methods from several classes and to limit access to them from outside: all fields and methods of a Renderer instance are public, but the only path to that instance is through a Proxy that only allows access to explicitly "published" methods and methods defined in base class. Plugins, however, have direct access to a Renderer instance and thus can freely use its API.
It is definitely possible to create a subclass of a built-in Renderer with a public __construct() method and do not use factory(). This will, however, prevent plugins from working.
Plugins are the means to enhance Renderer functionality at runtime. From user's point of view the Renderer object simply gets a new method:
<?php
require_once 'HTML/QuickForm2/Renderer.php';
HTML_QuickForm2_Renderer::registerPlugin('foo', 'Foo_PluginBar');
$foo = HTML_QuickForm2_Renderer::factory('foo');
$foo->doBar();
// This will also work
HTML_QuickForm2_Renderer::registerPlugin('foo', 'Foo_PluginMore');
$foo->performMore();
?>
The main puprose of plugins is to allow custom elements which require some complex output behaviour to define that behaviour in separate classes leveraging existing Renderer code. Previously it was usually implemented in the element class itself, leading to unnecessary bloat and code duplication.
dualselect.php
example installed with the package shows, among other things, how to create a renderer plugin for a custom element.
HTML_QuickForm2_Renderer_Default is essentially a primitive template engine (only supporting blocks and variable placeholders) that is intended for quick prototyping. This Renderer is used under the hood by a magic __toString() method of HTML_QuickForm2_Container (and consequently HTML_QuickForm2 itself). It is similar to HTML_QuickForm_Renderer_Default of HTML_QuickForm though has different template format and more customization possibilities.
Generated HTML is returned by a magic __toString() method, so the renderer can be used in string contexts.
The elements are output in the order they were added using an appropriate template, which looks similar to the following:
<div class="row"> <p class="label"> <qf:required><span class="required">*</span></qf:required> <qf:label><label for="{id}">{label}</label></qf:label> </p> <div class="element<qf:error> error</qf:error>"> <qf:error><span class="error">{error}<br /></span></qf:error> {element} </div> </div>
Here {foo}
are placeholders that will be replaced by actual element data and
<qf:bar>...</qf:bar>
are blocks that will be either removed if an
element does not have a bar
property or will be retained without their
<qf:bar>
delimiters if said property is present.
The following placeholders are recognized and will be replaced:
{id}
'id'
attribute of an element.
{class}
'class'
attribute of an element. Mostly needed to put repeat-specific
repeatItem
and repeatPrototype
CSS classes on a outer
<div>
for a group.
{attributes}
{error}
{label}
{label_more}
'more'
key.
{element}
{content}
{hidden}
group_hiddens
is on. Only in template for
form.
{errors}
group_errors
is on. Only in template for form.
{reqnote}
{message}
The following blocks are possible:
<qf:required>
<qf:error>, <qf:label>, <qf:label_more>, <qf:reqnote>, <qf:message>
<qf:error>
is also used to set a special CSS class
for an invalid element.
Form element templates are set by setTemplateForId(),
setTemplateForClass(),
setElementTemplateForGroupId()
and setElementTemplateForGroupClass().
The latter two methods apply to elements that are inside Groups. Note that the word
"class" in these method names refers to PHP class name rather than to CSS class name.
The renderer will also try going up the class hierarchy, so it will use a template for
HTML_QuickForm2_Element to render <input type="text"
/>
if more specific templates for
HTML_QuickForm2_Element_InputText and
HTML_QuickForm2_Element_Input are not available.
Another template-related method is setErrorTemplate()
which sets templates for errors output when group_errors
is TRUE.
When searching for an appropriate template for a given element, the following order is used:
When rendering elements inside Container, two templates are actually used:
{content}
placeholder;
Elements are first wrapped in their own templates, then {content}
placeholder
in Container template is replaced by HTML for all contained elements.
This is true for Groups as well, but is less obvious:
'{element}'
. So you will only get elements' HTML, maybe with separators in
between, no additional markup.The above also means that you will see neither labels for grouped elements, nor their validation errors by default. If you want to output these, you'll need to set a new template for grouped elements using either of setTemplateForId(), setElementTemplateForGroupId(), setElementTemplateForGroupClass():
<?php
// setElementTemplateForGroupId() may be used if you want to customize a specific group
$renderer->setElementTemplateForGroupClass(
'HTML_QuickForm2_Container_Group', 'HTML_QuickForm2_Element',
'a complex template with {element}, {label} and {error} placeholders'
);
?>
controller/wizard.php
example installed with the package shows setting a complex template for grouped elements.
HTML_QuickForm2_Renderer_Array does not generate HTML itself but rather converts the form structure to an array, which can later be used for generating the actual output. It is a port of HTML_QuickForm_Renderer_Array from HTML_QuickForm though generated arrays have some differences due to differences in form structure.
Array Renderer defines a new configuration parameter for setOption() / getOption():
Parameter name | Description | Expected type | Default value |
---|---|---|---|
static_labels |
Applies only to elements having several labels. If FALSE, label key of
the element's array will contain an array of labels as returned by getLabel(). If TRUE element's
array will contain several label_* keys corresponding to the keys in
label array.
|
boolean | FALSE |
HTML_QuickForm2_Renderer_Array::toArray() returns the resultant array and HTML_QuickForm2_Renderer_Array::setStyleForId() adds some (opaque) style information to the element's array that can later be used by a template engine.
renderers/array-twig.php
example installed with the package shows how to use Array Renderer together with Twig template engine.
array(
'id' => form's "id" attribute (string),
'frozen' => whether the form is frozen (bool),
'attributes' => attributes for <form> tag (string),
// if form contains required elements:
'required_note' => note about the required elements (string),
// if 'group_hiddens' option is true:
'hidden' => array with html of hidden elements (array),
// if form has some javascript for setup or validation:
'javascript' => form javascript (string)
// if 'group_errors' option is true:
'errors' => array(
'1st element id' => 'Error for the 1st element',
...
'nth element id' => 'Error for the nth element'
),
'elements' => array(
element_1,
...
element_N
)
);
where members of the 'elements'
array have the following structure
array(
'id' => element id (string),
'type' => type of the element (string),
'frozen' => whether element is frozen (bool),
// if element has a label:
'label' => 'label for the element',
// note that if 'static_labels' option is true and element's label is an
// array then there will be several 'label_*' keys corresponding to
// labels' array keys
'required' => whether element is required (bool),
// if a validation error is present and 'group_errors' option is false:
'error' => error associated with the element (string),
// if some style was associated with an element:
'style' => some information about element style (e.g. for Smarty),
// if element is not a Container
'value' => element value (mixed),
'html' => HTML for the element (string),
// if element is a Container
'attributes' => container attributes (string)
// only for groups, if separator is set:
'separator' => separator for group elements (array),
'elements' => array(
element_1,
...
element_N
)
);
You may often want to output form elements manually, especially if the form has a complex layout:
<complex html><?= $form->getElementById('someElement'); ?><more complex html>
or, if using a template engine
<complex html>{{ form.getElementById('someElement') }}<more complex html>
However, you will require a rendering step if your form uses client-side validation or contains Javascript-backed elements like Hierselect. Stub renderer can be used in such circumstances to reduce overhead as the results of a more complex renderer like Default will be discarded.
Stub renderer serves as a container for JavascriptBuilder object and does some minimal form
processing: it can group errors and hidden elements if group_errors
and
group_hiddens
parameters are set to true. The accumulated data is available
through getErrors() and
getHidden() methods,
respectively. You can also use hasRequired() method to
check whether form contains required elements.
Using Stub Renderer
<?php
$renderer = $form->render(
HTML_QuickForm2_Renderer::factory('stub')
->setOption('group_errors', true);
);
// outputting JS libraries
foreach ($renderer->getJavascriptBuilder()->getLibraries() as $link) {
echo $link . "\n";
}
// outputting form errors
foreach ($renderer->getErrors() as $id => $error) {
echo "<a href=\"#{$id}\">{$error}</a><br />";
}
// ...
// form output code goes here
// ...
echo $renderer->getJavascriptBuilder()->getFormJavascript();
?>
If client-side validation in QuickForm2 doesn't work, you probably did not include the file it depends upon.
While HTML_QuickForm implemented client-side validation and provided several Javascript-backed elements, support for Javascript there was quite limited. Basically, all scripts were inlined and there were some rudimentary checks to prevent outputting the same (library) code twice.
This was addressed in HTML_QuickForm2, Javascript handled by the package is logically split into several parts:
A page containing two Javascript-backed forms will look like this:
...
[JS libraries used by form1 and form2]
...
[form1 html, including inline javascript]
[form1 setup code]
...
[form2 html, including inline javascript]
[form2 setup code]
...
An instance of HTML_QuickForm2_JavascriptBuilder takes care of libraries and setup code, inline code can be added using HTML_QuickForm2_Element_Script element.
The new approach allows keeping the code that does not change (libraries) in external
*.js
files, instead of bloating the resultant HTML. If form's structure does
not change either, generated form setup code may also be moved to an external file. Only code
that changes from one request to the other (e.g. setting the element's value) has to be inlined.
HTML_QuickForm2_JavascriptBuilder is used together with renderers. Form Javascript is generated in the course of HTML_QuickForm2::render() call, so that call is mandatory if you are using Javascript in your form.
Javascript library files are registered via addLibrary(). You will
only need to call this directly if you create custom Elements or Rules or maybe want to override validation
behaviour. Correct $webPath
and
$absPath
need to be provided so that later call to getLibraries() can
generate both links to external files and inline code.
Form setup code usually contains client-side validation rules added with addRule() and element
setup code added with addElementJavascript(),
the latter dealing with event handlers necessary for element behaviour. For example, built-in
Hierselect element adds
onchange
handlers for contained selects, onreset
handler
for a containing form and onload
handler for window
. Once
again, you will only directly call addElementJavascript() if you are creating
a custom element or want to add some custom setup code. You don't need to call
addRule() at all, as it is done automatically if you added a Rule to an
Element with HTML_QuickForm2_Rule::CLIENT flag set. Javascript added by
these two methods is later returned by getFormJavascript()
method which is usually called automatically by a renderer. It is also possible to use separate
getSetupCode() and
getValidator()
methods.
HTML_QuickForm2_JavascriptBuilder also contains a static helper method
encode() which encodes a
PHP value as a Javascript literal. This is similar to json_encode(), but does not
enforce UTF-8
charset.
There are currently three library files installed with the package:
quickform.js
quickform-hierselect.js
quickform-repeat.js
Human-readable versions of these files are installed into HTML_QuickForm2/js
directory under PEAR's
data_dir
and minified versions are installed into HTML_QuickForm2/js/min
. If you have trouble finding where
data_dir
is, you can use config-show command of PEAR installer.
While form setup code and inline Javascript is automatically added to output when rendering a form, libraries are not added automatically. The main reasons for this are
<head></head>
section of HTML document, while renderer outputs only the form itself.
<script
src="..."></script>
tags referencing installed
*.js
files. Inlining the code works for package examples but leads to
useless bloat in most other circumstances.
Libraries should be added to a page before outputting forms and definitely before outputting form setup code. Both inline scripts and form setup code can contain calls to library functions and changes to library properties.
It is possible to just copy *.js
files from PEAR's
data_dir
to a directory accessible via HTTP and manually add necessary
<script src="..."></script>
tags to the page. You will
however need to manually update this list if you decide to use a new Element or a new Rule which
requires an additional Javascript file. A better approach is to let
HTML_QuickForm2_JavascriptBuilder keep track of the used libraries and
rely on its getLibraries() method to include them.
There are two ways to include the library code in your page using HTML_QuickForm2_JavascriptBuilder::getLibraries():
Inline the libraries, including their contents into the page
<?php
require_once 'HTML/QuickForm2/Renderer.php';
$renderer = HTML_QuickForm2_Renderer::factory('default');
$form->render($renderer);
echo $renderer->getJavascriptBuilder()->getLibraries(true, true);
echo $renderer;
?>
This is the approach taken by package example files as it is guaranteed to work with no
additional steps required from the user. It may also be useful while developing new
package-related *.js
files as you won't have problems with browser caching
them.
Copy/symlink *.js
files installed with the package to some directory under
your website's document root and provide this information to JavascriptBuilder:
<?php
require_once 'HTML/QuickForm2/Renderer.php';
require_once 'HTML/QuickForm2/JavascriptBuilder.php';
$renderer = HTML_QuickForm2_Renderer::factory('default');
// Here '/path/to/libraries' is whatever directory available via HTTP you copied libraries to
$renderer->setJavascriptBuilder(new HTML_QuickForm2_JavascriptBuilder('/path/to/libraries'));
$form->render($renderer);
// This will output necessary <script src="/path/to/libraries/..."></script> tags
foreach ($renderer->getJavascriptBuilder()->getLibraries() as $link) {
echo $link . "\n";
}
echo $renderer;
?>
The latter approach is obviously recommended for production use.
If your page contains several forms, you'll only need to have one set of libraries for them. Use the same instance of HTML_QuickForm2_JavascriptBuilder when rendering all forms.
dualselect.php
example shows among other things how to create a custom element with an additional JS library.
Form setup code is returned by HTML_QuickForm2_JavascriptBuilder::getFormJavascript(). This method is automatically called by built-in Renderers and thus the code is included in their output.
Note that HTML_QuickForm2's Javascript library does not use
DOMContentLoaded
event like jQuery and similar libraries
do:
$(document).ready(function() { // some code that will be run when complete DOM tree is built });
Instead, form setup code is run immediately and should be output after the form, when form's DOM tree is already available.
If you are already familiar with MVC frameworks, then you probably know: controller is a
component that accepts user input and instructs other components to perform actions based on that
input. HTML_QuickForm2_Controller is somewhat smaller in scope than a typical
framework controller since it only deals with forms. When using the Controller, an action name is
sent in GET
or POST
data, usually by clicking a specially
named button. This name contains id
of one of the form pages added to
Controller and name of action handler to call (e.g. 'next'
for going forward
in a wizard), that data is extracted and an appropriate action handler is called.
Key features:
Key classes and interfaces:
HTML_QuickForm2_Controller is a rewrite of PHP4 HTML_QuickForm_Controller, so if you are already using the latter you can go to migration guide which contains step-by-step instructions for porting your scripts to new package.
Session initialization
This simple example does not use sessions since there is no need to pass data between pages. You'll need to use sessions when dealing with a real multipage form, though. HTML_QuickForm2_Controller does not start a session automatically, you should explicitly call session_start() before instantiating the controller class.
More complex usage examples are installed with the package. Along with a form similar to the one below they include two multipage forms: a tabbed form and a wizard.
The same example form that was used in the package's tutorial will now be rewritten using the Controller:
Builds and processes a form with a single input field, using Controller
<?php
// Load the main class
require_once 'HTML/QuickForm2.php';
// Load the controller
require_once 'HTML/QuickForm2/Controller.php';
// Load the Action interface (we will implement it)
require_once 'HTML/QuickForm2/Controller/Action.php';
// Class representing a form page
class TutorialPage extends HTML_QuickForm2_Controller_Page
{
protected function populateForm()
{
// Add some elements to the form
$fieldset = $this->form->addElement('fieldset')->setLabel('QuickForm2_Controller tutorial example');
$name = $fieldset->addElement('text', 'name', array('size' => 50, 'maxlength' => 255))
->setLabel('Enter your name:');
// We set the name of the submit button so that it binds to default 'submit' handler
$fieldset->addElement('submit', $this->getButtonName('submit'),
array('value' => 'Send!'));
// The action to call if a user presses Enter rather than clicks on a button
$this->setDefaultAction('submit');
// Define filters and validation rules
$name->addFilter('trim');
$name->addRule('required', 'Please enter your name');
}
}
// Action to process the form after successful validation
class TutorialProcess implements HTML_QuickForm2_Controller_Action
{
public function perform(HTML_QuickForm2_Controller_Page $page, $name)
{
$values = $page->getController()->getValue();
echo '<h1>Hello, ' . htmlspecialchars($values['name']) . '!</h1>';
}
}
$page = new TutorialPage(new HTML_QuickForm2('tutorial'));
// We only add the custom 'process' handler, Controller will care for default ones
$page->addHandler('process', new TutorialProcess());
$controller = new HTML_QuickForm2_Controller('tutorial');
// Set defaults for the form elements
$controller->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'name' => 'Joe User'
)));
$controller->addPage($page);
// Process the request
$controller->run();
?>
You may note that the above code is more verbose than the original. While that is definitely
true, to make a three page wizard you'll only need to create three subclasses of HTML_QuickForm2_Controller_Page, a
'process'
handler and add them to Controller, which already has default
handlers for typical 'Back'
and 'Next'
buttons. It will
require a non-trivial amount of programming without the Controller infrastructure.
To define pages for your controller you need to subclass HTML_QuickForm2_Controller_Page implementing its abstract populateForm() method. Note that this method will be called on demand (i.e. only when the user sees the form in question), so it is better performance-wise to keep all form-building code in it than to pass a pre-populated instance of HTML_QuickForm2 to page's constructor.
Most of the code in the method just repeats what was done in the original tutorial, however some of it requires explanation:
<?php
// We set the name of the submit button so that it binds to default 'submit' handler
$fieldset->addElement('submit', $this->getButtonName('submit'),
array('value' => 'Send!'));
// The action to call if a user presses Enter rather than clicks on a button
$this->setDefaultAction('submit');
?>
The first line uses getButtonName() to
set a special name for form's submit button and thus trigger a 'submit'
action on a 'tutorial'
form when that button is clicked (default handler for
such action is implemented in HTML_QuickForm2_Controller_Action_Submit). The second one sets an action to
trigger when no submit button is clicked (e.g. user presses Enter
instead of
clicking). Under the hood this is done by adding an instance of HTML_QuickForm2_Controller_DefaultAction to the form.
You'll usually need custom handlers for only two actions:
'process'
'process'
handler is, obviously, completely application-specific and will
usually deal with storing the form values somewhere. When creating a custom
'display'
handler it is easiest to subclass HTML_QuickForm2_Controller_Action_Display and override its renderForm()
method to use a Renderer or do any other tweaks you like.
First we instantiate the custom Page class and add a custom action handler to it
<?php
$page = new TutorialPage(new HTML_QuickForm2('tutorial'));
// We only add the custom 'process' handler, Controller will care for default ones
$page->addHandler('process', new TutorialProcess());
?>
We don't bother with other action handlers as Controller will automatically load and use them.
Then we instantiate the controller
<?php
$controller = new HTML_QuickForm2_Controller('tutorial');
?>
Note that Controller needs an id
and it should be unique if you have several
Controllers in application, as it is used for storing values in session.
Then we add a DataSource and the page to the Controller
<?php
// Set defaults for the form elements
$controller->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'name' => 'Joe User'
)));
$controller->addPage($page);
?>
While it is possible to add DataSource to an instance of HTML_QuickForm2 within Page instead, the above code allows setting the default values for the whole (possibly multipage) form. Note also that Controller's DataSources, unlike form's, are stored in session.
Finally we call the Controller's run() method
<?php
$controller->run();
?>
which takes care of finding the name of the current action and calling the necessary handler.
This guide is intended for current users of HTML_QuickForm_Controller who want to update their scripts to HTML_QuickForm2, which now includes a rewrite of older controller package. It covers major API changes and provides links to further documentation.
It should be noted that API of HTML_QuickForm2_Controller is more similar to API of HTML_QuickForm_Controller than that of HTML_QuickForm2 and HTML_QuickForm. That being said, there are some important differences in method names and behaviour.
Of the methods you are most likely to use in your applications, former HTML_QuickForm_Controller::addAction() is now HTML_QuickForm2_Controller::addHandler() and HTML_QuickForm_Controller::exportValues() is now HTML_QuickForm2_Controller::getValue().
As is the case with HTML_QuickForm2 itself, HTML_QuickForm2_Controller no longer has setDefaults() and setConstants() methods. So instead of former call to HTML_QuickForm_Controller::setDefaults()
<?php
$controller->setDefaults(array(
'foo' => 'default foo value',
'bar' => 'default bar value'
));
?>
you should use HTML_QuickForm2_Controller::addDataSource():
<?php
$controller->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'foo' => 'default foo value',
'bar' => 'default bar value'
)));
?>
Like with older defaults and constants, Controller DataSources are stored in session. Note that Controller itself does not have a method for replacing the DataSource array similar to HTML_QuickForm2::setDataSources(), use HTML_QuickForm2_Controller_SessionContainer::storeDatasources().
Instead of the former HTML_QuickForm_Controller::container() method that both returned a reference to a session variable storing Controller data and cleared this variable if requested, there is now getSessionContainer() method that returns an instance of HTML_QuickForm2_Controller_SessionContainer wrapping around session variable and destroySessionContainer() method that clears the session variable.
You no longer need to directly access the session variable to store some custom values:
<?php
// Note the references
// on source page:
$data =& $controller->container();
$data['_my_stuff'] = $stuff;
// later on target page:
$data =& $controller->container();
$stuff = $data['_my_stuff'];
?>
you should use instead the storeOpaque() and getOpaque() methods of HTML_QuickForm2_Controller_SessionContainer:
<?php
// on source page:
$controller->getSessionContainer()->storeOpaque('my_stuff', $stuff);
// later on target page:
$stuff = $controller->getSessionContainer()->getOpaque('my_stuff');
?>
SessionContainer also has methods for storing and getting Controller DataSources, form values and form validation statuses, these should be used when writing custom action handlers.
The main difference between HTML_QuickForm_Page and HTML_QuickForm2_Controller_Page is that the latter no longer extends the form class, its constructor accepting an instance of HTML_QuickForm2:
<?php
class TutorialPage extends HTML_QuickForm2_Controller_Page
{
// ...
}
$page = new TutorialPage(new HTML_QuickForm2('tutorial'));
?>
This allows using custom subclasses of HTML_QuickForm2 with Controller and prevents problems like HTML_QuickForm_DHTMLRulesTableless faced, having to include both HTML_QuickForm_DHTMLRulesTableless and HTML_QuickForm_PageDHTMLRulesTableless the former extending HTML_QuickForm and the latter HTML_QuickForm_Page.
$controller
is no longer a public property of Page, use getController() to
access it. Similarly, use getForm() to access the
instance of HTML_QuickForm2.
As with HTML_QuickForm_Controller, former HTML_QuickForm_Page::addAction() is now HTML_QuickForm2_Controller_Page::addHandler(), old HTML_QuickForm_Page::buildForm() is renamed to HTML_QuickForm2_Controller_Page::populateForm().
In new HTML_QuickForm2_Controller_Page::populateForm() one no longer has to do something like
<?php
$this->_formBuilt = true;
?>
as was needed in old HTML_QuickForm_Page::buildForm(), the Page itself now makes sure that populateForm() is called only once.
Action handlers in HTML_QuickForm2_Controller define what should happen when request to a script containing the form is made. To make Controller execute a specific action handler after form submit you give a special name to a form's submit button:
<?php
$form->addElement('submit', $this->getButtonName('foo'),
array('value' => 'This button does foo!'));
?>
Action handlers are called via HTML_QuickForm2_Controller_Page::handle()
<?php
$page->handle('foo');
?>
which is usually done by HTML_QuickForm2_Controller::run() after finding page id and action name from
request, but can also be done manually. In fact, built-in handlers liberally call other action
handlers, the execution ending with either 'display'
,
'jump'
or 'process'
.
Action handlers can be added either to the form Page or to the Controller:
<?php
$page->addHandler('foo', new SpecificActionFoo());
$controller->addHandler('foo', new GenericActionFoo());
?>
When Page's handle() method is called it first checks for a handler added via HTML_QuickForm2_Controller_Page::addHandler() and calls its perform() method if present. If a handler is missing it calls Controller's handle() which checks for a handler added via HTML_QuickForm2_Controller::addHandler() and calls its perform() method. If a handler is missing here but its name is known and a default handler is available (see below), it is loaded and added automatically, otherwise an Exception is thrown.
Built-in name | Default handler | Description |
---|---|---|
'back' |
HTML_QuickForm2_Controller_Action_Back | This handler should be bound to the "Back" button of
(usually) a wizard-type multipage form, it redirects to the previous page whether current
page is valid or not. |
None, actual name should be equal to id of some
form page. |
HTML_QuickForm2_Controller_Action_Direct | Used to go to a specific page of the form, most useful for non-wizard forms, obviously. |
'next' |
HTML_QuickForm2_Controller_Action_Next | This handler should be bound to the "Next" button of
(usually) a wizard-type multipage form, it redirects to the next page if the current
one is valid (or the form is not wizard). On the last page of a multipage form it behaves
like 'submit' . |
'submit' |
HTML_QuickForm2_Controller_Action_Submit | This handler should be bound to a "global" submit button for a form. It can be
the "submit" button of a single-page or tabbed multi-page form, "finish"
button of a wizard. Default handler checks whether all the pages of the form are valid, then
either calls the 'process' handler or displays the invalid
page. |
Built-in name | Default handler | Description |
---|---|---|
'display' |
HTML_QuickForm2_Controller_Action_Display | Displays the form using Default renderer. You should subclass this handler and override its renderForm() method if you want to customize the form output. |
'jump' |
HTML_QuickForm2_Controller_Action_Jump | Performs a HTTP redirect to a given page. |
'process' |
None, application-specific | This is the action called by default 'submit' and
'next' (on the last page of the wizard only) handlers after
successful (i.e. without validation errors) form submit. This action doesn't have a
default handler, you should define the custom one yourself and implement all the necessary
logic to process the form's values in it. |
Basically you need to create a class implementing HTML_QuickForm2_Controller_Action and add necessary logic to its perform() method.
If you intend to bind this action to some submit button via getButtonName(), you should make sure that you store the submitted values in the session container. This is most easily done via HTML_QuickForm2_Controller_Page::storeValues().
As usual, see the built-in handlers' source for the inspiration.
Controller is able to properly handle actions bound to <input type="image"
/>
controls, too. You don't have to do anything special, just set the
control's name via getButtonName().
If you want to bind an action to something like a hyperlink, you must consider the following: the form must be submitted to be able to get its values, thus you need to write some javascript that will submit the form and pass the action name to the controller (possibly by setting a name of some hidden element to that name).
If an instance of HTML_QuickForm2 is not provided with an explicit
'action'
attribute it tries to guess it from environment using
$_SERVER['PHP_SELF']
. This may not be a best guess when all requests are
routed through index.php
.
Controller has even more problems since its 'jump'
handler has to build an
absolute URL for a redirect (per RFC 2616). It uses various values from
$_SERVER
array for this, but your server may be configured in such a way (e.g.
if you use reverse proxy) that an URL a user sees is quite different from one a script can guess.
Thus the only bulletproof solution sometimes will be to explicitly set the
'action'
attribute of all HTML_QuickForm2 instances in
Controller:
<?php
foreach ($controller as $page) {
$page->getForm()->setAttribute('action', 'http://example.com/pretty/url/of/my/form');
}
?>
Default handler for 'jump'
will use this absolute URL for redirects.
A common question is whether it is possible to get rid of "ugly" GET
parameters that appear when redirecting from one form page to the other:
http://example.com/wizard.php?_qf_page2_display=true
The simplest solution will be to get rid of 'jump'
altogether and use
'display'
in its place. However, this will completely break navigation with
browser's "Back" and "Forward" buttons, so don't do that.
It is possible to use an URL like
http://example.com/wizard/page2
instead of the above one, but it'll be more difficult:
'jump'
handler that'll redirect to a
"pretty" URL instead of an "ugly" one.$_REQUEST
.API to create HTML tables
HTML_Table offers an interface for create a HTML table. You can work with the table like a spreadsheet. Instead of working with HTML code and linear adding of cells, you can address and fill cells independend of there position. There is no different, whether you start with fill a cell at the beginning, in the middle or at the end of the table, a row or a column.
autoGrow
flagNormaly, you would define a table with a constant number of rows and columns. But sometimes, you does not know, how many rows or columns you need: ie. transforming user input or the result of a database query to an HTML table.
In this case, you should
enable
the autoGrow
feature.
In this mode, HTML_Table adds new
rows or columns automatically, if you use a cell address located in a
not existing row or column.
autoFill
value
If you create a table of data, sometimes you have not to fill
all cells with different values. Perhaps you do not know the
value for a cell, or you want to insert a default value - ie. retrieving
data about users. Not every user has a mobile, a email address etc.,
in this case, an "n/a"
should be inserted into that
specific cell.
So, simply
define "n/a"
as autoFill
value and fill only
the cells where data exist. You need not to fill every cell; unfilled
cells contain automatically an "n/a"
.
Our HTML table to create should contain the following data:
<?php
$data = array(
'0' => array('Bakken', 'Stig', '', 'stig@example.com'),
'1' => array('Merz', 'Alexander', 'alex.example.com', 'alex@example.com'),
'2' => array('Daniel', 'Adam', '', '')
);
?>
Let us now start by creating a new instance of
HTML_Table. The table
should be 600 pixel wide. We do not know
the quantity of the data to insert into the table -
so we enable the autoGrow
feature.
Unfilled cells should contain an "n/a"
.
<?php
require_once 'HTML/Table.php';
$attrs = array('width' => '600');
$table = new HTML_Table($attrs);
$table->setAutoGrow(true);
$table->setAutoFill('n/a');
?>
Setting table attributes is also possible by using the setAttributes() method. Therefore, the example from above can also be written as:
<?php
require_once 'HTML/Table.php';
$attrs = array('width' => '600');
$table = new HTML_Table();
$table->setAttributes($attrs);
// [...]
?>
Now process every data entry. Here we use also the alternate feature of HTML_Table. Every second row will be colored red.
<?php
for ($nr = 0; $nr < count($data); $nr++) {
$table->setHeaderContents($nr+1, 0, (string)$nr);
for ($i = 0; $i < 4; $i++) {
if ('' != $data[$nr][$i]) {
$table->setCellContents($nr+1, $i+1, $data[$nr][$i]);
}
}
}
$altRow = array('bgcolor' => 'red');
$table->altRowAttributes(1, null, $altRow);
?>
Now we want to define the cells in the first row and column as header cells. It should looks like a spreadsheet application, so we want to use the color "silver" as the background colour for each header cell. The first row contains a column headline, the first column the number of the data set row.
<?php
$table->setHeaderContents(0, 0, '');
$table->setHeaderContents(0, 1, 'Surname');
$table->setHeaderContents(0, 2, 'Name');
$table->setHeaderContents(0, 3, 'Website');
$table->setHeaderContents(0, 4, 'EMail');
$hrAttrs = array('bgcolor' => 'silver');
$table->setRowAttributes(0, $hrAttrs, true);
$table->setColAttributes(0, $hrAttrs);
?>
It is done! Our table is finished, now we can output the table as HTML code.
<?php
echo $table->toHtml();
?>
The output will look like this:
If you want to divide your tables into thead
,
tfoot
and tbody
groups, you need to
get table objects using
getHeader(),
getFooter(),
and getBody(),
which you can then use like the normal table object.
<?php
$table = new HTML_Table();
$head =& $table->getHeader();
$foot =& $table->getFooter();
$body =& $table->getBody();
$head->setCellContents(...);
$body->setCellContents(...);
echo $table->toHtml();
?>
In this example, there is no content set for the tfoot
group. Therefore, only thead
and tbody
will be rendered.
The rendering order is
thead
, thentfoot
and as the last grouptbody
. This is not a bug but intended behaviour because that's the way it is defined in the (X)HTML Standard.
Since release 1.8.0
getBody()
and several other methods like
setCellAttributes()
accept an optional numeric parameter $body
that allows
you to generate multiple tbody
groups in your table. A
new group can be generated by using
addBody()
or, if the
autoGrow
feature is enabled, by using a new number in one of the mentioned method
calls.
This document is based on questions asked on PEAR general mailing list and other mailing lists and forums.
thead
,
tfoot
or tbody
tags?
Here is an example on how to set the attribute string
id="header"
for the thead
tag. For
the other two tags the procedure is similar.
<?php
$table = new HTML_Table();
// [...]
$thead =& $table->getHeader();
$thead->setAttributes(array('id' => 'header'));
// [...]
$table->display();
?>
This would give the following result:
<table> <thead id="header"> [...] </thead> [...] </table>
table
tag?
Besides the possibility to pass attributes to the HTML_Table constructor, there are several more methods that can be used.
HTML_Table extends HTML_Common which offers methods like setAttributes() or updateAttributes(). A complete list of methods provided by HTML_Common can be found in its API documentation.
Stuart Langridge has developed the SortTable JavaScript class that allows adding of sorting facilities to tables very easily. Another class for this purpose is Standardista Table Sorting.
void HTML_Table::HTML_Table (
array $attributes
= null
,
integer $tabOffset=0
,
boolean $useTGroups
= false
)
Class constructor
array $attributes
-
Associative array of table tag attributes
integer $tabOffset
-
Tab offset of the table
boolean $useTGroups
-
Whether to use the thead, tfoot and tbody groups or not
This function can not be called statically.
int HTML_Table::addBody (
array $attributes
= null
)
Adds a new body to the table and returns the body identifier
array $attributes
-
Associative array of tbody tag attributes
int
- the body identifier
This function can not be called statically.
int HTML_Table::addCol (
array $contents
= null
,
mixed $attributes
= null
,
string $type='TD'
,
int $body=0
)
Adds a table column and returns the column identifier
array $contents
-
Must be an indexed array of valid cell contents.
mixed $attributes
-
Associative array or string of table row attributes
string $type
-
Cell type either 'th'
or
'td'
int $body
-
The number of the tbody
group that should be used
int
- the identifier for the column
This function can not be called statically.
int HTML_Table::addRow (
array $contents
= null
,
mixed $attributes
= null
,
string $type='TD'
,
boolean $inTR
= false
,
int $body=0
)
Adds a table row and returns the row identifier
array $contents
-
Must be an indexed array of valid cell contents
mixed $attributes
-
Associative array or string of table row attributes.
This can also be an array of attributes, in which case the attributes
will be repeated in a loop.
string $type
-
Cell type either 'th'
or
'td'
boolean $inTR
-
FALSE, if attributes are to be
applied in TD
tags;
TRUE, if attributes are to be
applied in TR
tag
int $body
-
The number of the tbody
group that should be used
int
-
the row identifier
This function can not be called statically.
void HTML_Table::altRowAttributes (
int $start
,
mixed $attributes1
,
mixed $attributes2
,
boolean $inTR
= false
,
int $body
= null
)
Alternates the row attributes starting at $start
int $start
-
Row index of row in which alternating begins
mixed $attributes1
-
Associative array or string of table row attributes
mixed $attributes2
-
Associative array or string of table row attributes
boolean $inTR
-
FALSE, if attributes are to be applied in
td
tags;
TRUE, if attributes are to be applied in
tr
tag.
int $body
-
The number of the tbody
group that should be used.
NULL indicates that all groups should be used.
This function can not be called statically.
double HTML_Table::apiVersion (
)
DEPRECATED: Returns the API version of HTML_Table
double
- the version number
This function can not be called statically.
This function is deprecated. That means that future versions of this package may not support it anymore.
string HTML_Table::display (
)
Outputs the table structure as HTML
This function can not be called statically.
mixed HTML_Table::getAutoFill (
int $body=0
)
Returns the autoFill value
int $body
-
The number of the tbody
group that should be used
mixed
- the value, which will be inserted into
empty cells
This function can not be called statically.
boolean HTML_Table::getAutoGrow (
int $body=0
)
Returns the value of the autoGrow
flag.
If a value into an cell, which not exists,
HTML_Table will automatically add a
necessary row or column, if the flag is TRUE.
int $body
-
The number of the tbody
group that should be used
boolean
- the flag state
This function can not be called statically.
mixed HTML_Table::getBody (
int $body=0
)
Returns the table object for a tbody group
int $body
-
The number of the tbody
group that should be used
object
- the table object for a tbody group
This function can not be called statically.
array HTML_Table::getCellAttributes (
int $row
,
int $col
,
int $body=0
)
Returns the attributes for a given cell
int $row
-
Row index
int $col
-
Column index
int $body
-
The number of the tbody
group that should be used
array
-
the attributes of the specific cell
This function can not be called statically.
mixed HTML_Table::getCellContents (
int $row
,
int $col
,
int $body=0
)
Returns the content of an existing cell
int $row
-
Row index
int $col
-
Column index
int $body
-
The number of the tbody
group that should be used
mixed
- the content of the specified cell
This function can not be called statically.
int HTML_Table::getColCount (
int $row=null
,
int $body=0
)
Returns the number of columns in the table
int $row
-
The row number to serve for cols count
int $body
-
The number of the tbody
group that should be used
int
- number of columns
This function can not be called statically.
mixed HTML_Table::getHeader (
)
Returns the table object for the thead group
If the usage of the thead, tfoot and tbody groups was not activated via the third parameter of the constructor, the grouping will be activated automatically when calling this function.
object
- the table object for the thead group
This function can not be called statically.
array HTML_Table::getRowAttributes (
int $row
,
int $body=0
)
Returns the attributes for a given row as contained in the
tr
tag
int $row
-
Row index
int $body
-
The number of the tbody
group that should be used
array
- the attributes of the row
This function can not be called statically.
int HTML_Table::getRowCount (
int $body=0
)
Returns the number of rows in the table
int $body
-
The number of the tbody
group that should be used
int
- number of rows
This function can not be called statically.
void HTML_Table::setAllAttributes (
mixed $attributes
= null
,
int $body
= null
)
Sets the attributes for all cells
mixed $attributes
-
Associative array or string of table cell attributes
int $body
-
The number of the tbody
group that should be used.
NULL indicates that all groups should be used.
This function can not be called statically.
void HTML_Table::setAutoGrow (
bool $grow
,
int $body
= null
)
If this flag is set to TRUE, HTML_Table will automatically add new rows or columns when you insert a value into a non-existing cell.
bool $grow
- TRUE
to automatically grow
int $body
-
The number of the tbody
group that should be used.
NULL indicates that all groups should be used.
This function can not be called statically.
void HTML_Table::setAutoFill (
mixed $fill
,
int $body
= null
)
The autoFill
value will be insert into
every cell not filled.
mixed $fill
-
standard value for empty cells
int $body
-
The number of the tbody
group that should be used.
NULL indicates that all groups should be used.
This function can not be called statically.
void HTML_Table::setCaption (
string $caption
,
mixed $attributes
= null
)
Sets the caption of a table. This does not refer to the
<th>
-tag. The
<caption>
-tag defines a headline for the
whole table.
string $caption
-
the caption string
mixed $attributes
-
Associative array or string of caption attributes
This function can not be called statically.
void HTML_Table::setCellAttributes (
int $row
,
int $col
,
mixed $attributes
,
int $body=0
)
Sets the cell attributes for an existing cell. If the given indices
do not exist and autoGrow
is
TRUE then the
given row and/or column is automatically added.
If autoGrow
is
FALSE, an error is returned.
int $row
-
Row index
int $col
-
Column index
mixed $attributes
-
Associative array or string of table cell attributes
int $body
-
The number of the tbody
group that should be used
Error code | Error message | Meaning | Solution |
---|---|---|---|
"
Invalid table row reference [$row ]
"
|
The row $row does not exists |
Enable the autoGrow feature |
|
"
Invalid table column reference [$column ]
"
|
The column $column does not exists |
Enable the autoGrow feature |
This function can not be called statically.
void HTML_Table::setCellContents (
int $row
,
int $col
,
mixed $contents
,
string $type='TD'
,
int $body=0
)
Sets the cell contents for an existing cell.
If the given indices do not exist and autoGrow
is TRUE then the given row and/or column is
automatically added. If autoGrow
is
FALSE, then an error is returned.
int $row
-
Row index
int $col
-
Column index
mixed $contents
-
May contain HTML or any object with a toHTML() method
string $type
-
Cell type either 'th'
or
'td'
int $body
-
The number of the tbody
group that should be used
Error code | Error message | Meaning | Solution |
---|---|---|---|
"
Invalid table row reference [$row ]
"
|
The row $row does not exists |
Enable the autoGrow feature |
|
"
Invalid table column reference [$column ]
"
|
The column $column does not exists |
Enable the autoGrow feature |
This function can not be called statically.
void HTML_Table::setColAttributes (
int $col
,
mixed $attributes
= null
,
int $body
= null
)
Sets the column attributes for an existing column
int $col
-
Column index
mixed $attributes
-
Associative array or string of table column attributes
int $body
-
The number of the tbody
group that should be used.
NULL indicates that all groups should be used.
This function can not be called statically.
void HTML_Table::setColCount (
int $cols
,
int $body=0
)
Sets the number of columns in the table
int $cols
-
number of columns to create
int $body
-
The number of the tbody
group that should be used
This function can not be called statically.
void HTML_Table::setColGroup (
mixed $colgroup
= null
,
mixed $attributes
= null
)
Allows to add a colgroup (with attributes)
mixed $colgroup
-
Empty string, string with attributes or an associative array of
attributes for the columns
mixed $attributes
-
Associative array or string of colgroup attributes
Usage without a col tag
<?php
require_once 'HTML/Table.php';
$table =& new HTML_Table();
$colgroup = '';
$attributes = 'span="3" class="group1"';
$table->setColGroup($colgroup, $attributes);
?>
Usage with col tags
<?php
require_once 'HTML/Table.php';
$table =& new HTML_Table();
$colgroup = array('style="font-size: 120%;"', 'class="col2"', 'align="right"');
$attributes = 'span="3" class="group1"';
$table->setColGroup($colgroup, $attributes);
?>
Usage with multiple colgroups
<?php
require_once 'HTML/Table.php';
$colgroup = array('style="font-size:120%;"', 'class="col2"', 'align="right"');
$attributes = 'span="3" class="group1"';
$table->setColGroup($colgroup, $attributes);
$colgroup = array(array('class' => 'col4'), array('class' => 'col5'));
$attributes = 'span="2" class="group2"';
$table->setColGroup($colgroup, $attributes);
?>
This function can not be called statically.
void HTML_Table::setColType (
int $col
,
string $type
,
int $body
= null
)
Sets the type of a column to 'th'
or 'td'
int $col
-
Column index
string $type
-
th
or td
int $body
-
The number of the tbody
group that should be used.
NULL indicates that all groups should be used.
This function can not be called statically.
void HTML_Table::setHeaderContents (
int $row
,
int $col
,
mixed $contents
,
int $body=0
)
Defines the specified cell as a header cell and sets the content
int $row
-
row index
int $col
-
column index
mixed $contents
-
cell content
int $body
-
The number of the tbody
group that should be used
This function can not be called statically.
void HTML_Table::setRowCount (
int $rows
,
int $body=0
)
Sets the number of rows in the table
int $rows
-
number of rows to create
int $body
-
The number of the tbody
group that should be used
This function can not be called statically.
void HTML_Table::setRowType (
int $row
,
string $type
,
int $body=0
)
Sets the type of a row to 'th'
or
'td'
int $row
-
Row index
string $type
-
'th'
or 'td'
int $body
-
The number of the tbody
group that should be used
This function can not be called statically.
void HTML_Table::setRowAttributes (
int $row
,
mixed $attributes
,
boolean $inTR
= false
,
int $body=0
)
Sets the attributes for an existing row
int $row
-
Row index
mixed $attributes
-
Associative array or string of table row attributes.
This can also be an array of attributes,
in which case the attributes will be repeated in a loop.
boolean $inTR
-
FALSE, if attributes are to be
applied in td
tags;
TRUE, if attributes are to be
applied in tr
tag
int $body
-
The number of the tbody
group that should be used
Error code | Error message | Meaning | Solution |
---|---|---|---|
"
Invalid table row reference [$row ]
"
|
The row $row does not exists |
Enable the autoGrow feature |
|
"
Invalid table column reference [$column ]
"
|
The column $column does not exists
|
Enable the autoGrow feature |
This function can not be called statically.
string HTML_Table::toHtml (
)
Returns the table structure as HTML
string
-
the generated HTML code
This function can not be called statically.
void HTML_Table::updateAllAttributes (
mixed $attributes
= null
,
int $body
= null
)
Updates the attributes for all cells.
mixed $attributes
-
Associative array or string of table row attributes
int $body
-
The number of the tbody
group that should be used.
NULL indicates that all groups should be used.
This function can not be called statically.
void HTML_Table::updateCellAttributes (
int $row
,
int $col
,
mixed $attributes
,
int $body=0
)
Updates the cell attributes passed but leaves other existing attributes intact
int $row
-
Row index
int $col
-
Column index
mixed $attributes
-
Associative array or string of table row attributes
int $body
-
The number of the tbody
group that should be used
This function can not be called statically.
void HTML_Table::updateColAttributes (
int $col
,
mixed $attributes
= null
,
int $body
= null
)
Updates the column attributes for an existing column
int $col
-
Column index
mixed $attributes
-
Associative array or string of table row attributes
int $body
-
The number of the tbody
group that should be used.
NULL indicates that all groups should be used.
This function can not be called statically.
void HTML_Table::updateRowAttributes (
int $row
,
mixed $attributes
= null
,
boolean $inTR
= false
,
int $body=0
)
Updates the row attributes for an existing row
int $row
-
Row index
mixed $attributes
-
Associative array or string of table row attributes
boolean $inTR
-
FALSE if attributes are to be applied in
td
tags; TRUE,
if attributes are to be applied in tr
tag.
int $body
-
The number of the tbody
group that should be used
Error code | Error message | Meaning | Solution |
---|---|---|---|
"
Invalid table row reference [$row ]
"
|
The row $row does not exists |
Enable the autoGrow feature |
|
"
Invalid table column reference [$column ]
"
|
The column $column does not exists
|
Enable the autoGrow feature |
This function can not be called statically.
Create and display defined "tags" (words) in a "tag cloud".
This package can be used to generate tag coulds in HTML and CSS. A tag cloud is an visual representation of list of so-called "tags" or keywords, that do have a different font size depending on how often they occur on the page/blog.
More information on tag clouds is available in Wikipedia.
This package does not only visualize frequency, but also timeline infomation. The newer the tag is, the deeper its color will be; older tags will have a lighter color.
Basic example
<?php
require_once 'HTML/TagCloud.php';
$tags = new HTML_TagCloud();
// add Elements
$tags->addElement('PHP' ,'http://www.php.net' , 39, strtotime('-1 day'));
$tags->addElement('XML' ,'http://www.xml.org' , 21, strtotime('-2 week'));
$tags->addElement('Perl' ,'http://www.xml.org' , 15, strtotime('-1 month'));
$tags->addElement('PEAR' ,'http://pear.php.net' , 32, time());
$tags->addElement('MySQL' ,'http://www.mysql.com', 10, strtotime('-2 day'));
$tags->addElement('PostgreSQL','http://pgsql.com' , 6, strtotime('-3 week'));
// output HTML and CSS
print $tags->buildALL();
?>
The above example code creates a tag cloud containing five items. The tag used most often is "PHP"; it will appear in the largest font. The tag used latest is"PEAR", it is in the deepest color. Tags are sorted by alphabetically, and escaped in HTML.
HTML_TagCloud creates HTML and CSS. The HTML is a simple list, decorated by CSS. You are able to output HTML and CSS separately.
output HTML and CSS separately
<?php
// CSS part only
$css = $tags->buildCSS();
// html part only
$taghtml = $tags->buildHTML();
?>
The HTML output depends on the tag data that have been added via addElement(), but the CSS output is static. You can use this package's CSS output statically.
It is possible to modify the colors used by the CSS. You need to define your own class which extends HTML_TagCloud and override color and size properties.
Decoration example
<?php
class MyTags extends HTML_TagCloud
{
protected $epocLevel = array(
array(
'earliest' => array(
'link' => 'ffdfdf',
'visited' => 'ffdfdf',
'hover' => 'ffdfdf',
'active' => 'ffdfdf',
),
),
array(
'earlier' => array(
'link' => 'ff7f7f',
'visited' => 'ff7f7f',
'hover' => 'ff7f7f',
'active' => 'ff7f7f',
),
),
array(
'previous' => array(
'link' => 'ff7f7f',
'visited' => 'ff7f7f',
'hover' => 'ff7f7f',
'active' => 'ff7f7f',
),
),
array(
'recent' => array(
'link' => 'ff4f4f',
'visited' => 'ff4f4f',
'hover' => 'ff4f4f',
'active' => 'ff4f4f',
),
),
array(
'later' => array(
'link' => 'ff1f1f',
'visited' => 'ff1f1f',
'hover' => 'ff1f1f',
'active' => 'ff1f1f',
),
),
array(
'latest' => array(
'link' => 'ff0000',
'visited' => 'ff0000',
'hover' => 'ff0000',
'active' => 'ff0000',
),
),
);
protected $size_suffix = 'pt';
protected $fontsizerange = 0;
protected $basefontsize = 12;
}
?>
If you don't want to add timeline information and have the color changing accordingly, just omit the fourth parameter to addElement(). When doing this, the current time is set.
Omitting the timeline
<?php
$tags->addElement('PHP','http://www.php.net', 39);
?>
An extremely flexible Template core, with multiple engines.
HTML_Template_Flexy started its life as a simplification of HTML_Template_Xipe, However the long term aim of Flexy is to provide a universal Template Base API for Compiling and native PHP type templates.
Flexy currently supports a number of backends (Template Formats), and is designed to be extended to support more, The Key Formats are:
Data can be assigned in two ways with flexy, depending on your preferred style of working.
With all this Flexibility, it still manages to achieve
If you look around you will see there are other template systems available in PHP, they generally fall into two categories, Replacement Systems, or PHP Code builders.
Replacement systems like HTML_Template_IT, FastTemplate, PhpLib Template tend to be slower at doing block and nested block type templates and involve alot of code to add each variable to the template.
Php Code builders like Flexy, Smarty, SimpleTemplate (now HTML_Template_Xipe) tend to be better at more complex templates, and can offer a better approach to extendability. (the long term aim of Flexy is to integrate support for all of these PHP Generator templates into a simple package)
The Standard Compiling Backend uses a Tokenizer, which offers the possiblities of using HTML tags and attributes to provide looping and conditionals, and make dynamic XML_Tree like elements of HTML Forms that can be manipulated in your code. (This conversion is only done once when the template compiles)
Flexy template is normally called from within a Controller Class (in the Model,View,Controller paragam). You just send HTML_Template_Flexy, the name of the template, and the object to output. - any variable you want printing out just has to be set in the object being used to ouput.
Typical usage example for HTML_Template_Flexy
<?php
/* configure the application - probably done elsewhere */
require_once 'HTML/Template/Flexy.php';
require_once 'PEAR.php';
$options = &PEAR::getStaticProperty('HTML_Template_Flexy','options');
$config = parse_ini_file('example.ini',TRUE);
$options = $config['HTML_Template_Flexy'];
/* the page controller class */
class controller_test
{
var $template = "home.html"; // name of template
var $title; // this relates to {title};
var $numbers = array(); // this relates to {numbers} , used with foreach
var $anObject;
var $elements = array(); // this is where the elements are stored
/* start section - deals with posts, get variables etc.*/
function controller_test()
{
$this->start();
$this->output();
}
function start()
{
// the title
$this->title = "Hello World";
// store an object.
$this->anObject = new StdClass;
// assign a value to a member.
$this->anObject->member = 'Object Member';
// if you need form elements - you have to include them.
require_once 'HTML/Template/Flexy/Element.php';
// create an HTML Element for the form element.
$this->elements['input'] = new HTML_Template_Flexy_Element;
// assign a value to it
$this->elements['input']->setValue('Hello');
for ($i = 1;$i< 5;$i++) {
$this->numbers[$i] = "Number $i";
}
}
/* output section - probably best to put this in the default_controller class */
function output() {
$output = new HTML_Template_Flexy();
$output->compile($this->template);
$output->outputObject($this,$this->elements);
}
function someMethod() {
return "<b>Hello From A Method</b>";
}
}
/* the page controller instantaation - probably done with a factory method in your master page controller class */
new controller_test();
?>
Now the example template,
Example Template for HTML_Template_Flexy
//ignore any php tags before this comment
<html>
<head>
<title>{title}</title>
</head>
<body>
<H1>{title}</H1>
<form name="form">
<table>
<tr>
<td>
<?php /* this will be merged with the date in the $element['input'] object*/ ?>
Input Box: <input name="input">
</td>
</tr>
<?php /* note here the use for flexy:foreach as an attribute value */ ?>
<tr flexy:foreach="numbers,number,string">
<td>
<?php /* note here the use for flexy:foreach as an attribute value */ ?>
<a href="mypage.html?id=%7Bnumber%7D">{string}</a>
</td>
</tr>
</table>
<?php /* there is some limited support for array access */ ?>
this is number 2 : {numbers[2]}
<?php /* note that full stops separate object and variables or methods */ ?>
This is a {anObject.member}
<?php /* you can call methods of the object */ ?>
{someMethod()}
<?php /* by default everything is htmlspecialchar escaped use the modifier :h to prevent this. */ ?>
{someMethod():h}
</body>
</html>
<?php /* I've used php for comments here as HTML comments didnt work when generating the
manual .. - you dont have to use them - it has nothing to do with the template engine */ ?>
And the output
Output of example for HTML_Template_Flexy
Hello World Input Box : [Hello ] Number 1 Number 2 Number 3 Number 4 this is number 2 : Number 2 This is a member Variable <B>Hello From A Method</B> Hello From A Method
HTML_Template_Flexy can either be configured globally or on each instance, using an associated array. The easiest way to configure HTML_Template_Flexy is to use ini files (although you may also like to consider the PEAR::Config class, or your own configuration system)
This is a typical configuration file for HTML_Template_Flexy
[HTML_Template_Flexy] templateDir = /home/me/Projects/myapplication/templates compileDir = /home/me/Projects/myapplication/compiled_templates forceCompile = 0 debug = 0 locale = en compiler = Standard
To use this ini file with HTML_Template_Flexy, (and Possibly any other classes that use options like this)
Setting the default options
<?php
$config = parse_ini_file('example.ini',TRUE);
foreach($config as $class=>$values) {
$options = &PEAR::getStaticProperty($class,'options');
$options = $values;
}
?>
Alternatively you can set (or override) the configuration when you instantate the class
Setting the default options
<?php
$options = array(
'templateDir' => '/home/me/Projects/myapplication/templates',
'compileDir' => '/home/me/Projects/myapplication/compiled_templates',
'forceCompile' => 0,
'debug' => 0,
'locale' => 'en',
'compiler' => 'Standard',
);
$template = new HTML_Template_Flexy($options);
?>
templateDir
directory
This is the directory where all your templates are located
compileDir
directory
The directory where the compiled templates will be stored, This directory should be writable by the web server
forceCompile
boolean
Normally 0, means that the template will only be compiled once (or if the template file is altered), this is only really usefull if you are developing filters and need to test the result.
debug
integer
The default debugging level (default 0=off), 1= shows some debugging information
locale
string
Default is 'en' - english. The language use for reading/writing templates. Currently it is only used in the compiled files filename = eg. originalname.html.en.php
Flexy uses get_text() internally if it is installed, and will replace all strings in a HTML page with the return value of get_text(). - This enables the creation of multilanguage sites with a little less pain.
A file {templatename}.strings.serial is created for each file that is parsed, you can use this with PHP's unserialize function to retrieve an array of all the strings in a file. (for translating), or just use the tool xgettext.
compiler
string
Default is 'Flexy' - The Flexy Tokenizer Driver engine. Other engines available are regex (similar to Xipe's engine), Raw (For plain PHP files with no replacement or compiling) and Standard (depreciated). You can use this field to load your own engines, either based Off the core code, or totally separate..
multiSource
boolean
Default is false - Allow the same template to exist in multiple places (eg. if you have theme's and want to fall back to a default template if the themed version doesnt exist.)
templateDirOrder
string
Default is '' - by default, the first matched template is used, if you have multiple paths, and want the last in the list to be used, then set this to 'reverse'
filters
array|string
an array or comma separated string of filters ONLY USED BY THE Regex backend, available filters are: BodyOnly (strip everything before and after body tag), Mail (add an extra line break after php tags.), Php (removes php code, not very reliably), SimpleTags (variable, method etc. replacement), XML (replace XML opening tag with echos.)
nonHTML
boolean
default is false - if you use the Flexy compiler, it turns off parsing of HTML, (not heavily tested)
allowPHP
boolean|string
default is false - allows php code in templates, normally off to help you reduce the chance of you shooting your self in the foot by forgetting to escape output.. (can be usefull for complex looping), but not normally recommended. setting to true, enables PHP code, setting to 'delete' removes php code. (although it doesnt prevent XSS attacks, so it is only suited to trusted users)
flexyIgnore
boolean
default is false - setting to true, will turn off the conversion of html form elements into HTML_Template_Flexy_Element's
numberFormat
string
default is ",2,'.',','" - this is the piece of code that is appended to the output engine when using the :n modifier, eg. {xxxx:n} is replaced with number_format($t->xxxx,2,'.',','); see the php manual page for number_format the default output would be: 1,200.00
url_rewrite
string
when compiling the template flexy can rewrite <img src, <script src, <a href and xul stylesheet urls. The format is "match/original:new/url, match/another/original:new/url" each combo is separated by a comma, and the colon separates the pair. This helps previewing templates without using the engine.
compileToString
boolean
default false - if set to true, the compile will return a string of the compiled template, rather than writing it to the cache file. eg. {object._myvar}
privates
boolean
default false - if set to true, you can access variables prefixed with an underscore (normally private in PEAR's coding standards) eg. {someobject._myprivatevar} and {_myprivate}
globals
boolean
default false - if set to true, you can access php's globals and superglobals, eg. {_POST[myvar]}, {GLOBALS[somevalue]}
globalfunctions
boolean
default false - if set to true, you can access any native php function using the GLOBALS. prefix eg. {GLOBALS.date(#d/m/Y#)} obviously you should trust your template authors, as they can easily run exec() if this is enabled.
locale
string
default 'en' - either used to search for language specific templates with {filename}.{locale}.{extension} or in conjunction with the Translation2 language translation toolkit, to set the language used to translate templates to at compile time.
Translation2
mixed
default false - you can set this to an array or an existing Translation2 object eg. Translation2 => array('driver'=>'dataobjectsimple', options=>array()));
strict
boolean
default false - By default warnings about undefined variabes are hidden, this turns on all PHP warnings during the outputObject calls. Can be usefull for finding bugs hidden by method callbacks.
fatalError
int
default constant HTML_TEMPLATE_FLEXY_ERROR_DIE - this determines the behaviour when compiling a template fails, normally flexy will die and report the error to the screen, you can change this to HTML_TEMPLATE_FLEXY_ERROR_RETURN, if you want to recieve a PEAR_Error object from compile().
plugins
string|array
loads plugin classes, (by default from the Plugin folder), these can be used either via {plugin.nameofmethod} or as a modifier {outputstring:dateformat}, default formats are normally collected via configuration options plugin.dateformat, plugin.numberformat.decimals, plugin.numberformat.point, plugin.numberformat.thousands
new
HTML_Template_Flexy (
array
$options
)
The Object Constructor accepts options as its arguments. Normally, you do not need to provide any options, as they are set by the PEAR::getStaticPropery(), as described in the configuration.
array $options
-
anything you need to change from the default settings
This function can not be called statically.
void $flexy->
compile (
string
$template
)
If necessary it will convert the Template markup into PHP code, and writes it to the compiledTemplate directory adding the {locale}.php to the end of the filename. The Template is only compiled if
It is not normally necessary to set the forceCompile flag, unless you are working on the engine itself.
string $template
-
Used in conjuction with the config variable 'templateDir' to locate the
template to compile into PHP code.
string
- the location of the compiled file (which could be used
with include) - although it is recommended to use the outputObject methods.
In case compileToString
option is set to TRUE, the compiled file
is directly returned as string here.
This function can not be called statically.
Compiling multiple files.
<?php
class controller_test
{
var $masterTemplate = "master.html"
var $template = "home.html"; // name of template
var $title; // page title;
var $numbers = array(); // an array example
/* start section - deals with posts, get variables etc.*/
function controller_test()
{
$this->start();
$this->output();
}
function start()
{
$this->title = "<Hello World>";
for ($i = 1;$i< 5;$i++) {
$this->numbers[$i] = "Number $i";
}
}
/* output section - probably best to put this in the default_controller class */
function output() {
$master = new HTML_Template_Flexy();
$master->compile('some_file_name');
$master->outputObject($this);
}
function outputBody() {
$body = new HTML_Template_Flexy();
$body->compile($this->template);
$body->outputObject($this);
}
}
new controller_test;
?>
Master template example
Page Header Goes here. {outputBody()} Page Footer Goes here
Simple ouput example
Page Header Goes here. some numbers Number 1 Number 2 Number 3 Number 4 Page Footer Goes here
void $flexy->
outputObject (
object $controllerObject
, array $elements
)
This makes the values of the supplied object (and optionally loads the HTML_Template_Flexy_Elements) available to the template when it is run.
object $controllerObject
-
The object you want to use with the template, the values of the object will
relate to the $controllerObject->tag will map to {tag} on the template
array $elements
-
This is an associative array of form, or dynamic elements names (or id's) which will be merged with
the one defined in the template.
This function can not be called statically.
PHP code initiating the template, and outputing it
<?php
class example {
var $tag = ">> hello world";
}
$data = new example;
$elements['test'] = new HTML_Template_Flexy_Element;
$elements['test']->setValue("hello input");
$output = new HTML_Template_Flexy();
$output->compile("hello.html");
$output->outputObject($data,$elements);
?>
The Template with some tags
<B>{tag}</B> <B>{tag:h}</B> <INPUT name="test">
Resulting output
<B>>> hello world</B> <B>>> hello world</B> <INPUT name="test" value="hello input">
string $flexy->bufferedOutputObject (
object $controllerObject
, array $elements
)
This maps the values of the supplied object and runs the compiled template, and returns the result.
This can be used in conjuction with PEAR::Cache, or in the example below, with a email template (note this still needs testing.. - the backend should eventually support a native tokenizer for email templates.)
object $controllerObject
-
The object you want to use with the template, the values of the object will
relate to the $controllerObject->tag will map to {tag} on the template
array $elements
-
This is an associative array of form, or dynamic elements names (or id's) which will be merged with
the one defined in the template.
string
- the object variables overlayed on the template
This function can not be called statically.
Person DataObject send_password method
<?php
class DataObjects_Person {
var $id;
var $name;
var $password;
var $cleartextPassword;
var $email;
function sendEmail($templateFile,$content) {
$content = is_object($content) ? $content : (object) $content;
foreach(get_object_vars($this) as $k=>$v) {
$content->$k = $v;
}
/* use the regex compiler, as it doesnt parse <tags */
$template = new HTML_Template_Flexy( array(
'compiler' => 'Regex',
'filters' => array('SimpleTags','Mail'),
));
/* compile a text file (email template) */
$template->compile($templateFile);
/* use variables from this object to ouput data. */
$mailtext = $template->bufferedOutputObject($content);
//echo "<PRE>";print_R($mailtext);
/* With the output try and send an email, using a few tricks in Mail_MimeDecode. */
require_once 'Mail/mimeDecode.php';
require_once 'Mail.php';
$decoder = new Mail_mimeDecode($mailtext);
$parts = $decoder->getSendArray();
if (PEAR::isError($parts)) {
return $parts;
}
list($recipents,$headers,$body) = $parts;
$mailOptions = PEAR::getStaticProperty('Mail','options');
$mail = Mail::factory("SMTP",$mailOptions);
return PEAR::isError($mail) ? $mail : $mail->send($recipents,$headers,$body);
}
}
?>
An email template (using the Regex Parser - hence the {t.*})
From: "HTML_Template_Flexy" <html_template_flexy@pear.php.net> Sender: "HTML_Template_Flexy" <html_template_flexy@pear.php.net> To: {t.email} Subject: Here is your new password Content-Type: text/plain; charset=us-ascii Dear {t.name} Your New Password is {t.cleartextPassword} please use it to log into your bank account :)
The resulting email head and body
From: "HTML_Template_Flexy" <html_template_flexy@pear.php.net> Sender: "HTML_Template_Flexy" <html_template_flexy@pear.php.net> To: demo@example.com Subject: Here is your new password Content-Type: text/plain; charset=us-ascii Dear Fred Blobs Your New Password is 0ab123dcc please use it to log into your bank account :)
array $flexy->
getElements (
)
All Form elements, FORM, INPUT, SELECT and any HTML tag that includes the attribute flexy:dynamic Is converted into HTML_Template_Flexy_Element's and stored serialized in the same folder as the Compiled flexy template.
You can use this array to make changes to these elements or find out what form elements exist on a page.
Note: you should put the modified result as the $elements argument of >outputObject(), you do not however have to fetch the elements to assign them, you can just create blank elements, and merge them.
array
- of Elements contained within the template. (or an empty array if
no form/dynamic elements are used)
This function can not be called statically.
Introspecting a template
<?php
$form = new HTML_Template_Flexy();
$form->compile('some_file_name');
print_r($form->getElements());
?>
template example
<BODY> <FORM name="XXXX"> <INPUT name="yyy"> <SELECT name="zzz"> <OPTION value="aaaa">AAAAA</OPTION> </SELECT> </FORM> </BODY>
template compiled
<BODY> <?php echo $this->elements['XXXX']->toHtmlnoClose();?> <?php echo $this->elements['yyy']->toHtml();?> <?php echo $this->elements['zzz']->toHtml();?> </form> </BODY>
output from the Introspection
new HTML_Template_Flexy_Element (
string $tag = ''
, array $attributes
= null
)
Flexy uses a single lightweight class to represent All HTML Tags, All the variables of the class are public, and you are encouraged to use them. And the methods provide generic assignment and conversion abilities.
To force the toHtml() method to generate XHTML, rather than standard HTML, use $element->setAttributes(array('flexy:xhtml'=>true)); or add flexy:xhtml="true" to the attribute of the element in the template.
$tag
- The name of the HTML Tag eg. img for <img ....
$attributes
-
Associative array of attributes, where key="value" is output when you turn it
in toHtml(), If you need to represent a attribute without a value, use TRUE as the value.
This also accepts a string in the format "href='/test.jpg' alt='test'", which will be
parsed into the attributes array of the object.
$element->tag
The name of the html element eg. img for <img...
$element->attributes
Attributes for the element
$element->children
All the sub elements inside this, can be any object that implements toHtml(), or a string.
$element->override
this value of thiswill be output when toHtml() is called, rather than the tags.
$element->prefix
string or object that implements toHtml() method, and is returned by toHtml() before the tag HTML
$element->suffix
string or object that implements toHtml() method, and is returned by toHtml() after the tag HTML
$element->value
when you create an element, that is to be merged later with a full definition, you can assign the value here, and during toHtml(), the toValue() method will be called and select options, checkboxes and input values will be correctly filled in.
throws no exceptions thrown
This function can not be called statically.
Using an element to change a template.
<?php
$form = new HTML_Template_Flexy();
$form->compile('some_file_name');
// create an instance (note you dont have to specify any details..)
$elements['test'] = new HTML_Template_Flexy_Element;
// change an attribute
$elements['test']->attributes['class'] = 'bold';
// sets the value
$elements['test']->setValue('Fred');
// wrap it with something
$elements['test']->prefix = '******';
$elements['test']->suffix = '!!!!!!';
// for the different types of elements:
$elements['test_textarea'] = new HTML_Template_Flexy_Element;
$elements['test_textarea']->setValue('Blogs');
// select options
$elements['test_select'] = new HTML_Template_Flexy_Element;
$elements['test_select']->setOptions( array(
'123' => 'a select option',
'1234' => 'another select option'
));
$elements['test_select']->setValue('1234');
// checkboxes
$elements['test_checkbox'] = new HTML_Template_Flexy_Element;
$elements['test_checkbox']->setValue(1);
$elements['test_checkbox']->setAttributes(array('flexy:xhtml'=>true));
// array type checkboxes..
$elements['test_checkbox_array[]'] = new HTML_Template_Flexy_Element;
$elements['test_checkbox_array[]']->setValue(array(1,2));
// radio buttons
$elements['test_radio'] = new HTML_Template_Flexy_Element;
$elements['test_radio']->setValue('yes');
$form->outputObject(new StdClass, $elements);
// in the example below, the new data you have added is to the existing attributes
?>
template example
<body> <form name="xxxx"> <input name="test" length="12"> <textarea name="test_textarea"></textarea> <select name="test_select"></select> <input name="test_checkbox" type="checkbox" value="1"> <input name="test_checkbox_array[]" type="checkbox" value="1" />1<br /> <input name="test_checkbox_array[]" type="checkbox" value="2" />2<br /> <input name="test_checkbox_array[]" type="checkbox" value="3" />3<br /> <input name="test_radio" type="radio" id="yes" value="yes" />yes<br /> <input name="test_radio" type="radio" id="no" value="no" />no<br /> </form> </body>
output from the Template
<body> <form name="xxxx"> ******<input name="test" length="12" class="bold" value="fred">!!!!!! <textarea name="test_textarea">blogs</textarea> <select name="test_select"> <option value="123">a selection option</option> <option value="1234" selected>another selection option</option> </select> <input name="test_checkbox" type="checkbox" value="1" checked="checked" /> <input name="test_checkbox_array[]" type="checkbox" value="1" checked>1<br /> <input name="test_checkbox_array[]" type="checkbox" value="2" checked>2<br /> <input name="test_checkbox_array[]" type="checkbox" value="3">3<br /> <input name="test_radio" type="radio" id="yes" value="yes" checked>yes<br /> <input name="test_radio" type="radio" id="no" value="no">no<br /> </form> </body>
$element->setValue (
mixed $value
)
This is used to set the values of form elements, results depend on the type of element
text,password inputs, buttons
Fills in the value attribute
checkboxs / radio buttons
adds a checked tag to the matching element
textarea
fills in the text area content
checkbox with arrays
adds checked to the matching elements
selects
adds selected to the
$value
The value to assign a form element, (use arrays for multiple selects or checkbox groups)
throws no exceptions thrown
This function can not be called statically.
$element->setOptions (
mixed $array
, boolean $noValue
= false
)
Used with HTML Selects, to fill in the options available
$array
the options to appear.
$noValue
don't use the keys from the array sent as the value= part of the option tag.
throws no exceptions thrown
This function can not be called statically.
$element->removeAttributes (
mixed $attrs
)
use this to remove attributes from the tag when the element is rendered, It may be easier to access the attributes array directly and assign the value to false.
$attributes
name or array of names to remove from tag
throws
This function can not be called statically.
HTML_Template_Flexy_Element::setAttributes (
mixed $attributes
)
Can be used to set attribute values, It is easier to set the attributes directly using the public attributes property.
$attributes
Either a typical HTML attribute string or an associative array
throws no exceptions thrown
This function can not be called statically.
string $element->toHtml (
)
returns the HTML to represent the object. If you wish to implement your own element, you only need to implement this method, and assign it to the element array..
throws no exceptions thrown
This function can not be called statically.
string $element->toHtmlnoClose (
)
Used by the template to represent form tags, which only the opening tag are rendered dynamically (so, all tags inside the tag are not forced to be dynamic).
throws no exceptions thrown
This function can not be called statically.
array HTML_Template_Flexy_Factory::freeze (
array $array
)
this probably needs more thought.. - it would probably need to merge the full tag info with types, to be usefull..
$ar = HTML_Element_Factory::freeze($ar);
$array
(return by refernce key(tag name) => HTML_Template_Flexy_Element
return Array of HTML_Template_Flexy_Elements
throws no exceptions thrown
Not documented yet.
array HTML_Template_Flexy_Factory::fromArray (
array $ar
, optional $ret = array()
)
This package is not documented yet.
$ar
key(tag name) => value
$ret
array key(tag name) => HTML_Element
return Array of HTML_Template_Flexy_Elements
throws no exceptions thrown
Not documented yet.
array HTML_Template_Flexy_Factory::fromArrayPrefixed (
array $prefix
, array $ar
, optional $ret = array()
)
This package is not documented yet.
$prefix
$ar
$ret
return Array of HTML_Template_Flexy_Elements
throws no exceptions thrown
This function can not be called statically.
array HTML_Template_Flexy_Factory::setErrors (
array $ret
, array $set
, string $format = '<span class="error">%s</span>'
)
HTML_Element_Factory::setErrors($elements,array('name','not long enough'));
$prefix
$ar
$ret
return Array of HTML_Template_Flexy_Element's
throws no exceptions thrown
This function can not be called statically.
array HTML_Template_Flexy_Factory::setRequired (
array $ret
, array $set
, string $format = '>span class="required"<*>/span<'
)
HTML_Element_Factory::setRequired($elements,array('name',true));
$prefix
$ar
$ret
return Array of HTML_Template_Flexy_Element's
throws no exceptions thrown
This function can not be called statically.
Usage (
{variable}
, {variable:h}
, {variable:u}
)
creates PHP code to echo a variable, with optional modifier. Modifiers are
by default variables are assumed to have calling scope, and are prefixed with $t->, however if they are inside a loop (foreach), then the variables created by the loop are added to the scope and the prefix is not added.
Accessing object variables can be done using a dot as separator:
{object.property}
,
calling methods is identical: {object.method()}
.
setting an object variable
$this->a = "hello >>"; $template->outputObject($this);
Outputting the variable
{a} {a:h} {a:u}
Compiled template
<?php echo htmlspecialchars($t->a); ?> <?php echo $t->a; ?> <?php echo urlencode($t->a); ?>
Simple ouput example
hello >> hello >> hello+%3E%3
Usage (
{method()}
, {method():h}
, {method():u}
, {object.method()}
, {method(with.a.variable)}
, {method(with.multiple,variables)}
, {method(with.variables,#and strings or literals#)}
)
creates an PHP method call, any with any number of arguments, as variables or literals (quoted in #). the return value is echoed, and passed by a modifier (like variables)
Including another template
class example_page { var $masterTemplate = "master.html"; var $bodyTemplate = "body.html"; function ucfirst($string) { return ucfirst($string); } function includeBody() { $template = new HTML_Template_Flexy(); $template->compile($this->bodyTemplate); $template->outputObject($this); } }
Calling includeBody in template, and ucfirst
{ucfirst(#this is the header#)} {includeBody():h} Footer Goes Here.
Compiled template
<?php echo htmlspecialchars($t->ucfirst("this is the header")); ?> <?php echo $t->includeBody(); ?> Footer Goes Here.
Output from the code
This is the header Hello World Footer Goes Here.
Usage (
{foreach:variable,key,value}
, {foreach:variable,value}
)
creates a foreach loop, needs an {end:} tag. note that the engine will add the variable to the scope, so they will not be prefixed with $t-> when used inside the loop.
string variable
-
relates to $object->variable
string key
-
creates a variable 'key' in the current scope.
string value
- optionally
creates a variable 'value' in the current scope. (as in $key=>$value)
Setting variables for foreach
$this->a = array( "dog" => "cat", "fire" => "water" ); $this->b = array('a','b','c'); $template->outputObject($this);
Foreach in template
{foreach:a,k,v} k is {k}, and v is {v} {end:} {foreach:b,v} v is {v} {end:}
Compiled template
<?php if (is_array($t->a)) foreach($t->a as $k => $v) { ?> k is <?php echo htmlspecialchars($k); ?>, and v is <?php echo htmlspecialchars($v); ?> <?php } ?> <?php if (is_array($t->a)) foreach($t->b as $v) { ?> v is <?php echo htmlspecialchars($v); ?> <?php } ?>
example output
k is dog, v is cat k is fire, v is water v is a v is b v is c
Usage (
{if:variable}
, {if:method()}
)
creates an if statement, with the argument as a variable or method. You must close an if statement with an {end:} statement, and you may use {else:} with it.
Setting variables for if
class example { function showDog() { return true; } function output() { $this->showStuff = true; ......... $template->outputObject($this); } }
The template
{if:showStuff}Hello{end:} {if:showDog()}Doggy{end:}
Compiled template
<?php if ($t->showStuff) { ?>Hello<?php } ?> <?php if ($t->showDog()) { ?>Doggy<?php } ?>
The output
Hello Doggy
Usage (
{end:}
)
adds a closing brace in PHP so that blocks of conditional HTML are shown correctly. You do not need to use {end:} tags if you are using HTML tag attributes like IF="a,b", as the closing HTML tag will indicate that.
Setting variables for if
class example { function showDog() { return true; } function output() { $this->showStuff = true; ......... $template->outputObject($this); } }
The template
{if:showStuff}Hello{end:} {if:showDog()}Doggy{end:}
The compiled template
<?php if ($t->showStuff) { ?>Hello<?php } ?> <?php if ($t->showDog()) { ?>Doggy<?php } ?>
The output
Hello Doggy
Usage (
{else:}
)
adds an else statement in PHP so that blocks of conditional HTML are shown correctly.
Setting variables for if
class example { function output() { $this->showStuff = false; ......... $template->outputObject($this); } }
Else in template
{if:showStuff}Hello{else:}World{end:}
Compiled template
<?php if ($t->showStuff) { ?>Hello<?php } else { ?>World<<?php } ?>
The output
World
Usage (
<FORM NAME="name">
)
By default, all forms are converted to HTML_Template_Flexy_Elements, so they can be altered at runtime.
Using an element to change a template.
<?php
$form = new HTML_Template_Flexy();
$form->compile('some_file_name');
// create an instance (note you dont have to specify any details..)
$elements['theform'] = new HTML_Template_Flexy_Element;
// change an attribute
$elements['theform']->attributes['action'] = 'http://pear.php.net';
//
// for the different types of elements:
$elements['test_textarea'] = new HTML_Template_Flexy_Element;
$elements['test_textarea']->setValue('Blogs');
// select options
$elements['test_select'] = new HTML_Template_Flexy_Element;
$elements['test_select']->setOptions( array(
'123' => 'a select option',
'1234' => 'another select option'
));
$elements['test_select']->setValue('1234');
// checkboxes
$elements['test_checkbox'] = new HTML_Template_Flexy_Element;
$elements['test_checkbox']->setValue(1);
// array type checkboxes..
$elements['test_checkbox_array[]'] = new HTML_Template_Flexy_Element;
$elements['test_checkbox_array[]']->setValue(array(1,2));
// radio buttons
$val = 'yes';
$elements['test_radio'] = new HTML_Template_Flexy_Element;
// if you have a default - you may want default to using it..
$elements['test_radio']->setValue($val != 'no' ? $val : 'no');
$form->outputObject(new StdClass, $elements);
// in the example below, the new data you have added is to the existing attributes
?>
template example
<BODY> <FORM name="theform"> <TEXTAREA name="test_textarea"></TEXTAREA> <SELECT name="test_select"></SELECT> <input name="test_checkbox" type="checkbox" value="1"> <input name="test_checkbox_array[]" type="checkbox" value="1">1<BR> <input name="test_checkbox_array[]" type="checkbox" value="2">2<BR> <input name="test_checkbox_array[]" type="checkbox" value="3">3<BR> <!-- you need to use id's --> <input name="test_radio" type="radio" id="radio_yes" value="yes">yes<BR> <input name="test_radio" type="radio" id="radio_no" value="no">no<BR> </FORM> </BODY>
output from the Template
<BODY> <FORM name="theform" action="http://pear.php.net"> <TEXTAREA name="test_textarea">Blogs</TEXTAREA> <SELECT name="test_select"> <option value="123">a selection option</option> <option value="1234" selected>another selection option</option> </SELECT> <input name="test_checkbox" type="checkbox" value="1" checked> <input name="test_checkbox_array[]" type="checkbox" value="1" checked>1<BR> <input name="test_checkbox_array[]" type="checkbox" value="2" checked>2<BR> <input name="test_checkbox_array[]" type="checkbox" value="3">3<BR> <input name="test_radio" type="radio" value="yes" id="radio_yes" checked>yes<BR> <input name="test_radio" type="radio" value="no" id="radio_no">no<BR> </FORM> </BODY>
Usage (
<INPUT NAME="name">
)
fills in the form with values based on the form name and the tag name. If flexyignore is used, it is left alone (or if the body or form has a flexyignore tag it will be left alone).
Using an element to change a template.
<?php
$form = new HTML_Template_Flexy();
$form->compile('some_file_name');
// create an instance (note you dont have to specify any details..)
$elements['test'] = new HTML_Template_Flexy_Element;
// change an attribute
$elements['test']->attributes['class'] = 'bold';
// sets the value
$elements['test']->setValue('Fred');
// wrap it with something
$elements['test']->prefix = '******';
$elements['test']->suffix = '!!!!!!';
$form->outputObject(new StdClass, $elements);
// in the example below, the new data you have added is to the existing attributes
?>
template example
<BODY> <FORM name="XXXX" flexy:ignoreonly="yes"> <INPUT name="test" length="12"> </FORM> </BODY>
compiled template
<BODY>
<FORM name="XXXX">
<?php echo $this->elements['test']->toHtml();?>
</FORM>
</BODY>
output from the Template
<BODY> <FORM name="XXXX"> ******<INPUT name="test" length="12" class="bold" value="Fred">!!!!!! </FORM> </BODY>
Usage (
<TEXTAREA NAME="name">
)
fills in the form with values based on the form name and the tag name. If flexyignore is used, it is left alone (or if the body or form has a flexyignore tag it will be left alone).
Using an element to change a template.
<?php
$form = new HTML_Template_Flexy();
$form->compile('some_file_name');
// create an instance (note you dont have to specify any details..)
$elements['test'] = new HTML_Template_Flexy_Element;
// change an attribute
$elements['test']->attributes['class'] = 'bold';
// sets the value
$elements['test']->setValue('Fred');
$form->outputObject(new StdClass, $elements);
// in the example below, the new data you have added is to the existing attributes
?>
template example
<BODY> <FORM name="XXXX" flexy:ignoreonly="yes"> <textarea name="test"></textarea> </FORM> </BODY>
compiled template
<BODY> <FORM name="XXXX"> <?php echo $this->elements['test']->toHtml();?> </FORM> </BODY>
output from the Template
<BODY> <FORM name="XXXX"> <textarea name="test" class="bold">Fred</textarea> </FORM> </BODY>
Usage (
<SELECT NAME="name">
)
fills in the select values based on the form name and the tag name and adds code to check if the object variable matches them. If flexyignore is used, it is left alone. If static is set, the currently defined options will be used.
Using an element to change a template.
<?php
$form = new HTML_Template_Flexy();
$form->compile('some_file_name');
// create an instance (note you dont have to specify any details..)
// select options
$elements['test_select'] = new HTML_Template_Flexy_Element;
$elements['test_select']->setOptions( array(
'123' => 'a select option',
'1234' => 'another select option'
));
$elements['test_select']->setValue('1234');
$form->outputObject(new StdClass, $elements);
// in the example below, the new data you have added is to the existing attributes
?>
template example
<BODY> <FORM name="theform" flexy:ignoreonly="yes"> <SELECT name="test_select"></SELECT> </FORM> </BODY>
compiled template
<BODY>
<FORM name="theform">
<?php echo $this->elements['test_select']->toHtml();?>
</FORM>
</BODY>
output from the Template
<BODY> <FORM name="theform"> <SELECT name="test_select"> <option value="123">a selection option</option> <option value="1234" selected>another selection option</option> </SELECT> </FORM> </BODY>
Usage (
flexy:if="variable"
, flexy:if="method()"
, flexy:if="object.method()"
)
creates an if condition surrounding the tag that it's included in.
string variable
-
relates to $object->variable
string method
-
relates to $object->method()
string object.method
relates to $object->object->method()
Setting variables for foreach
class output { function hasTest() { return false; } function run() { $this->a = true; $this->message = 'oops' $template->outputObject($this); } }
flexy:if in template
<a href="{baseURL}/somepath.html" flexy:if="a">this is the a link</a> <a href="{baseURL}/somepath.html" flexy:if="!a">this is not the a link</a> <b flexy:if="hasTest()">hasTest is true</b> <b flexy:if="!hasTest()">hasTest is false</b> <span flexy:if="message" class="error">{message}</span>
Compiled template
<?php if ($t->a) { ?><A HREF="<?php echo htmlspecialchars($t->baseURL); ?>/somepath.html">this is the a link</A><?php } ?> <?php if (!$t->a) { ?><A HREF="<?php echo htmlspecialchars($t->baseURL); ?>/somepath.html">this is not the a link</A><?php } ?> <?php if (isset($t) && method_exists($t,'hasTest')) if ($t->hasTest()) { ?><B>hasTest is true</B><?php } ?> <?php if (isset($t) && method_exists($t,'hasTest')) if (!$t->hasTest()) { ?><B>hasTest is false</B><?php } ?> <?php if ($t->message) { ?><SPAN CLASS="error"><?php echo htmlspecialchars($t->message); ?></SPAN><?php } ?>
Simple ouput example
this is the a link hasTest is false oops
Usage (
flexy:foreach="variable,key,value"
, flexy:foreach="variable,value"
)
creates a foreach loop, around the tag and close tag.
string variable
-
relates to $object->variable
string key
-
creates a variable 'key' in the current scope.
string value
- optionally
creates a variable 'value' in the current scope. (as in $key=>$value)
Setting variables for foreach
$this->a = array( "dog" => "cat", "fire" => "water" ); $this->b = array('a','b','c'); $template->outputObject($this);
Foreach in template
<table> <tr flexy:foreach="a,k,v"> <td>k is {k}, and v is {v}</td> </tr> </table> <table> <tr flexy:foreach="b,v"> <td>v is {v}</td> </tr> </table>
Compiled template
<table> <?php if (is_array($t->a)) foreach($t->a as $k => $v) { ?><tr> <td>k is <?php echo htmlspecialchars($t->k); ?>, and v is <?php echo htmlspecialchars($t->v); ?></td> </tr><?php } ?> </table> <table> <?php if (is_array($t->b)) foreach($t->b as $v) { ?><tr> <td>v is <?php echo htmlspecialchars($t->v); ?></td> </tr><?php } ?> </table>
Simple ouput example
k is dog, v is cat k is fire, V is water v is a v is b v is c
Usage (
flexy:start="here"
)
Tells the generator to start outputing using this tag. This can be useful if you want to edit the template in a editor that expects a head/footer, and you can list the available tags in the comments at the top of the page.
The actual value of the tag is not relivant.
Template with flexy:start
<HTML> <HEAD></HEAD> <BODY> <H1>This is an example</H1> <FORM name="input" flexy:start="yes"> <INPUT name="hello" flexy:ignore="yes"> </FORM> </BODY> </HTML>
Compiled template
<FORM NAME="input"> <INPUT NAME="hello"> </FORM>
Usage (
flexy:startchildren="here"
)
Tells the generator to start outputing using the children of this tag. This can be useful if you want to edit the template in a editor that expects a head/footer, normally adding this to body
The actual value of the tag is not relivant.
Template with flexystartchildren
<HTML> <HEAD></HEAD> <BODY flexy:startchildren="here"> <H1>This is an example</H1> <FORM name="input" flexy:ignoreonly="yes"> <INPUT name="hello" flexy:ignore="yes"> </FORM> </BODY> </HTML>
Compiled template
<H1>This is an example</H1> <FORM NAME="input"> <INPUT NAME="hello"> </FORM>
Usage (
flexy:ignore="yes"
)
Tells the generator not to replace form elements with PHP code. Can be used with a Form Tag, or individual elements.
<input>
s and<textarea>
s withflexy:include
orflexy:ignore
: If you are turning off flexy element creation withflexy:ignore="yes"
, then this is not inherited by included templates, and you need to add that tag to the included template as well.
Template with flexy:ignore
<form name="theform1"> <input name="theinput1"> <input name="theinput2" value="dummy"> </form> <form name="theform2" flexy:ignore> <input name="theinput3" value="dummy"> <input name="theinput4" value="dummy"> </form> <form name="theform3"> <input name="theinput5" value="dummy" flexy:ignore> <input name="theinput6" value="dummy"> </form> <form name="theform4" flexy:ignoreonly="yes"> <input name="theinput7" value="dummy"> <input name="theinput8" value="dummy"> </form>
Compiled template
<?php echo $this->elements['theform1']->toHtmlnoClose();?> <?php echo $this->elements['theinput1']->toHtml();?> <?php echo $this->elements['theinput2']->toHtml();?> </form> <form name="theform2"> <input name="theinput3" value="dummy"> <input name="theinput4" value="dummy"> </form> <?php echo $this->elements['theform3']->toHtmlnoClose();?> <input name="theinput5" value="dummy" flexy:ignore> <?php echo $this->elements['theinput6']->toHtml();?> </form> <form name="theform4"> <?php echo $this->elements['theinput7']->toHtml();?> <?php echo $this->elements['theinput8']->toHtml();?> </form>
Usage (
flexy:nameuses="variable" name="sometag[%s]"
)
If you have a form with multiple input boxes (eg. group members), which are dynamically generated, then this attribute can be used to generate flexy elements based on sprintf'ing the variable value into the name attribute value.
Template with flexy:ignore
<html> <head> <title>Example of name Uses</title> </head> <body> <form name="formtest"> <span flexy:foreach="data,key,row"> {key}: <input name="data%s" flexy:nameuses="key" type="hidden" size="10"><br> </span> </form> </body> </html>
Backend Code Snippet
<?php
$this->data = array();
$elem = array();
$elem['formtest'] = &new HTML_Template_Flexy_Element();
$elem['formtest']->attributes['method'] = 'post';
$elem['formtest']->attributes['action'] = 'test';
for($i = 0; $i < 10; $i++) {
$this->data[$i] = $i;
$elem["data$i"] = new HTML_Template_Flexy_Element();
$elem["data$i"]->attributes['type'] = 'text';
$elem["data$i"]->attributes['size'] = $i;
}
$flexy->outputObject($this, $elem);
?>
Compiled template
<html>
<head>
<title>Example of name Uses</title>
</head>
<body>
<?php echo $this->elements['formtest']->toHtmlnoClose();?>
<?php if ($this->options['strict'] || (is_array($t->data) || is_object($t->data)))
foreach($t->data as $key => $row) {?>
<?php echo htmlspecialchars($key);?>: <?php if (!isset($this->elements[sprintf('data%s',$key)])) $this->elements[sprintf('data%s',$key)]= $this->elements['data%s'];
$this->elements[sprintf('data%s',$key)] = $this->mergeElement($this->elements['data%s'],$this->elements[sprintf('data%s',$key)]);
$this->elements[sprintf('data%s',$key)]->attributes['name'] = sprintf('data%s',$key);
echo $this->elements[sprintf('data%s',$key)]->toHtml();?><br>
<?php }?>
</form>
</body>
</html>
Resulting Output
<html> <head> <title>test for PEAR bug#4683</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <form name="formtest" method="post" action="test"> 0: <input name="data0" type="text" size="0"><br> 1: <input name="data1" type="text" size="1"><br> 2: <input name="data2" type="text" size="2"><br> 3: <input name="data3" type="text" size="3"><br> 4: <input name="data4" type="text" size="4"><br> 5: <input name="data5" type="text" size="5"><br> 6: <input name="data6" type="text" size="6"><br> 7: <input name="data7" type="text" size="7"><br> 8: <input name="data8" type="text" size="8"><br> 9: <input name="data9" type="text" size="9"><br> </form> </body> </html>
Usage (
<flexy:tojavascript JSVAR="PHPVAR" ...>
)
To reduce the "WTF" effect of magically having javascript code that looks like flexy tags completely broken by the template engine, Flexy deliberately turns the parser OFF when dealing with script contents (see Configuration Options for details of PHP in script tags)
As a result of this, it is not possible to put any flexy tags within blocks of javascript code. the flexy:tojavascript tag solves this in a way that enables javascript to be tested seperatly from the application, and also serves to encourage better coding practices. (eg. seperating your code with distinct lines of communication)
This feature depends on PEAR's HTML_Javascript Library
A template with javascript and flexy:tojavasscript
<?php
<html><head>
<title>Example</title>
<flexy:toJavascript
flexy:prefix="test_abc_"
abcg="xyz"
abcd="xyz"
srcXxx="xyz"
>
<!--
We can use the inner contents for testing the template in a browser.
The template will remove these contents when it is compiled.
-->
<script type="text/javascript">
var test_abc_abcg = '123';
var test_abc_abcd = '123';
var test_abc_srcXxx = '123';
</script>
</flexy:toJavascript>
<flexy:toJavascript abcg="xyz">
<script type="text/javascript">
var xyz = '123';
</script>
</flexy:toJavascript>
<body>
<p>Example of flexy:toJavascript with default values.</p>
</body></html>
?>
compiled template
<html><head>
<title>Example</title>
<?php require_once 'HTML/Javascript/Convert.php';?>
<script type='text/javascript'>
<?php $__tmp = HTML_Javascript_Convert::convertVar($t->xyz,'test_abc_abcg',true);
echo (is_a($__tmp,"PEAR_Error")) ? ("<pre>".print_r($__tmp,true)."</pre>") : $__tmp;?>
<?php $__tmp = HTML_Javascript_Convert::convertVar($t->xyz,'test_abc_abcd',true);
echo (is_a($__tmp,"PEAR_Error")) ? ("<pre>".print_r($__tmp,true)."</pre>") : $__tmp;?>
<?php $__tmp = HTML_Javascript_Convert::convertVar($t->xyz,'test_abc_srcXxx',true);
echo (is_a($__tmp,"PEAR_Error")) ? ("<pre>".print_r($__tmp,true)."</pre>") : $__tmp;?>
</script>
?php require_once 'HTML/Javascript/Convert.php';?>
<script type='text/javascript'>
<?php $__tmp = HTML_Javascript_Convert::convertVar($t->xyz,'abcg',true);
echo (is_a($__tmp,"PEAR_Error")) ? ("<pre>".print_r($__tmp,true)."</pre>") : $__tmp;?>
</script>
<body>
<p>Example of flexy:toJavascript with default values.</p>
</body></html>
output from the Template (with no values set)
<html><head> <title>Example</title> <script type='text/javascript'> test_abc_abcg = null; test_abc_abcd = null; test_abc_srcXxx = null; </script> <script type='text/javascript'> abcg = null; </script> <body> <p>Example of flexy:toJavascript with default values.</p> </body></html>
A template consists of text and special labeled blocks and placeholders. The content of blocks can be re-used and parsed multiple times with different placeholder values.
A typical template
<html> <body> Userlist <table> <!-- BEGIN row --> <tr> <td>{USERNAME}</td> <td>{EMAIL}</td> </tr> <!-- END row --> </table> </body> </html>
Placeholders can be defined in templates and are filled from PHP code with content. The format of placeholder up to version (1.1.x) is
{[0-9A-Za-z_-]+}
Since version 1.2.x dots are allowed, too.
{[\.0-9A-Za-z_-]+}
This means, the name of the placeholder can consist of upper- and lowercase letters, underscores and hypens. The name must be placed between curly brackets without any spaces. Valid names are i.e.:
Valid names since version 1.2.x
Non-valid names are i.e.
The format of a block is
Since version 1.2.x dots are allowed in block definitions
The rules for the block name are the same like for placeholders. In contrast to placeholders the spaces in the block markup are required.
The nesting of blocks is permitted, but be careful while parsing. You have to set and parse the deepest inner block first and then set and parse from inner to outer.
In IT the whole template file itself is nested in a meta block called "__global__". Most block-related functions use this block name as default.
The template
The script
<?php
require_once "HTML/Template/IT.php";
$data = array
(
"0" => array("Stig", "Bakken"),
"1" => array("Martin", "Jansen"),
"2" => array("Alexander", "Merz")
);
$tpl = new HTML_Template_IT("./templates");
$tpl->loadTemplatefile("main.tpl.htm", true, true);
foreach($data as $name) {
foreach($name as $cell) {
// Assign data to the inner block
$tpl->setCurrentBlock("cell") ;
$tpl->setVariable("DATA", $cell) ;
$tpl->parseCurrentBlock("cell") ;
}
// parse outter block
$tpl->parse("row");
}
// print the output
$tpl->show();
?>
The output
void
HTML_Template_IT::HTML_Template_IT (
string
$root = '.'
)
Constructor. Create a new instance of HTML_Template_IT and sets the search path for templates.
string $root
-
path to the directory of the template files
object - a new HTML_Template_IT object
This function can be called statically.
string
HTML_Template_IT::get (
string
$block = "__global__"
)
This functions return a block with all replacements done.
string $block
-
The block to return. If not used, the whole template
is returned.
string
-
the template with all replacements done.
Error code | Error message | Reason | Solution |
---|---|---|---|
IT_BLOCK_NOT_FOUND |
"
Cannot find this block block
"
|
The given block does not exists. | Check for typing mistakes in the argument. |
This function can not be called statically.
array
HTML_Template_IT::getGlobalvariables (
)
Gets an array of all global variables in the variable cache. Only variables that are filled using HTML_Template_IT::setVariable() are returned. The returned has two values. The first values holds an array with the names of the global variables, the second value holds an array with all the values.
array
-
An array. The key 0 holds an array with the names of all filled variables, the key 1 holds an array with names of the according values.
Script
<?php
require_once("HTML/Template/IT.php");
$template = <<<EOF
<!-- BEGIN a -->
Hello {username}
<!-- END a -->
Welcome to {page},
You are visitor number {visitorcount}.
EOF;
$tpl = new HTML_Template_IT('.');
$tpl->setTemplate($template);
// set the {page} variable. It will be returned by getGlobalvariables then.
$tpl->setVariable("page", "http://example.com");
$tpl->setVariable("username", "foo");
// getGlobalvariables won't return {username} as it is not a global variable
// It won't return {visitorcount} as it is not set
print_r($tpl->getGlobalvariables());
?>
The output
This function can not be called statically.
boolean
HTML_Template_IT::loadTemplatefile
(
string
$filename
, boolean
$removeUnknownVariables
= true
, boolean
$removeEmptyBlocks
= true
)
Loads a template from a file and generates internal lists for blocks and variables.
string $filename
-
file to load
boolean $removeUnknownVariables
-
if TRUE, not substituted placeholders in a block will
be removed
boolean $removeEmptyBlocks
-
if TRUE, not touched blocks will be removed. Blocks can be touched with HTML_Template_IT::touchBlock().
boolean
-
Returns TRUE on success, FALSE on failure.
Templatefile main.tpl.htm
<html> <body> User {USERNAME} logged in successfull as {ROLE}. </body> </html>
Script with $removeUnknownVariables
= FALSE
<?php
require_once 'HTML/Template/IT.php';
$tpl = new HTML_Template_IT('.');
$tpl->loadTemplatefile ('main.tpl.htm', false, false);
$tpl->setVariable ('USERNAME', 'foo');
// Placeholder ROLE is not set
$tpl->show();
?>
Output
Script with $removeUnknownVariables
= TRUE
<?php
require_once 'HTML/Template/IT.php';
$tpl = new HTML_Template_IT('.');
$tpl->loadTemplatefile ('main.tpl.htm', true, true);
$tpl->setVariable ('USERNAME', 'foo');
// Placeholder ROLE is not set, but $removeUnknownVariables is set to true.
$tpl->show();
?>
Output
This function can not be called statically.
void
HTML_Template_IT::parse (
string
$block = "__global__"
, boolean
$flag_recursion
= false
)
Parses the defined block, performs all substitutions and appends the result to already parsed blocks.
string $block
-
block to parse. When not set the complete template is used.
boolean $flag_recursion
-
Used internally. Can be ignored
boolean
-
Returns TRUE if there was no placeholder to substitute, otherwise FALSE or IT_Error.
The template cvsnames.tpl.htm
<html> <table> <!-- BEGIN row --> <tr> <td> {CVS_USERNAME} </td> <td> {REALNAME} </td> </tr> <!-- END row --> </table> </html>
The script
<?php
require_once "HTML/Template/IT.php";
$data = array
(
"0" => array("cvs_username" => "pajoye",
"realname" => "Pierre-Alain Joye"),
"1" => array("cvs_username" => "dsp",
"realname" => "David Soria Parra")
);
$tpl = new HTML_Template_IT("./templates");
$tpl->loadTemplatefile("cvsnames.tpl.htm", true, true);
foreach($data as $name) {
$tpl->setVariable("CVS_USERNAME", $name["cvs_username"]);
$tpl->setVariable("REALNAME", $name["realname"]);
$tpl->parse("row");
}
// show() parses the __global__ block and
// print the output
$tpl->show();
?>
Error code | Error message | Reason | Solution |
---|---|---|---|
IT_BLOCK_NOT_FOUND |
"
Cannot find this block block
"
|
The given block does not exists. | Check for typing mistakes in the argument. |
This function can not be called statically.
void
HTML_Template_IT::parseCurrentBlock (
)
Parses the current block. The current block can be set with HTML_Template_IT::setCurrentBlock().
boolean
-
Returns TRUE if there was no placeholder to substitute, otherwise FALSE or IT_Error.
The template cvsnames.tpl.htm
<html> <table> <!-- BEGIN row --> <tr> <td> {CVS_USERNAME} </td> <td> {REALNAME} </td> </tr> <!-- END row --> </table> </html>
Script
<?php
require_once "HTML/Template/IT.php";
$data = array
(
"0" => array("cvs_username" => "pajoye",
"realname" => "Pierre-Alain Joye"),
"1" => array("cvs_username" => "dsp",
"realname" => "David Soria Parra")
);
$tpl = new HTML_Template_IT("./templates");
$tpl->loadTemplatefile("cvsnames.tpl.htm", true, true);
// set the current block, which can now be used with parseCurrentBlock()
$tpl->setCurrentBlock("row");
foreach($data as $name) {
// Assign data to the inner block
$tpl->setVariable("CVS_USERNAME", $name["cvs_username"]);
$tpl->setVariable("REALNAME", $name["realname"]);
// parse the current set block
$tpl->parseCurrentBlock();
}
// show() parses the __global__ block and
// print the output
$tpl->show();
?>
Error code | Error message | Reason | Solution |
---|---|---|---|
IT_BLOCK_NOT_FOUND |
"
Cannot find this block block
"
|
The given block does not exists. | Check for typing mistakes in the argument. |
This function can not be called statically.
boolean
HTML_Template_IT::setCurrentBlock (
string
$block = "__global__"
)
Sets the name of the current block, where placeholder should be substituted. The current block can be parsed with HTML_Template_IT::parseCurrentTemplate().
string $block
-
block to set, if not given, the complete template is set
boolean
-
TRUE, if found and succcessful set, otherwise IT_Error will be returned.
Error code | Error message | Reason | Solution |
---|---|---|---|
IT_BLOCK_NOT_FOUND |
"
Cannot find this block block
"
|
The given block does not exists. | Check for typing mistakes in the argument. |
This function can not be called statically.
void
HTML_Template_IT::setRoot (
string $root
)
Sets the path to the template directory where HTML_Template_IT::loadTemplatefile() searches for templates. Every filename is prefixed with the given string.
string $root
-
Path to the template directory.
Directorytree.
script - testscript.php
<?php
/*
* testscipt.php - sets a random design
*/
require_once "HTML/Template/IT.php";
$tpl = new HTML_Template_IT();
$randomNumber = (int) round(rand(0,1));
switch ($randomNumber)
{
case 0:
// Set root to design01 so the called main.tpl.htm is searched there
$tpl->setRoot("./templates/design01");
break;
case 1:
default:
// Set root to design02 so the called main.tpl.htm is searched there
$tpl->setRoot("./templates/design02");
break;
}
/* Loads either ./templates/design01/main.tpl.htm
* or ./templates/design02/main.tpl.htm depending on the root directory set
* with setRoot in the switch */
$tpl->loadTemplatefile("main.tpl.htm", true, true);
/* ... assign variables .. */
// show
$tpl->show();
?>
This function can not be called statically.
int
HTML_Template_IT::setOption (
string
$option
, mixed
$value
)
Sets an option. Please notice that changing some option might result in an unexpected behaviour of HTML_Template_IT.
int
-
Returns 1 on success, otherwise an IT_Error object.
mixed $option
-
Option identifier
mixed $value
-
New value of the option
Currently
setOption
allows to set only two options. Instead of using this method, access the properties directly on the template object.
Option | Default value | Description |
---|---|---|
removeUnknownVariables
|
TRUE | If TRUE all template variables, which are not filled, are removed while parsing. This option is usually set by calling HTML_Template_IT::loadTemplatefile() or HTML_Template_IT::setTemplate(). |
removeEmptyBlocks
|
TRUE | If TRUE all blocks not containing any filled template variables are removed. This option is usually set by calling HTML_Template_IT::loadTemplatefile() or HTML_Template_IT::setTemplate(). |
clearCache
|
FALSE | If TRUE parsed blocks are not cached. If you don't know exactly what you do, just leave the default value. |
clearCacheOnParse
|
FALSE | If TRUE the variable cache will be cleaned after parsing. If you don't know exactly what you do, just leave the default value. |
openingDelimiter
|
'{'
|
Defines the character, every template variable has to start with. If you change this value, you have to call init() to reinitialise the template. If you don't know exactly what you do, just leave the default value. |
closingDelimiter
|
'}'
|
Defines the character, every template variable has to end with. If you change this value, you have to call init() to reinitialise the template. If you don't know exactly what you do, just leave the default value. |
blocknameRegExp
|
'[\.0-9A-Za-z_-]+'
|
The regular expression, thats used to parse block names. If you change this value, you have to call init() to reinitialise the template. If you don't know exactly what you do, just leave the default value. |
variablenameRegExp
|
'[\.0-9A-Za-z_-]+'
|
The regular expression, thats used to parse template variable names. If you change this value, you have to call init() to reinitialise the template. If you don't know exactly what you do, just leave the default value. |
This function can not be called statically.
boolean
HTML_Template_IT::setTemplate (
string $template
, boolean
$removeUnkownVariables
= true
, boolean
$removeEmptyBlocks
= true
)
Loads template from a string and controls the behavior in case of unused variables and blocks
string $template
-
The content of the template.
boolean $removeUnknowVariables
-
if TRUE, not substituted placeholders in a block will
be removed
boolean $removeEmptyBlocks
-
if TRUE, not touched blocks will be removed
boolean
-
Returns TRUE on success, FALSE on failure.
Script
<?php
require_once "HTML/Template/IT.php";
$data = array
(
"0" => array("Stig", "Bakken"),
"1" => array("Martin", "Jansen"),
"2" => array("Alexander", "Merz")
);
$templateString = <<<EOD
<html>
<table>
<!-- BEGIN row -->
<tr>
<!-- BEGIN cell -->
<td>
{DATA}
</td>
<!-- END cell -->
</tr>
<!-- END row -->
</table>
</html>
EOD;
$tpl = new HTML_Template_IT();
$tpl->setTemplate($templateString, true, true);
foreach($data as $name) {
foreach($name as $cell) {
// Assign data to the inner block
$tpl->setCurrentBlock("cell") ;
$tpl->setVariable("DATA", $cell) ;
$tpl->parseCurrentBlock("cell") ;
}
// parse outter block
$tpl->parse("row");
}
// show
$tpl->show();
?>
This function can not be called statically.
void
HTML_Template_IT::setVariable (
mixed
$placeholder
, mixed
$variable = ""
)
Set the value of a variable in the current template block.
If $placeholder
is an array, the key of an element
is treated as a placeholder name while the value is treated as its substitution.
mixed $placeholder
-
name of the placeholder to
substitute or a array with the placeholder as key and the data to
assign as value.
mixed $variable
-
if $placeholder
is not a array, the
value to assign to the placeholder.
Template - cvsnames.tpl.htm
<html> <table> <!-- BEGIN row --> <tr> <td> {CVS_USERNAME} </td> <td> {REALNAME} </td> <td> <ul> <!-- BEGIN project_row --> <li>{PROJECT}</li> <!-- END project_row --> </ul> </td> </tr> <!-- END row --> </table> </html>
Script
<?php
require_once "HTML/Template/IT.php";
$data = array
(
"0" => array("cvs_username" => "pajoye",
"realname" => "Pierre-Alain Joye",
"projects" => array("PEAR",
"PEAR_Frontend_Web",
"PEAR_RemoteInstaller",
"HTML_Template_IT")),
"1" => array("cvs_username" => "dsp",
"realname" => "David Soria Parra",
"projects" => array("HTML_Template_IT"))
);
$tpl = new HTML_Template_IT("./templates");
$tpl->loadTemplatefile("cvsnames.tpl.htm", true, true);
foreach($data as $name) {
// Assign data to the inner block
$tpl->setCurrentBlock("project_row");
foreach ($name['projects'] as $projectname) {
$tpl->setVariable("PROJECT", $projectname);
$tpl->parseCurrentBlock();
}
// use the possbility to set the placeholders using an assoc array
$tpl->setVariable(
array("CVS_USERNAME" => $name["cvs_username"],
"REALNAME" => $name["realname"])
);
$tpl->parse("row");
}
// show() parses the __global__ block and
// print the output
$tpl->show();
?>
This function can not be called statically.
void
HTML_Template_IT::show (
string $block
)
This functions prints a block with all replacements done.
string $block
-
The block to return. If not used, the whole template
is returned.
Error code | Error message | Reason | Solution |
---|---|---|---|
IT_BLOCK_NOT_FOUND |
"
Cannot find this block block
"
|
The given block does not exists. | Check for typing mistakes in the argument. |
This function can not be called statically.
boolean
HTML_Template_IT::touchBlock (
string
$block
)
Preserves an empty template block, even if $removeEmptyBlocks
is TRUE and
no substition of placeholders took place.
string $block
-
block to preserve
boolean
-
TRUE, if block was found, otherwise IT_Error.
Template - login.tpl.htm
<html> <body> <!-- BEGIN login_successfull --> You have logged in successfully! <!-- END login_successfull --> <!-- BEGIN login_failed --> Login failed <!-- END login_failed --> </body> </html>
Script
<?php
require_once "HTML/Template/IT.php";
// Remove blocks with no placeholders, or no placeholders set ($removeEmptyBlocks=true)
$tpl->loadTemplatefile("login.tpl.htm", true, true);
// hypothetical
if (login_successfull($username, $password)) {
// print login_successfull block.
// login_failed is removed, due to $removeEmptyBlocks = true
$tpl->touchBlock("login_successfull");
} else {
$tpl->touchBlock("login_failed");
}
$tpl->show();
?>
Error code | Error message | Reason | Solution |
---|---|---|---|
IT_BLOCK_NOT_FOUND |
"
Cannot find this block block
"
|
The given block does not exists. | Check for typing mistakes in the argument. |
This function can not be called statically.
HTML_Template_PHPLIB is the PEARified version of the once-so-famous PHPLIB template class. It provides variables and repeatable blocks. The original PHPLIB code can be found at phplib.sf.net.
When using HTML_Template_PHPLIB to generate HTML or other code from templates, you do the following steps:
Create template class instance
Load template file
Define blocks
Set variables, parse blocks
Finish and output
Variables are placeholders in your HTML code
that can be replaced with dynamic values provided from database or
calculated in your code. An example for a variable is
{CODE_AUTHOR}
: You enclose the variable
name in curly braces. The name of the variable may contain
any characters, except spaces, tabs and newlines.
Blocks surround certain pieces of HTML code
and can be re-used, e.g. a <tr>
row in a table.
Blocks are defined by using HTML comments, containing
BEGIN
or END
, and the block name.
Example:
<table> <caption>Authors</caption> <thead> <tr><th>Name</th><th>Email</th></tr> </thead> <tbody> <!-- BEGIN authorline --> <tr><td>{AUTHOR_NAME}</td><td>{AUTHOR_EMAIL}</td></tr> <!-- END authorline --> </tbody> </table>
Template file: authors.tpl
<html> <head><title>{PAGE_TITLE}</title></head> <body> <table> <caption>Authors</caption> <thead> <tr><th>Name</th><th>Email</th></tr> </thead> <tfoot> <tr><td colspan="2">{NUM_AUTHORS}</td></tr> </tfoot> <tbody> <!-- BEGIN authorline --> <tr><td>{AUTHOR_NAME}</td><td>{AUTHOR_EMAIL}</td></tr> <!-- END authorline --> </tbody> </table> </body> </html>
PHP code: authors.php
<?php
//we want to display this author list
$authors = array(
'Christian Weiske' => 'cweiske@php.net',
'Bjoern Schotte' => 'schotte@mayflower.de'
);
require_once 'HTML/Template/PHPLIB.php';
//create template object
$t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
//load file
$t->setFile('authors', 'authors.tpl');
//set block
$t->setBlock('authors', 'authorline', 'authorline_ref');
//set some variables
$t->setVar('NUM_AUTHORS', count($authors));
$t->setVar('PAGE_TITLE', 'Code authors as of ' . date('Y-m-d'));
//display the authors
foreach ($authors as $name => $email) {
$t->setVar('AUTHOR_NAME', $name);
$t->setVar('AUTHOR_EMAIL', $email);
$t->parse('authorline_ref', 'authorline', true);
}
//finish and echo
echo $t->finish($t->parse('OUT', 'authors'));
?>
Template file locations are relative to a definable
root directory. You can specify it directly in
the constructor (first parameter, defaults to .
)
or via setRoot().
Files are referenced via handle names. Thus you need to specify the handle name as first parameter, and the file's name as second parameter when calling setFile(). To speed things up, you may pass an array of handle-filename pairs to the method.
Loading a template file
<?php
$t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
$t->setFile('authors', 'authors.tpl');
?>
If you want to re-use certains parts of your HTML code multiple times, e.g. rows in a table, you can define blocks as described in the example above.
Blocks are not automatically detected, they need to be defined explicitely via the setBlock() method. The method takes the handle of the file in which the block is located as first parameter, the name of the block as written in the file as second and the new block handle as third parameter.
Defining a block
<?php
$t->setBlock('authors', 'authorline', 'authorline_ref');
?>
In the example template (available as handle authors
),
a block named authorline
is defined. The handle of
the block is now authorline_ref
.
This is the most easy part: Just use setVar() with the variable name and its new value. An optional third boolean parameter tells if the value should be appended to the existing value (defaults to false).
Setting a variable
<?php
$t->setVar('AUTHOR_NAME', 'Christian Weiske');
?>
Variables set influence all variables in the template, no matter if they are defined within a block or not.
Once you filled all variables in a block, you want to "save" them to be able to re-use the block another time. Method parse() is useful here. First parameter is the variable/handle in which the block content shall be stored, second is the handle name of the block. The optional third parameter tells if you want to append the block content to the handle value or not - it defaults to false, but you probably want to do that.
Parsing a block
<?php
foreach ($authors as $name => $email) {
$t->setVar('AUTHOR_NAME', $name);
$t->setVar('AUTHOR_EMAIL', $email);
$t->parse('authorline_ref', 'authorline', true);
}
?>
In this example, handle authorline_ref
is filled
with the contents of block authorline
. If
the third parameter would be false
instead of
true
, only the last line would be in it
at the end.
When you're done with setting variables and parsing your blocks, it's time to write the contents to your client. At first, you need to parse() the contents of the file handle into a new handle.
To remove all unused variables and blocks, you might want to call
finish(). Depending on the value set for
unknowns
(as second paramter in the constructor,
or via setUnknowns()), unused variables
are either erased (remove
),
kept (keep
) or commented out
(comment
).
To echo your HTML, either directly use the return value of finish(), or use get() to retrieve the content.
Finishing a file and writing it out
<?php
$t->parse('OUT', 'authors');
$htmlcode = $t->finish(
$tpl->get('OUT')
);
echo $htmlcode;
?>
Finishing a file and writing it out, the easy way
<?php
echo $tpl->finish($t->parse('OUT', 'authors'));
?>
HTML_Template_Sigma: implementation of Integrated Templates API with template 'compilation' added.
Class Trees for HTML_Template_Sigma()
HTML_Template_Sigma
The default format of placeholder is
{[0-9A-Za-z_-]+}
This means, the name of the placeholder can consist of upper- and lowercase letters, numbers, underscores and hyphens. The name must be placed between curly brackets without any spaces.
Actual values for the placeholders are set using setVariable() and setGlobalVariable() methods. Placeholders for which no values were set are removed from output by default.
The format of a block is
The rules for the block name are the same like for placeholders. In contrast to placeholders the spaces in the block markup are required.
The nesting of blocks is permitted, but be careful while parsing. You have to parse() the innermost block first and then go from inner to outer.
In Sigma the whole template itself is treated as a virtual block called
"__global__"
. Most block-related functions use this block
name as default.
It is possible to include a template file from within another template file using an <!-- INCLUDE filename -->
statement:
When such a template file gets loaded, the <!-- INCLUDE filename.html -->
will be replaced by contents of filename.html
.
Some things to note:
<!-- INCLUDE -->
statements are only processed when loading the
template files from disk. If you use
setTemplate()
method to set the template or
addBlock() and
replaceBlock()
to work with its blocks, then <!-- INCLUDE -->
statements in
these templates will not be replaced!
Although this functionality is implemented using addBlockfile(), unlike addBlockfile() no new blocks are created in the template.
<!-- INCLUDE -->
statements are processed before any variable substitution can take place. So <!-- INCLUDE {placeholder} -->
will not work unless you actually have a file named {placeholder}
and want to load it.
Sigma templates can contain simple function calls. This means that the author of the template can add a special placeholder to it
... some content ... func_h1("embedded in h1") ... some more content ...
Sigma will parse the template for these placeholders and will allow you to define a callback function for them via setCallbackFunction(). Callback will be called automatically when the block containing such function call is parse()'d.
Format of such function name is as follows
func_[_a-zA-Z]+[A-Za-z_0-9]*
that means that it should start with a 'func_' prefix, then has a letter or an undercore and then a sequence of letters, digits or underscores. Arguments to these template functions can contain variable placeholders
func_translate('Hello, {username}')
but not blocks or other function calls.
The information in this section applies to HTML_Template_Sigma version 1.1.2 and later, please upgrade if you have problems with template function arguments in previous versions.
Quoting of function arguments is not mandatory, the following is a perfectly valid template function:
But consider the following: function arguments are contained within
parentheses and separated by commas. Therefore if closing parenthesis
')'
or comma ','
appears in function argument, it should be quoted.
The next thing to consider is that HTML_Template_Sigma is mostly targeted for generating HTML. Therefore a quoted string within an argument is most probably a tag attribute. The contents of such strings are not parsed for commas and parentheses. Therefore the following is also a perfectly valid template function:
But if you have an unmatched single or double quote in your argument or if the argument starts with a quote, it should be quoted.
Finally, the argument should be quoted if it is an empty string or if its leading or trailing whitespace is significant (leading and trailing whitespace will be removed from unquoted arguments).
The arguments can be quoted using either single or double quotes. If an
argument contains a quote of the same type, then it should be escaped using
the backslash symbol '\'
. The backslash symbol
itself should also be escaped,
will pass O'really
and AC\DC
to
the relevant callbacks.
Valid and invalid template function arguments
Since release 1.1.0, instead of using
func_callback({var})
you can write
{var:callback}
There are 3 automatically registered template functions
Thus, if you add {var:h}
placeholder to the template, var will be have unsafe characters replaced by corresponding HTML entitites.
Since release 1.2.0 it is possible to provide the charset parameter for 'h' and 'e' callbacks via setOption().
Since release 1.2.0 it is possible to add comments to the template file:
These comments will be removed from the output, unlike standard HTML comments.
It is possible to somewhat customize the syntax of templates by changing regular expressions (or their parts) used by Sigma for parsing.
The actual regular expressions used to parse templates are generated in constructor using parts specified as class properties. It is generally not safe to change the generated regular expressions (as their format is hardcoded in several places throughout Sigma), but you can override the properties:
'{'
'}'
'[0-9A-Za-z_-]+'
'[0-9A-Za-z._-]+'
'[_a-zA-Z][A-Za-z_0-9]*'
'#<!--\s+INCLUDE\s+(\S+)\s+-->#im'
'#<!--\s+COMMENT\s+-->.*?<!--\s+/COMMENT\s+-->#sm'
To customize some of the above parts you need to subclass
HTML_Template_Sigma and override the relevant properties
so that generated regular expressions will use the new parts. For example,
if you want to prevent Sigma treating {1}
(which may appear
as a part of Javascript regular expression) as a placeholder (see
bug #18147)
you can do the following
<?php
class SanerPlaceholders extends HTML_Template_Sigma
{
var $variablenameRegExp = '[A-Za-z_][0-9A-Za-z._-]*';
}
$tpl = new SanerPlaceholders();
$tpl->setTemplate('{foo} {1} {bar}');
$tpl->setVariable('foo', 'foo value');
$tpl->show();
?>
Other usage examplesThere are several usage examples in the package archive that cover most of its functionality. You are encouraged to review them along with the docs.
The table.html
template file
The table_header.html
template file
The script
<?php
require_once 'HTML/Template/Sigma.php';
function toggle($item1, $item2)
{
static $i = 1;
return $i++ % 2? $item1: $item2;
}
$data = array (
array("Stig", "Bakken"),
array("Martin", "Jansen"),
array("Alexander", "Merz")
);
$tpl =& new HTML_Template_Sigma('.');
$tpl->loadTemplateFile('table.html');
$tpl->setCallbackFunction('bgcolor', 'toggle');
foreach ($data as $name) {
// assign data
$tpl->setVariable(array(
'first_name' => $name[0],
'last_name' => $name[1]
));
// process the block
$tpl->parse('table_row');
}
// print out the output
$tpl->show();
?>
The output
This is the way to bypass RegExp parsing on template load. Instead of parsing the original template on every request, we keep its internal representation (a serialized array, essentially) and load it instead.
Think about template compilation in Smarty. Only Sigma does not compile templates to PHP code.
No data caching is taking place. If you want to do this, consider using some of the PEAR's cache packages.
Yes.
No. Cached version is considered valid until the source template changes.
Yes, just delete all the files in the cache dir.
Yes. The answer is based on personal experience.
If you are going to perform some benchmarks, then use some real-world complex templates, not artificial ones. The performance gain will be greater with bigger and more complex (dozens of blocks) ones.
Caching is completely transparent. To take advantage of this feature you only have to either pass a second parameter to the constructor or call a setCacheRoot() method later.
<?php
require_once 'HTML/Template/Sigma.php';
$tpl =& new HTML_Template_Sigma('./templates', './templates/prepared');
$tpl->loadTemplateFile('default.html');
// go on
?>
For each distinct template file in ./templates
loaded with either loadTemplatefile(), addBlockfile(), replaceBlockfile() or <!-- INCLUDE --> a prepared version will be generated in ./templates/prepared
.
void HTML_Template_Sigma::HTML_Template_Sigma (
string $root = ''
, string $cacheRoot = ''
)
Constructor: builds some complex regular expressions and optionally sets the root directories.
Make sure that you call this constructor if you derive your template class from this one.
$root
root directory for templates
$cacheRoot
directory to cache "prepared" templates in
throws no exceptions thrown
This function can not be called statically.
mixed HTML_Template_Sigma::addBlock (
string $placeholder
, string $block
, string $template
)
Adds a block to the template changing a variable placeholder to a block placeholder. This means that a new block will be integrated into the template in place of a variable placeholder. The variable placeholder will be removed and the new block will behave in the same way as if it was inside the original template.
The block content must not start with <!-- BEGIN blockname -->
and end with <!-- END blockname -->
, if it does the error will be thrown.
$placeholder
name of the variable placeholder, the name must be unique within the template.
$block
name of the block to be added
$template
content of the block
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_EXISTS | Block '$block ' already exists |
Tried to add a block with a name that is already present in the template | Choose a different name for a new block |
SIGMA_PLACEHOLDER_NOT_FOUND | Variable placeholder '$placeholder ' not found |
There is no placeholder to replace by a new block in the template | Check the spelling of the placeholder name |
SIGMA_PLACEHOLDER_DUPLICATE | Placeholder '$placeholder ' should be unique, found in multiple blocks |
A placeholder to be replaced by a new block should appear only in one place | Check the spelling of the placeholder name, choose a different placeholder |
SIGMA_BLOCK_DUPLICATE | The name of a block must be unique within a template. Block 'blockname' found twice. | The added block contains a subblock that has the same name as the existing one | Check the $template and rename the block to something else |
SIGMA_CALLBACK_SYNTAX_ERROR | Cannot parse template function: (error description) | Bogus syntax for template function parameters. | Fix the template function definition, pay special attention to quoting rules. |
This function can not be called statically.
mixed HTML_Template_Sigma::addBlockfile (
string $placeholder
, string $block
, string $filename
)
Adds a block taken from a file to the template, changing a variable placeholder to a block placeholder.
$placeholder
name of the variable placeholder
$block
name of the block to be added
$filename
template file that contains the block
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_EXISTS | Block '$block ' already exists |
Tried to add a block with a name that is already present in the template | Choose a different name for a new block |
SIGMA_PLACEHOLDER_NOT_FOUND | Variable placeholder '$placeholder ' not found |
There is no placeholder to replace by a new block in the template | Check the spelling of the placeholder name |
SIGMA_PLACEHOLDER_DUPLICATE | Placeholder '$placeholder ' should be unique, found in multiple blocks |
A placeholder to be replaced by a new block should appear only in one place | Check the spelling of the placeholder name, choose a different placeholder |
SIGMA_TPL_NOT_FOUND | Cannot read the template file '$filename ' |
File is unreadable for some reason | Check if the file exists and has correct permissions set |
SIGMA_CACHE_ERROR | Cannot save template file 'filename' | A prepared template file cannot be saved | Check if the directory for prepared templates cache exists and is writeable for your script |
SIGMA_BLOCK_DUPLICATE | The name of a block must be unique within a template. Block 'blockname' found twice. | The added file contains a block that has the same name as the existing one | Check the file and rename the block to something else |
SIGMA_CALLBACK_SYNTAX_ERROR | Cannot parse template function: (error description) | Bogus syntax for template function parameters. | Fix the template function definition, pay special attention to quoting rules. |
This function can not be called statically.
bool HTML_Template_Sigma::blockExists (
string $block
)
Checks if the block exists in the template
$block
block name
throws no exceptions thrown
This function can not be called statically.
void HTML_Template_Sigma::clearVariables (
void
)
Global variables are not affected, only the ones set via setVariable(). The method is useful when you add a lot of variables via setVariable() and are not sure whether all of them appear in the block you parse(). If you clear the variables after parse(), you don't risk them suddenly showing up in other blocks.
throws no exceptions thrown
This function can not be called statically.
string HTML_Template_Sigma::errorMessage (
integer $code
, string $data
= null
)
Returns a textual error message for an error code. This is usually called when throwing a error, there is seldom need to call this method directly.
$code
error code
$data
additional data to insert into message
return error message
throws no exceptions thrown
This function can not be called statically.
string HTML_Template_Sigma::get (
string $block = '__global__'
, bool $clear
= false
)
Returns a parsed block: block with all replacements done.
This method will automatically call parse(), only if called with $block
='__global__' when '__global__' was not parse()'d before. In all other cases you should call parse() before calling get()
$block
block name
$clear
whether to clear parsed block contents
return block with all replacements done
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$block ' |
There is no block $block in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
This function can not be called statically.
array HTML_Template_Sigma::getBlockList (
string $parent = '__global__'
, bool $recursive
= false
)
Returns a list of blocks within a template.
If $recursive
is FALSE, it returns just a 'flat' array of $parent
's direct subblocks. If $recursive
is TRUE, it builds a tree of template blocks using $parent
as root. Tree structure is compatible with PEAR::Tree's Memory_Array driver.
$parent
parent block name
$recursive
whether to return a tree of child blocks (TRUE) or a 'flat' array (FALSE)
return a list of child blocks
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$parent ' |
There is no block $parent in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
This function can not be called statically.
string HTML_Template_Sigma::getCurrentBlock (
void
)
Returns the current block name (set by HTML_Template_Sigma::setCurrentBlock())
return block name
throws no exceptions thrown
This function can not be called statically.
array HTML_Template_Sigma::getPlaceholderList (
string $block = '__global__'
)
Returns a list of placeholders within a block.
Only 'normal' placeholders are in the list, not ones automatically created for blocks and template functions.
$block
block name
return a list of placeholders
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$block ' |
There is no block $block in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
This function can not be called statically.
mixed HTML_Template_Sigma::hideBlock (
string $block
)
Hides the block even if it is not "empty".
Is somewhat an opposite to touchBlock().
Consider a block (a 'edit' link for example) that should be visible to registered/"special" users only, but its visibility is triggered by some little 'id' field passed in a large array into setVariable(). You can either carefully juggle your variables to prevent the block from appearing (a fragile solution) or simply call hideBlock()
$block
block name
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$block ' |
There is no block $block in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
This function can not be called statically.
mixed HTML_Template_Sigma::loadTemplateFile (
string $filename
, boolean $removeUnknownVariables
= true
, boolean $removeEmptyBlocks
= true
)
Loads a template file. If caching is on, then it checks whether a "prepared" template exists. If it does, it gets loaded instead of the original, if it does not, then the original gets loaded and prepared and then the prepared version is saved. addBlockfile() and replaceBlockfile() implement quite the same logic.
$filename
filename
$removeUnknownVariables
remove unknown/unused variables?
$removeEmptyBlocks
remove empty blocks?
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_TPL_NOT_FOUND | Cannot read the template file '$filename ' |
File is unreadable for some reason | Check if the file exists and has correct permissions set |
SIGMA_CACHE_ERROR | Cannot save template file 'filename' | A prepared template file cannot be saved | Check if the directory for prepared templates cache exists and is writeable for your script |
SIGMA_BLOCK_DUPLICATE | The name of a block must be unique within a template. Block 'blockname' found twice. | The loaded file contains two blocks sharing the same name | Check the file and rename one of the blocks to something else |
SIGMA_CALLBACK_SYNTAX_ERROR | Cannot parse template function: (error description) | Bogus syntax for template function parameters. | Fix the template function definition, pay special attention to quoting rules. |
see HTML_Template_Sigma::setTemplate(), HTML_Template_Sigma::$removeUnknownVariables
, HTML_Template_Sigma::$removeEmptyBlocks
This function can not be called statically.
void HTML_Template_Sigma::parse (
string $block = '__global__'
, boolean $flagRecursion
= false
, boolean $fakeParse
= false
)
Parses the given block. It substitutes local and global variables appearing in the block and set via setVariable() and setGlobalVariable(), calls all the callback functions and recursively processes the subblocks of the block.
$block
block name
$flagRecursion
TRUE if the function is called recursively (do not set this to TRUE yourself!)
$fakeParse
TRUE if parsing a "hidden" block (do not set this to TRUE yourself!)
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$block ' |
There is no block $block in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
This function can not be called statically.
void HTML_Template_Sigma::parseCurrentBlock (
void
)
Parses the current block (set by HTML_Template_Sigma::setCurrentBlock())
throws no exceptions thrown
This function can not be called statically.
string HTML_Template_Sigma::placeholderExists (
string $placeholder
, string $block = ''
)
Checks whether the placeholder exists in the template, returns the name of the (first) block that contains the specified placeholder.
$placeholder
Name of the placeholder you're searching
$block
Name of the block to scan. If left out (default) all blocks are scanned.
return Name of the (first) block that contains the specified placeholder. If the placeholder was not found an empty string is returned.
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$block ' |
There is no block $block in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
This function can not be called statically.
mixed HTML_Template_Sigma::replaceBlock (
string $block
, string $template
, boolean $keepContent
= false
)
Replaces an existing block with new content. This function will replace a block of the template and all blocks contained in it and add a new block instead. This means you can dynamically change your template.
Sigma analyses the way you've nested blocks and knows which block belongs into another block. This nesting information helps to make the API short and simple. Replacing blocks does not only mean that Sigma has to update the nesting information (relatively time consuming task) but you have to make sure that you do not get confused due to the template change yourself.
$block
name of a block to replace
$template
new content
$keepContent
TRUE if the parsed contents of the block should be kept
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$block ' |
There is no block $block in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
SIGMA_BLOCK_DUPLICATE | The name of a block must be unique within a template. Block 'blockname' found twice. | The new block contains a subblock that has the same name as the existing one | Check the $template and rename the block to something else |
SIGMA_CALLBACK_SYNTAX_ERROR | Cannot parse template function: (error description) | Bogus syntax for template function parameters. | Fix the template function definition, pay special attention to quoting rules. |
This function can not be called statically.
mixed HTML_Template_Sigma::replaceBlockfile (
string $block
, string $filename
, boolean $keepContent
= false
)
Replaces an existing block with new content from a file.
$block
name of a block to replace
$filename
template file that contains the block
$keepContent
TRUE if the parsed contents of the block should be kept
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$block ' |
There is no block $block in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
SIGMA_BLOCK_DUPLICATE | The name of a block must be unique within a template. Block 'blockname' found twice. | The loaded block contains a subblock that has the same name as the existing one | Check the file contents and rename the block to something else |
SIGMA_TPL_NOT_FOUND | Cannot read the template file '$filename ' |
File is unreadable for some reason | Check if the file exists and has correct permissions set |
SIGMA_CACHE_ERROR | Cannot save template file 'filename' | A prepared template file cannot be saved | Check if the directory for prepared templates cache exists and is writeable for your script |
SIGMA_CALLBACK_SYNTAX_ERROR | Cannot parse template function: (error description) | Bogus syntax for template function parameters. | Fix the template function definition, pay special attention to quoting rules. |
This function can not be called statically.
void HTML_Template_Sigma::setCacheRoot (
string $root
)
Sets the directory to cache "prepared" templates in, the directory should be writable for PHP.
The "prepared" template contains an internal representation of template structure: essentially a serialized array of $_blocks, $_blockVariables, $_children and $_functions, may also contain $_triggers. This allows to bypass expensive calls to HTML_Template_Sigma::_buildBlockVariables() and especially HTML_Template_Sigma::_buildBlocks() when reading the "prepared" template instead of the "source" one.
The files in this cache do not have any TTL and are regenerated when the source templates change.
$root
directory name
throws no exceptions thrown
This function can not be called statically.
mixed HTML_Template_Sigma::setCallbackFunction (
string $tplFunction
, mixed $callback
, bool $preserveArgs
= false
)
Sets a callback function. Sigma templates can contain simple function calls. This means that the author of the template can add a special placeholder to it: func_h1("embedded in h1") Sigma will parse the template for these placeholders and will allow you to define a callback function for them. Callback will be called automatically when the block containing such function call is parse()'d.
Please note that arguments to these template functions can contain variable placeholders: func_translate('Hello, {username}'), but not blocks or other function calls.
This should NOT be used to add logic (except some presentation one) to the template. If you use a lot of such callbacks and implement business logic through them, then you're reinventing the wheel. Consider using XML/XSLT, native PHP or some other template engine.
Script:
<?php
function h_one($arg)
{
return '<h1>' . $arg . '</h1>';
}
// ...
$tpl = new HTML_Template_Sigma(' ... ');
// ...
$tpl->setCallbackFunction('h1', 'h_one');
// ...
$tpl->show()
?>
template:
... func_h1('H1 Headline') ...
$tplFunction
Function name in the template
$callback
A callback: anything that can be passed to call_user_func_array()
$preserveArgs
If TRUE, then no variable substitution in arguments will take place before function call
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_INVALID_CALLBACK | Callback does not exist | The $callback is not an existing function or method |
Check spelling, check whether the object was correctly instantiated if using the method callback |
This function can not be called statically.
mixed HTML_Template_Sigma::setCurrentBlock (
string $block = '__global__'
)
Sets the name of the current block: the one in which variables will be substituted.
$block
block name
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$block ' |
There is no block $block in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
This function can not be called statically.
void HTML_Template_Sigma::setGlobalVariable (
mixed $variable
, string $value = ''
)
Sets a global variable value. Global variables are "special": they do not get cleared after substitution and do not make blocks not empty if substituted.
$variable
variable name or array ('varname'=>'value')
$value
variable value if $variable is not an array
throws no exceptions thrown
This function can not be called statically.
mixed HTML_Template_Sigma::setOption (
string $option
, mixed $value
)
Sets the option for the template class. Currently it understands the following options:
If set to TRUE, then do not substitute variables and remove unused placeholders in data added through setVariable() and setGlobalVariable(). See also bugs #20199 and #21951. Default is FALSE, for performance reasons.
Whether to trim extra whitespace from template on cache save. Generally safe to have this on, unless you have <pre></pre> in templates or want to preserve HTML indentantion. Default is TRUE
Character set to use by builtin 'h' and 'e' callbacks.
Default is 'iso-8859-1'
.
Available since release 1.2.0
$option
option name
$value
option value
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_UNKNOWN_OPTION | Unknown option '$option ' |
$option is not known to Sigma |
Check the option name spelling |
This function can not be called statically.
void HTML_Template_Sigma::setRoot (
string $root
)
Sets the file root for templates. The file root gets prefixed to all filenames passed to the object.
$root
directory name
throws no exceptions thrown
This function can not be called statically.
mixed HTML_Template_Sigma::setTemplate (
string $template
, boolean $removeUnknownVariables
= true
, boolean $removeEmptyBlocks
= true
)
Sets the template. You can either load a template file from disk with loadTemplateFile() or set the template manually using this function.
$template
template content
$removeUnknownVariables
remove unknown/unused variables?
$removeEmptyBlocks
remove empty blocks?
return SIGMA_OK on success, error object on failure
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_DUPLICATE | The name of a block must be unique within a template. Block 'blockname' found twice. | The $template contains two blocks sharing the same name |
Check the $template and rename one of the blocks to something else |
SIGMA_CALLBACK_SYNTAX_ERROR | Cannot parse template function: (error description) | Bogus syntax for template function parameters. | Fix the template function definition, pay special attention to quoting rules. |
This function can not be called statically.
void HTML_Template_Sigma::setVariable (
mixed $variable
, string $value = ''
)
Sets a variable value. The function can be used either like setVariable("varname", "value") or with one array $variables["varname"] = "value" given setVariable($variables)
$variable
variable name or array ('varname'=>'value')
$value
variable value if $variable
is not an array
throws no exceptions thrown
This function can not be called statically.
void HTML_Template_Sigma::show (
string $block = '__global__'
)
Prints a block with all replacements done.
$block
block name
throws no exceptions thrown
This function can not be called statically.
mixed HTML_Template_Sigma::touchBlock (
string $block
)
Sometimes you have blocks that should be preserved although they are empty (no placeholder replaced). Think of a shopping basket. If it's empty you have to show a message to the user. If it's filled you have to show the contents of the shopping basket. Now where to place the message that the basket is empty? It's not a good idea to place it in you application as customers tend to like unecessary minor text changes. Having another template file for an empty basket means that one fine day the filled and empty basket templates will have different layouts.
So blocks that do not contain any placeholders but only messages like "Your shopping basked is empty" are introduced. Now if there is no replacement done in such a block the block will be recognized as "empty" and by default ($removeEmptyBlocks = true) be stripped off. To avoid this you can call touchBlock()
$block
block name
return SIGMA_OK on success, error object on failure
see HTML_Template_Sigma::$removeEmptyBlocks
Error code | Error message | Reason | Solution |
---|---|---|---|
SIGMA_BLOCK_NOT_FOUND | Cannot find block '$block ' |
There is no block $block in the template |
Check the block name spelling, check whether you added all the necessary blocks to the template |
This function can not be called statically.
The template engine is a compiling engine, all templates are compiled into PHP-files. This will make the delivery of the files faster on the next request, since the template doesn't need to be compiled again. If the template changes it will be recompiled. There is no new template language to learn. Beside the default mode, there is a set of constructs since version 1.6 which allow you to edit your templates with WYSIWYG editors
The template engine is a compiling engine, all templates are compiled into PHP-files. This will make the delivery of the files faster on the next request, since the template doesn't need to be compiled again. If the template changes it will be recompiled. There is no new template language to learn. Beside the default mode, there is a set of constructs since version 1.6 which allow you to edit your templates with WYSIWYG editors. By default the template engine uses indention for building blocks (you can turn that off). This feature was inspired by Python and by the need I felt to force myself to write proper HTML-code, using proper indentions, to make the code better readable.
Unfortunately, complete documentation is not available at the moment.
This manual page shows the basic steps to get HTML_TreeMenu working.
Data paging class which also builds links to the pages.
Pager is a class to page an array of data. It is taken as input and it is paged according to various parameters. Pager also builds links within a specified range, and allows complete customization of the output (it even works with mod_rewrite). It is compatible with Pager v.1.x and Pager_Sliding API
This simple example will page the array of alphabetical letters, giving back pages with 3 letters per page, and links to the previous two / next two pages:
<?php
require_once 'Pager.php';
$params = array(
'mode' => 'Jumping',
'perPage' => 3,
'delta' => 2,
'itemData' => array('a','b','c','d','e',[...omissis...],'z')
);
$pager = & Pager::factory($params);
$data = $pager->getPageData();
$links = $pager->getLinks();
//$links is an ordered+associative array with 'back'/'pages'/'next'/'first'/'last'/'all' links.
//NB: $links['all'] is the same as $pager->links;
//echo links to other pages:
echo $links['all'];
//Pager can also generate <link rel="first|prev|next|last"> tags
echo $pager->linkTags;
//Show data for current page:
echo 'PAGED DATA: ' ; print_r($data);
//Results from methods:
echo 'getCurrentPageID()...: '; var_dump($pager->getCurrentPageID());
echo 'getNextPageID()......: '; var_dump($pager->getNextPageID());
echo 'getPreviousPageID()..: '; var_dump($pager->getPreviousPageID());
echo 'numItems()...........: '; var_dump($pager->numItems());
echo 'numPages()...........: '; var_dump($pager->numPages());
echo 'isFirstPage()........: '; var_dump($pager->isFirstPage());
echo 'isLastPage().........: '; var_dump($pager->isLastPage());
echo 'isLastPageComplete().: '; var_dump($pager->isLastPageComplete());
echo '$pager->range........: '; var_dump($pager->range);
?>
In case you're wondering, $pager->range is a numeric array; its keys are the numbers of the pages in the current range, and the matching values are booleans (TRUE if its key represents currentPage, FALSE otherwise). This array can be useful to build the links manually, e.g. when using a template engine.
This example shows how you can use this class with mod_rewite. Let's suppose we have a .htaccess like this:
It should transform an url like "/articles/march/art15.html" into "/article.php?num=15&month=march"
<?php
require_once 'Pager.php';
$month = 'september';
$params = array(
'mode' => 'Sliding',
'append' => false,
'urlVar' => 'num',
'path' => 'http://myserver.com/articles/' . $month,
'fileName' => 'art%d.html', //Pager replaces "%d" with page number...
'itemData' => array('a','b','c',[...omissis...],'z'),
'perPage' => 3
);
$pager = & Pager::factory($params);
$data = $pager->getPageData();
echo $pager->links;
echo 'Data for current page: '; print_r($data);
?>
Using more than one pager in a single page is as simple as using a different
urlVar
for each pager:
<?php
require_once 'Pager.php';
//first pager
$params1 = array(
'perPage' => 3,
'urlVar' => 'pageID_articles', //1st identifier
'itemData' => $someArray
);
$pager1 = & Pager::factory($params1);
$data1 = $pager1->getPageData();
$links1 = $pager1->getLinks();
//second pager
$params2 = array(
'perPage' => 8,
'urlVar' => 'pageID_news', //2nd identifier
'itemData' => $someOtherArray
);
$pager2 = & Pager::factory($params2);
$data2 = $pager2->getPageData();
$links2 = $pager2->getLinks();
?>
If you want to paginate db resultsets, fetching them all into an array and passing it to Pager might not be the best option. You can still leverage Pager and have good performances using a wrapper. There is a sample wrapper for each one of the PEAR db abstraction systems in the /examples/ dir of the package. You may use it as-is or customize it to your needs.
If you need to add some extra variables to the querystring,
use the extraVars
parameter:
<?php
$params = array(
'extraVars' => array(
'firstKey' => 'firstValue',
'secondKey' => 'secondValue',
//...
),
//...
);
$pager1 = & Pager::factory($params);
?>
Since version 2.2.1, Pager works with PHP 5 too, but you must use the factory() method instead of the constructor (which is deprecated):
<?php
require_once 'Pager.php';
//wrong:
//$pager =& new Pager($params);
//right
$pager =& Pager::factory($params);
//continue as you did before
?>
If you are using a previous revision and cannot update, you must write the following code on PHP 5:
<?php
//chose your preferred mode [Jumping | Sliding]:
//require_once 'Pager/Jumping.php';
require_once 'Pager/Sliding.php';
//$pager =& new Pager_Jumping($params);
$pager =& new Pager_Sliding($params);
//continue as you did before
?>
There are some other online resources with in-depth coverage of what Pager can do: PEAR::Pager tutorials.
Since an example is worth 1000 words:
Let's suppose that the data spans on 15 pages, and the window width is 5 page links. The links are built on "frames" of 5 pages each: [1-5] [6-10] [11-15] Pager in "Jumping" mode always shows the same 5 page links while you are on one of these pages. Here's a temporal succession of the links, starting from page 1 and moving forward. There are brakets around current page number to highlight this:
<?php
a) {1} 2 3 4 5 => // first frame: [1-5]
b) <= 1 {2} 3 4 5 =>
c) <= 1 2 {3} 4 5 =>
d) <= 1 2 3 {4} 5 =>
e) <= 1 2 3 4 {5} => // HERE IT JUMPS TO THE NEXT FRAME
f) <= {6} 7 8 9 10 => // second frame: [6-10]
g) <= 6 {7} 8 9 10 =>
h) <= 6 7 {8} 9 10 =>
?>
and so on. See what a "jumping window" frame is? When you reach a limit (in the example, you go from page 5 to page 6), it "jumps" to next frame (links from page 6 to 10).
Instead of jumping from one frame to the other, with Pager in "Sliding" mode the change is done smoothly, and the current page is always shown at the center of the "window" (except of course for the first and the last pages):
<?php
a) {1} 2 3 4 5 => [15]
b) [1] <= 1 {2} 3 4 5 => [15]
c) [1] <= 1 2 {3} 4 5 => [15] // HERE IT's STARTING WORKING AS DESIGNED
d) [1] <= 2 3 {4} 5 6 => [15] // see: current page number is at the center of the window
e) [1] <= 3 4 {5} 6 7 => [15] // and it stays there...
f) [1] <= 4 5 {6} 7 8 => [15]
g) [1] <= 5 6 {7} 8 9 => [15]
h) [1] <= 6 7 {8} 9 10 => [15]
?>
and so on.
Apart from the different "philosophy", there is one difference in the
delta
parameter: in "Jumping" mode, it's the
number of page numbers to show; in "Sliding" mode it's the number of
page numbers to show before and after the current one.
object &factory (
array $options
)
Pager::factory() method takes an associative array of parameters as input values. This is the complete list of these options:
itemData
[array]
Array of items to page.
totalItems
[integer]
Number of items to page (used only if itemData
is not provided).
perPage
[integer]
Number of items to display on each page.
delta
[integer]
Number of page numbers to display before and after the current one.
mode
[string]
"Jumping" or "Sliding" -window - It determines pager behaviour.
httpMethod
[string]
Specifies the HTTP method to use. Valid values are 'GET' or 'POST'.
formID
[string]
Specifies which HTML form to use in POST mode.
importQuery
[boolean]
if true (default behaviour), variables and values are imported from the submitted data (query string) and used in the generated links, otherwise they're ignored completely
currentPage
[integer]
Initial page number (if you want to show page #2 by default,
set currentPage
to 2)
expanded
[boolean]
if TRUE, window size is always 2*delta+1
linkClass
[string]
Name of CSS class used for link styling.
urlVar
[string]
Name of URL var used to indicate the page number. Default value is "pageID".
path
[string]
Complete path to the page (without the page name).
fileName
[string]
name of the page, with a "%d" if append
== TRUE.
fixFileName
[boolean]
If set to FALSE, the fileName
option is not overridden. Use at your own risk.
append
[boolean]
If TRUE pageID is appended as GET value to the URL.
If FALSE it is embedded in the URL according to
fileName
specs.
altFirst
[string]
Alt text to display on the link of the first page. Default value is "first page"; if you want a string with the page number, use "%d" as a placeholder (for instance "page %d")
altPrev
[string]
Alt text to display on the link of the previous page. Default value is "previous page";
altNext
[string]
Alt text to display on the link of the next page. Default value is "next page";
altLast
[string]
Alt text to display on the link of the last page. Default value is "last page"; if you want a string with the page number, use "%d" as a placeholder (for instance "page %d")
altPage
[string]
Alt text to display before the page number. Default value is "page " (followed by the page number). You can optionally use "%d" as a placeholder (for instance "page n. %d") to place the page number where you want.
prevImg
[string]
Something to display instead of "<<". It can be text such as "<< PREV" or an <img/> as well.
nextImg
[string]
Something to display instead of ">>". It can be text such as "NEXT >>" or an <img/> as well.
separator
[string]
What to use to separate numbers. It can be an <img/>, a comma, an hyphen, or whatever.
spacesBeforeSeparator
[integer]
Number of spaces before the separator.
spacesAfterSeparator
[integer]
Number of spaces after the separator.
firstLinkTitle
[string]
String used as title in <link rel="first"> tag
nextLinkTitle
[string]
String used as title in <link rel="next"> tag
prevLinkTitle
[string]
String used as title in <link rel="previous"> tag
lastLinkTitle
[string]
String used as title in <link rel="last"> tag
curPageLinkClassName
[string]
CSS class name for the current page link.
curPageSpanPre
[string]
Text before the current page link.
curPageSpanPost
[string]
Text after the current page link.
firstPagePre
[string]
String used before the first page number. It can be an <img/>, a "{", an empty string, or whatever.
firstPageText
[string]
String used in place of the first page number.
firstPagePost
[string]
String used after the first page number. It can be an <img/>, a "}", an empty string, or whatever.
lastPagePre
[string]
Similar to firstPagePre
, but used for
last page number.
lastPageText
[string]
Similar to firstPageText
, but used for
last page number.
lastPagePost
[string]
Similar to firstPagePost
, but used for
last page number.
clearIfVoid
[boolean]
if there's only one page, don't display pager links (returns an empty string).
extraVars
[array]
additional URL vars to be added to the querystring.
excludeVars
[array]
URL vars to be excluded from the querystring.
useSessions
[boolean]
if TRUE, number of items to display per page is stored in the $_SESSION[$_sessionVar] var.
closeSession
[boolean]
if TRUE, the session is closed just after R/W.
sessionVar
[string]
Name of the session var for perPage value. A value different from default can be useful when using more than one Pager istance in the page.
showAllText
[string]
Text to be used for the 'show all' option in the select box generated by getPerPageSelectBox()
pearErrorMode
[constant]
PEAR_ERROR mode for raiseError(). Default is PEAR_ERROR_RETURN.
REQUIRED options are:
fileName
IF
append
==FALSE
(default is TRUE)
itemData
OR totalItems
(if itemData is set, totalItems is overwritten)
object
- a specific Pager instance
or a PEAR_Error object, if fails
array Pager::setOptions (
array $options
)
Remember to call build() after this method to regenerate the data and the links.
integer $index
- an associative array of options.
See Pager::factory()
for the full list of options.
return PAGER_OK constant on success
array Pager::build (
)
If you want to change an option after the Pager object has been constructed, after the call to setOptions() you have to generate or refresh the links and paged data with this method.
integer getCurrentPageID (
)
integer
- Current page number.
array Pager::getLinks (
integer $pageID
= null
)
integer $pageID
- Optional pageID. If specified,
linksfor that page are provided instead of current one.
return back/pages/next/first/last/all links, both as numeric and associative array.
mixed Pager::getNextPageID (
)
If current page is last page this function returns FALSE, otherwise returns next page number.
return Next page number or FALSE
array Pager::getOffsetByPageId (
integer $pageid
= null
)
Returns offsets for given pageID
.
Eg, if you pass it pageID
one and your
perPage
limit is 10 it will return (1, 10).
pageID
=2 would give you (11, 20).
if the method is called without parameter, pageID is set to currentPage
integer $pageid
- PageID to get offsets for
return array with first and last offsets
deprecated
array Pager::getPageData (
integer $pageID
= null
)
integer $pageID
- Desired page ID (optional)
return array of data for this page.
array Pager::getPageIdByOffset (
integer $index
)
This method is only available in "Jumping" mode.
integer $index
- Offset to get pageID for
return Page number for this offset
array Pager::getPageRangeByPageId (
integer $pageid
= null
)
Given a PageId, it returns the limits of the range of pages displayed. While getOffsetByPageId() returns the offset of the data within the current page, this method returns the offsets of the page numbers interval.
E.g., in "Jumping" mode, if you have pageId
=3 and
delta
=10, it will return (1, 10).
pageID
=8 would give you (1, 10) as well, because
1 <= 8 <= 10.
pageID
=11 would give you (11, 20).
In "Sliding" mode, if you have pageId
=5 and
delta
=2, it will return (3, 7).
pageID
of 9 would give you (4, 8).
if the method is called
without parameter, pageID
is set to currentPage number
integer $pageid
- PageID to get offsets for
return array with first and last offsets
array Pager::getPageSelectBox (
array $params
, string $extraAttributes = ''
)
array $params
(optional)
'optionText'
: text to show in each option.
Use '%d' where you want to see the number of the page.
'autoSubmit'
: if true, add some js code
to submit the form on the onChange event
string $extraAttributes
(html attributes) Tag attributes or
HTML attributes (id="foo" pairs), will be inserted in the <select> tag.
Returns a string with a XHTML SELECT menu with the page numbers, useful as an alternative to the links
This example shows how you can create a select box to let your users choose the number of the page to go to.
<?php
include 'Pager.php';
$params = array(
'mode' => 'Jumping',
'perPage' => 3,
'delta' => 2,
'itemData' => array('a','b','c','d','e',[...omissis...],'z'),
);
$pager = & Pager::factory($params);
$selectBoxParams = array(
'optionText' => 'page %d',
'autoSubmit' => true,
);
$selectBox = $pager->getPageSelectBox();
echo '<form action="' . htmlspecialchars($_SERVER['PHP_SELF']) . '" method="GET">';
echo $selectBox;
echo '<input type="submit" value="submit" />';
echo '</form>';
?>
return string with the XHTML SELECT menu.
mixed Pager::getPreviousPageID (
)
If current page is first page this function returns FALSE, otherwise returns previous page number.
return Previous page number or FALSE.
array Pager::getPerPageSelectBox (
integer $start = 5
, integer $end = 30
, integer $step = 5
, boolean $showAllData = false
, string $optionText = '%d'
)
integer $start
- Min. number of items per page (optional)
integer $end
- Max. number of items per page (optional)
integer $step
- Increment between two options (optional)
boolean $showAllData
- If true, perPage is set equal to totalItems (optional)
array $extraParams
(optional)
'optionText'
: text to show in each option.
Use '%d' where you want to see the number of pages selected.
'attributes'
: (html attributes) Tag attributes or
HTML attributes (id="foo" pairs), will be inserted in the <select> tag.
'checkMaxLimit'
: if true, Pager checks
if $end is bigger than $totalItems, and doesn't show the extra
select options.
Returns a string with a XHTML SELECT menu, useful for letting the user choose
how many items per page should be displayed. If parameter
useSessions
is TRUE, this value is stored in
a session var. The string isn't echoed right away so you can use it
with template engines.
This example shows how you can create a select box to let your users choose the number of items to display on each page.
<?php
include 'Pager/Pager.php';
$params = array(
'mode' => 'Jumping',
'perPage' => 3,
'delta' => 2,
'itemData' => array('a','b','c','d','e',[...omissis...],'z')
);
$pager = & Pager::factory($params);
$selectBox = $pager->getPerPageSelectBox();
echo '<form action="' . htmlspecialchars($_SERVER['PHP_SELF']) . '" method="GET">';
echo $selectBox;
echo '<input type="submit" value="submit" />';
echo '</form>';
?>
return string with the XHTML SELECT menu.
bool Pager::isFirstPage (
)
return TRUE or FALSE, wrt it is the first page or not.
bool Pager::isLastPage (
)
return TRUE or FALSE, wrt it is the last page or not.
bool Pager::isLastPageComplete (
)
return TRUE or FALSE, wrt the last page is complete
(i.e. it has perPage
values in the array for the last
page) or not.
int Pager_Sliding::numItems (
)
return integer - Number of items
int Pager_Sliding::numPages (
)
return integer - Number of pages
object &Pager (
array $options
)
The constructor is deprecated in favour of the new factory() method, which is PHP5 compatible too.
Pager constructor takes an associative array of parameters as input values. This is the complete list of these options:
itemData
[array]
Array of items to page.
totalItems
[integer]
Number of items to page (used only if itemData
is not provided).
perPage
[integer]
Number of items to display on each page.
delta
[integer]
Number of page numbers to display before and after the current one.
mode
[string]
"Jumping" or "Sliding" -window - It determines pager behaviour.
expanded
[boolean]
if TRUE, window size is always 2*delta+1
linkClass
[string]
Name of CSS class used for link styling.
urlVar
[string]
Name of URL var used to indicate the page number. Default value is "pageID".
path
[string]
Complete path to the page (without the page name).
fileName
[string]
name of the page, with a "%d" if append
== TRUE.
append
[boolean]
If TRUE pageID is appended as GET value to the URL.
If FALSE it is embedded in the URL according to
fileName
specs.
altPrev
[string]
Alt text to display for prev page, on prev link. Default value is "previous page";
altNext
[string]
Alt text to display for next page, on next link. Default value is "next page";
altPage
[string]
Alt text to display before the page number. Default value is "page ".
prevImg
[string]
Something to display instead of "<<". It can be text such as "<< PREV" or an <img/> as well.
nextImg
[string]
Something to display instead of ">>". It can be text such as "NEXT >>" or an <img/> as well.
separator
[string]
What to use to separate numbers. It can be an <img/>, a comma, an hyphen, or whatever.
spacesBeforeSeparator
[integer]
Number of spaces before the separator.
spacesAfterSeparator
[integer]
Number of spaces after the separator.
firstPagePre
[string]
String used before first page number. It can be an <img/>, a "{", an empty string, or whatever.
firstPageText
[string]
String used in place of first page number.
firstPagePost
[string]
String used after first page number. It can be an <img/>, a "}", an empty string, or whatever.
lastPagePre
[string]
Similar to firstPagePre
, but used for
last page number.
lastPageText
[string]
Similar to firstPageText
, but used for
last page number.
lastPagePost
[string]
Similar to firstPagePost
, but used for
last page number.
curPageLinkClassName
[string]
Name of CSS class used for current page link.
clearIfVoid
[boolean]
if there's only one page, don't display pager (returns an empty string).
useSessions
[boolean]
if TRUE, number of items to display per page is stored in the $_SESSION[$_sessionVar] var.
closeSession
[boolean]
if TRUE, the session is closed just after R/W.
sessionVar
[string]
Name of the session var for perPage value. A value different from default can be useful when using more than one Pager istance in the page.
pearErrorMode
[constant]
PEAR_ERROR mode for raiseError(). Default is PEAR_ERROR_RETURN.
REQUIRED options are:
fileName
IF
append
==FALSE
(default is TRUE)
itemData
OR totalItems
(if itemData is set, totalItems is overwritten)
object
- a specific Pager instance
or a PEAR_Error object, if fails
Data paging class which also builds links to the pages.
Pager_Sliding is a class to page an array of data. It is taken as input and it is paged according to various parameters. Pager_Sliding also builds links within a specified range, and allows complete customization of the output (it even works with mod_rewrite). It is compatible with PEAR::Pager's API
This package is deprecated in favour of the new Pager v.2.x.
This simple example will page the array of alphabetical letters, giving back pages with 3 letters per page, and links to the previous two / next two pages:
<?php
require_once 'Pager/Sliding.php';
$params = array(
'perPage' => 3,
'delta' => 2,
'itemData' => array('a','b','c','d','e',[...omissis...],'z')
);
$pager = & new Pager_Sliding($params);
$data = $pager->getPageData();
$links = $pager->getLinks();
//$links is an ordered+associative array with 'back'/'pages'/'next'/'first'/'last'/'all' links.
//NB: $links['all'] is the same as $pager->links;
//echo links to other pages:
echo $links['all']
//Show data for current page:
echo 'PAGED DATA: ' ; print_r($data);
//Results from methods:
echo 'getCurrentPageID()...: '; var_dump($pager->getCurrentPageID());
echo 'getNextPageID()......: '; var_dump($pager->getNextPageID());
echo 'getPreviousPageID()..: '; var_dump($pager->getPreviousPageID());
echo 'numItems()...........: '; var_dump($pager->numItems());
echo 'numPages()...........: '; var_dump($pager->numPages());
echo 'isFirstPage()........: '; var_dump($pager->isFirstPage());
echo 'isLastPage().........: '; var_dump($pager->isLastPage());
echo 'isLastPageComplete().: '; var_dump($pager->isLastPageComplete());
echo '$pager->range........: '; var_dump($pager->range);
?>
In case you're wondering, $pager->range is a numeric array; its keys are the numbers of the pages in the current range, and the matching values are booleans (TRUE if its key represents currentPage, FALSE otherwise). This array can be useful to build the links manually, e.g. when using a template engine.
This example shows how you can use this class with mod_rewite. Let's suppose we have a .htaccess like this:
It should transform an url like "/articles/march/art15.html" into "/article.php?num=15&month=march"
<?php
require_once 'Pager/Sliding.php';
$month = 'september';
$params = array(
'append' => false,
'urlVar' => 'num',
'path' => 'http://myserver.com/articles/' . $month,
'fileName' => 'art%d.html', //Pager replaces "%d" with page number...
'itemData' => array('a','b','c',[...omissis...],'z'),
'perPage' => 3
);
$pager = & new Pager_Sliding($params);
$data = $pager->getPageData();
echo $pager->links;
echo 'Data for current page: '; print_r($data);
?>
Using more than one pager in a single page is as simple as using a different
urlVar
for each pager:
<?php
require_once 'Pager/Sliding.php';
//first pager
$params1 = array(
'perPage' => 3,
'urlVar' => pageID_articles, //1st identifier
'itemData' => $someArray
);
$pager1 = & new Pager_Sliding($params1);
$data1 = $pager1->getPageData();
$links1 = $pager1->getLinks();
//second pager
$params2 = array(
'perPage' => 8,
'urlVar' => pageID_news, //2nd identifier
'itemData' => $someOtherArray
);
$pager2 = & new Pager_Sliding($params2);
$data2 = $pager2->getPageData();
$links2 = $pager2->getLinks();
?>
While Pager v.1.x has a "jumping window" style, Pager_Sliding has a "sliding window" style. What does that mean? Let's see an example:
Let's suppose that the data spans on 15 pages, and the window width is 5 page links. The links are built on "frames" of 5 pages each: [1-5] [6-10] [11-15] Pager v.1.x always shows the same 5 page links while you are on one of these pages. Here's a temporal succession of the links, starting from page 1 and moving forward. There are brakets around current page number to highlight this:
<?php
a) {1} 2 3 4 5 => // first frame: [1-5]
b) <= 1 {2} 3 4 5 =>
c) <= 1 2 {3} 4 5 =>
d) <= 1 2 3 {4} 5 =>
e) <= 1 2 3 4 {5} => // HERE IT JUMPS TO THE NEXT FRAME
f) <= {6} 7 8 9 10 => // second frame: [6-10]
g) <= 6 {7} 8 9 10 =>
h) <= 6 7 {8} 9 10 =>
?>
and so on. See what a "jumping window" frame is? When you reach a limit (in the example, you go from page 5 to page 6), it "jumps" to next frame (links from page 6 to 10).
Instead of jumping from one frame to the other, with Pager_Sliding the change is done smoothly, and the current page is always shown at the center of the "window" (except of course for the first and the last pages):
<?php
a) {1} 2 3 4 5 => [15]
b) [1] <= 1 {2} 3 4 5 => [15]
c) [1] <= 1 2 {3} 4 5 => [15] // HERE IT's STARTING WORKING AS DESIGNED
d) [1] <= 2 3 {4} 5 6 => [15] // see: current page number is at the center of the window
e) [1] <= 3 4 {5} 6 7 => [15] // and it stays there...
f) [1] <= 4 5 {6} 7 8 => [15]
g) [1] <= 5 6 {7} 8 9 => [15]
h) [1] <= 6 7 {8} 9 10 => [15]
?>
and so on.
Apart from the different "philosophy", Pager_Sliding is also designed to be highly customizable.
UPDATE: since Pager 2.x release, every option once only available
in Pager_Sliding is now fully implemented in Pager too, and more. As stated in the
Intro, Pager_Sliding is now deprecated in favour of the new
Pager v.2.x. You can have both behaviours ("Jumping" and
"Sliding") in Pager v.2.x, just change the mode
.
Please refer to Pager docs for more details.
There are *many* options to change the look and feel of the links in your page. They are explained in the constructor docs.
It can work with Apache mod_rewrite module too, see the examples for that.
It is "template"-friendly, i.e. you can assign the processed links and page numbers to your custom vars and use them to draw your link bar according to your page layout, without worrying about the underlying logic.
You can easily extend the class; this is useful if you plan to use the class in many different pages and you don't want to set the same options everytime: just set your preferred default values in your extending class and you're done!
integer getCurrentPageID (
)
integer
- Current page number.
array Pager_Sliding::getLinks (
integer $pageID
= null
)
integer $pageID
- Optional pageID. If specified,
linksfor that page are provided instead of current one.
return back/pages/next/first/last/all links, both as numeric and associative array.
mixed Pager_Sliding::getNextPageID (
)
If current page is last page this function returns FALSE, otherwise returns next page number.
return Next page number or FALSE
array Pager_Sliding::getOffsetByPageId (
integer $pageid
= null
)
Eg, if you pass it pageID
= 5 and your delta
is 2 it will return you 3 and 7.
PageID of 6 would give you 4 and 8
NB: The behaviour of this function could be misleading: it was left only
for compatibility with PEAR::Pager.
It could raise some confusion when pageID is within delta
positions from an extreme: in fact this method returns also the extremes, while
$this->_getPageLinks leaves them out.
Pager_Sliding works this way: if pageID is NOT an extreme, show first and
last page within brackets: [1] << 5 | _6_ | 7 >> [15]
So when dealing with pageID within delta positions from an extreme,
this method would return the extreme as well, while $this->_getPageLinks
would return (for instance) 2 | _3_ | 4 | 5 even if pageID is 3 and delta
is 2. Consider this method deprecated and/or subject
to changes.
integer $pageid
- PageID to get offsets for
return array with first and last offsets
deprecated
array Pager_Sliding::getPageData (
integer $pageID
= null
)
integer $pageID
- Desired page ID (optional)
return array of data for this page.
array Pager_Sliding::getPageIdByOffset (
integer $index
)
integer $index
- Offset to get pageID for
return Page number for this offset
deprecated. It is only here for compatibility with PEAR::Pager.
mixed Pager_Sliding::getPreviousPageID (
)
If current page is first page this function returns FALSE, otherwise returns previous page number.
return Previous page number or FALSE.
bool Pager_Sliding::isFirstPage (
)
return TRUE or FALSE, wrt it is the first page or not.
bool Pager_Sliding::isLastPage (
)
return TRUE or FALSE, wrt it is the last page or not.
bool Pager_Sliding::isLastPageComplete (
)
return TRUE or FALSE, wrt the last page is complete
(i.e. it has perPage
values in the array for the last
page) or not.
int Pager_Sliding::numItems (
)
return integer - Number of items
int Pager_Sliding::numPages (
)
return integer - Number of pages
object &Sliding (
array $options
)
Pager_Sliding constructor takes an associative array of parameters as input values. This is the complete list of these options:
itemData
[array]
Array of items to page.
totalItems
[integer]
Number of items to page (used only if itemData
is not provided).
perPage
[integer]
Number of items to display on each page.
delta
[integer]
Number of page numbers to display before and after the current one.
expanded
[boolean]
if TRUE, window size is always 2*delta+1
linkClass
[string]
Name of CSS class used for link styling.
urlVar
[string]
Name of URL var used to indicate the page number. Default value is "pageID".
path
[string]
Complete path to the page (without the page name).
fileName
[string]
name of the page, with a "%d" if append
== TRUE.
append
[boolean]
If TRUE pageID is appended as GET value to the URL.
If FALSE it is embedded in the URL according to
fileName
specs.
altPrev
[string]
Alt text to display for prev page, on prev link. Default value is "previous page";
altNext
[string]
Alt text to display for next page, on next link. Default value is "next page";
altPage
[string]
Alt text to display before the page number. Default value is "page ".
prevImg
[string]
Something to display instead of "<<". It can be text such as "<< PREV" or an <img/> as well.
nextImg
[string]
Something to display instead of ">>". It can be text such as "NEXT >>" or an <img/> as well.
separator
[string]
What to use to separate numbers. It can be an <img/>, a comma, an hyphen, or whatever.
spacesBeforeSeparator
[integer]
Number of spaces before the separator.
spacesAfterSeparator
[integer]
Number of spaces after the separator.
firstPagePre
[string]
String used before first page number. It can be an <img/>, a "{", an empty string, or whatever.
firstPagePost
[string]
String used after first page number. It can be an <img/>, a "}", an empty string, or whatever.
lastPagePre
[string]
Similar to firstPagePre
, but used for
last page number.
lastPagePost
[string]
Similar to firstPagePost
, but used for
last page number.
curPageLinkClassName
[string]
Name of CSS class used for current page link.
lastPagePost
[boolean]
if there's only one page, don't display pager (returns an empty string).
REQUIRED options are:
fileName
IF
append
==FALSE
(default is TRUE)
itemData
OR totalItems
(if itemData is set, totalItems is overwritten)
object
- a specific Pager_Sliding instance
or a PEAR_Error object, if fails
Provides Packages for working with the Hyper-Text-Transfer-Protocol.
Provides a set of usefull, static functions related to the Hyper-Text-Transfer-Protocol.
Warning: The HTTP package is deprecated. Use the HTTP2 package instead.
string HTTP::date (
integer time
)
Converts a UNIX timestamp into an RFC compliant HTTP header line. This function honors the "y2k_compliance" php.ini directive.
The HTTP package is deprecated. Use HTTP2::date instead.
integer $time
- a UNIX timestamp
string
- a RFC compliant date header line
This function can be called statically.
array HTTP::head (
string $url
)
Sends a "HEAD" HTTP command to a server and returns the headers in an associative array.
The HTTP package is deprecated. Use HTTP2::head instead.
HEAD request to example.com
<?php
require_once "PEAR.php";
require_once "HTTP.php";
$result = HTTP::head("http://example.com/");
if (PEAR::isError($result)) {
echo "Error: " . $result->getMessage();
} else {
echo "<pre>";
print_r($result);
echo "</pre>";
}
?>
The output of the print_r() call is shown below.
string $url
- a valid absolute URL
array
- an array containing the header lines
or a PEAR_Error.
Example output:
<?php
Array
(
[response_code] => 200
[response] => HTTP/1.1 200 OK
[Date] => Tue, 25 Nov 2003 22:08:57 GMT
[Server] => Apache/1.3.27 (Unix) (Red-Hat/Linux)
[Last-Modified] => Wed, 08 Jan 2003 23:11:55 GMT
[ETag] => "3f80f-1b6-3e1cb03b"
[Accept-Ranges] => bytes
[Content-Length] => 438
[Connection] => close
[Content-Type] => text/html
)
?>
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"HTTP::head Error $errstr ($erno )"
|
Connection to server failed |
Check connectivity of your host and the given URL in
$url
|
This function can be called statically.
string HTTP::negotiateLanguage (
array $supported
, string $default = 'en_US'
)
Negotiate language with the user's browser through the Accept-Language HTTP header or the user's host address. Language codes are generally in the form "ll" for a language spoken in only one country, or "ll-CC" for a language spoken in a particular country. For example, U.S. English is "en-US", while British English is "en-UK". Portugese as spoken in Portugal is "pt-PT", while Brazilian Portugese is "pt-BR". Two-letter country codes can be found in the ISO 3166 standard.
Quantities in the Accept-Language:
header are
supported, for example:
Accept-Language: en-UK;q=0.7, en-US;q=0.6, no;q=1.0, dk;q=0.8
The HTTP package is deprecated. Use HTTP2::negotiateLanguage instead.
Usage example
<?php
require_once "HTTP.php";
$supported = array("de" => true, "en-US" => true);
echo HTTP::negotiateLanguage($supported);
?>
This example negotiates with the user agent if any of the
languages, which are specified in supported
,
are supported on the user's system. If the negotiation has a
positive result, the language code of the most preferred language
is printed. Otherwise the default language code (en-US) is
printed.
Being able to perform language negotiation is a big help when developing internationalized website with pages, that are available in more than one language. Using negotiation, the user will always get pages in the language which he prefers. (Assuming that their user agent is configured properly.)
array $supported
- an
associative array indexed by language codes (country codes)
supported by the application. Values must evaluate to TRUE.
string $default
- the default
language that should be used if none of the other languages
are found during negotiation.
string
- a language code
This function can be called statically.
The returned language is only a hint! Sending the accepted languages by the client is optional. The language settings of the browser do not have to meet the user's native language - for example a german traveller in a spanish internet cafe. You can improve your result by combining it with the result of the Net_Geo package. Apart from that, you should still give the user the chance to manually choose their preferred language menu.
void HTTP::redirect (
string url
)
This function redirects the client. This is done by issuing
a Location:
header and exiting.
The HTTP package is deprecated. Use HTTP2::redirect instead.
Redirecting to another site
<?php
require_once 'HTTP.php';
HTTP::redirect("http://example.com/");
?>
Local redirect
<?php
require_once 'HTTP.php';
HTTP::redirect("/foo.php");
?>
This will redirect the client to /foo.php
.
The method will take care of adding the right hostname as
required by RFC 2616.
string $url
- the new URL, where
to client should be redirected to.
This function can be called statically.
Avoid sending any kind of data to the client before calling redirect().
The location header requires an absolute URL. If not given,
redirect() tries to build one from
$url
. So if the redirect fails,
set the absolute URL manually as argument.
Provides a set of useful functions related to the Hyper-Text-Transfer-Protocol. PHP 5 compatible.
string HTTP::date (
integer time
)
Converts a UNIX timestamp into an RFC compliant HTTP header line. This function honors the "y2k_compliance" php.ini directive.
integer $time
- a UNIX timestamp
string
- a RFC compliant date header line
This function can not be called statically.
array HTTP2::head (
string $url
)
Sends a "HEAD" HTTP command to a server and returns the headers in an associative array.
HEAD request to example.com
<?php
require_once "PEAR.php";
require_once "HTTP2.php";
$http = new HTTP2();
try {
$result = $http->head("http://example.com/");
echo "<pre>";
print_r($result);
echo "</pre>";
} catch (HTTP2_Exception $e) {
echo "Error: " . $e->getMessage();
}
?>
The output of the print_r() call is shown below.
string $url
- a valid absolute URL
array
- an array containing the header lines
or a PEAR_Error.
Example output:
<?php
Array
(
[response_code] => 200
[response] => HTTP/1.1 200 OK
[Date] => Tue, 25 Nov 2003 22:08:57 GMT
[Server] => Apache/1.3.27 (Unix) (Red-Hat/Linux)
[Last-Modified] => Wed, 08 Jan 2003 23:11:55 GMT
[ETag] => "3f80f-1b6-3e1cb03b"
[Accept-Ranges] => bytes
[Content-Length] => 438
[Connection] => close
[Content-Type] => text/html
)
?>
An exception of type HTTP2_Exception is thrown when an error occurs.
This function can not be called statically.
string HTTP2::negotiateLanguage (
array $supported
, string $default = 'en_US'
)
Negotiate language with the user's browser through the Accept-Language HTTP header or the user's host address. Language codes are generally in the form "ll" for a language spoken in only one country, or "ll-CC" for a language spoken in a particular country. For example, U.S. English is "en-US", while British English is "en-UK". Portugese as spoken in Portugal is "pt-PT", while Brazilian Portugese is "pt-BR". Two-letter country codes can be found in the ISO 3166 standard.
Quantities in the Accept-Language:
header are
supported, for example:
Accept-Language: en-UK;q=0.7, en-US;q=0.6, no;q=1.0, dk;q=0.8
Usage example
<?php
require_once "HTTP2.php";
$supported = array("de" => true, "en-US" => true);
$http = new HTTP2();
echo $http->negotiateLanguage($supported);
?>
This example negotiates with the user agent if any of the
languages, which are specified in supported
,
are supported on the user's system. If the negotiation has a
positive result, the language code of the most preferred language
is printed. Otherwise the default language code (en-US) is
printed.
Being able to perform language negotiation is a big help when developing internationalized website with pages, that are available in more than one language. Using negotiation, the user will always get pages in the language which he prefers. (Assuming that their user agent is configured properly.)
array $supported
- an
associative array indexed by language codes (country codes)
supported by the application. Values must evaluate to TRUE.
string $default
- the default
language that should be used if none of the other languages
are found during negotiation.
string
- a language code
This function can not be called statically.
The returned language is only a hint! Sending the accepted languages by the client is optional. The language settings of the browser do not have to meet the user's native language - for example a german traveller in a spanish internet cafe. You can improve your result by combining it with the result of the Net_Geo package. Apart from that, you should still give the user the chance to manually choose their preferred language menu.
array HTTP2::parseLinks (
array|string $lines
)
Parses RFC 5988 ("Web Linking") HTTP link headers and splits each of the links up into an array you can then use further.
Usage example
<?php
require_once 'HTTP2.php';
//Link headers that we got from somewhere, e.g.
// HTTP_Request2_Response::getHeader('link')
$link = '<http://pear.php.net/webmention.php>; rel="webmention"';
$http = new HTTP2();
$links = $http->parseLinks($link);
var_dump($links);
?>
The script produces this output:
array(1) { [0] => array(2) { '_uri' => string(34) "http://pear.php.net/webmention.php" 'rel' => array(1) { [0] => string(10) "webmention" } } }
Note that a single link header may contain multiple links. For that reason, the method returns an array of link arrays.
array|string $lines
-
A single Link:
header value, or an array
of multiple Link:
header values, without
the Link:
.
array
- Array of arrays.
Each array describes a link.
The URI is the key _uri
,
all parameters have their names as keys.
The parameters title*
,
rel
and rev
are multi-valued
and thus have an array as their value.
This function can not be called statically.
void HTTP2::redirect (
string url
)
This function redirects the client. This is done by issuing
a Location:
header and exiting.
Redirecting to another site
<?php
require_once 'HTTP2.php';
$http = new HTTP2();
$http->redirect("http://example.com/");
?>
Local redirect
<?php
require_once 'HTTP.php';
$http = new HTTP2();
$http->redirect("/foo.php");
?>
This will redirect the client to /foo.php
.
The method will take care of adding the right hostname as
required by RFC 2616.
string $url
- the new URL, where
to client should be redirected to.
This function can not be called statically.
Avoid sending any kind of data to the client before calling redirect().
The location header requires an absolute URL. If not given,
redirect() tries to build one from
$url
. So if the redirect fails,
set the absolute URL manually as argument.
A simple HTTP client class. The class wraps around HTTP_Request providing a higher-level API for performing multiple HTTP requests. It handles HTTP redirects, stores cookies and sets referrers between requests.
HTTP_Client
void constructor HTTP_Client::HTTP_Client (
array $defaultRequestParams
= null
, array $defaultHeaders
= null
)
Sets default request parameters and default headers.
$defaultRequestParams
Parameters to pass to HTTP_Request's constructor
$defaultHeaders
Default headers to send on every request
throws no exceptions thrown
This function can not be called statically.
boolean HTTP_Client::attach (
object HTTP_Request_Listener &$listener
, boolean $propagate
= false
)
The attached Listeners are notified of the following events:
'request'
'httpSuccess'
'httpRedirect'
'httpError'
If $propagate
is TRUE the Listener will be attached to the created HTTP_Request objects and will be notified of their events as well.
&$listener
Listener instance to attach
$propagate
Whether the listener should be attached to the created HTTP_Request objects
returns whether the listener was successfully attached
throws no exceptions thrown
see detach()
This function can not be called statically.
array& HTTP_Client::currentResponse (
)
The returned array has the following keys: 'code'
, 'headers'
, 'body'
.
throws no exceptions thrown
This function can not be called statically.
boolean HTTP_Client::detach (
object HTTP_Request_Listener &$listener
)
This package is not documented yet.
&$listener
Listener instance to detach
returns whether the listener was successfully detached
throws no exceptions thrown
see attach()
This function can not be called statically.
integer HTTP_Client::get (
string $url
, mixed $data
= null
, boolean $preEncoded
= false
)
This package is not documented yet.
$url
URL
$data
additional data to send
$preEncoded
Whether the data is already urlencoded
returns HTTP response code
throws PEAR_Error
This function can not be called statically.
integer HTTP_Client::head (
string $url
)
This package is not documented yet.
$url
URL
returns HTTP response code
throws PEAR_Error
This function can not be called statically.
integer HTTP_Client::post (
string $url
, mixed $data
, boolean $preEncoded
= false
, array $files = array()
)
This package is not documented yet.
$url
URL
$data
Data to send
$preEncoded
Whether the data is already urlencoded
$files
Files to upload. Elements of the array should have the form: array(name, filename(s)[, content type]), see HTTP_Request::addFile()
returns HTTP response code
throws PEAR_Error
This function can not be called statically.
void HTTP_Client::reset (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void HTTP_Client::setDefaultHeader (
mixed $name
, string $value
= null
)
These are standard HTTP headers that will be sent by all created HTTP_Request objects.
$name
header name or array ('header name' => 'header value')
$value
header value if $name is not an array
throws no exceptions thrown
This function can not be called statically.
void HTTP_Client::setMaxRedirects (
int $value
)
Setting this value to 0 disables redirect processing. If it is not 0 and the number of redirects after a request is bigger than this number, then an error will be raised.
$value
Max number of redirects to process
throws no exceptions thrown
This function can not be called statically.
void HTTP_Client::setRequestParameter (
mixed $name
, string $value
= null
)
These parameters will be passed to constructors of created HTTP_Request instances.
$name
parameter name or array ('parameter name' => 'parameter value')
$value
parameter value if $name is not an array
throws no exceptions thrown
This function can not be called statically.
Stores cookies and passes them between HTTP requests. While this class is a part of HTTP_Client package, it can be used separately.
HTTP_Client_CookieManager
void constructor HTTP_Client_CookieManager::HTTP_Client_CookieManager (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void HTTP_Client_CookieManager::addCookie (
array $cookie
)
This package is not documented yet.
$cookie
An array representing cookie, this function expects all of the array's fields ('name'
, 'value'
, 'domain'
, 'path'
, 'expires'
, 'secure'
) to be set.
throws no exceptions thrown
This function can not be called statically.
void HTTP_Client_CookieManager::passCookies (
object An &$request
)
The method sends only the cookies that should be sent with the request and it adds them in right order.
&$request
HTTP_Request object
throws no exceptions thrown
This function can not be called statically.
void HTTP_Client_CookieManager::reset (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void HTTP_Client_CookieManager::updateCookies (
object HTTP_Request &$request
)
This method gets the cookies from HTTP response received by HTTP_Request instance and adds them to the object's internal list.
&$request
HTTP_Request object containing the HTTP response, i.e. its sendRequest() should already be called.
throws no exceptions thrown
see passCookies(), addCookie()
This function can not be called statically.
Send HTTP Downloads
HTTP_Download provides an interface to easily send any arbitrary data to HTTP clients. HTTP_Download can gain its data from variables, files or stream resources.
With this package you can easily handle (hidden) downloads. Hidden means not accessible by the public - for instance if you want to restrict access to particular downloads.
It supports HTTP compression, caching and partial downloads, resuming and sending raw data, for example from database BLOBs.
ATTENTION: You shouldn't use this package together with ob_gzhandler or zlib.output_compression enabled in your php.ini, especially if you want to send already gzipped data!
Have a look at the following examples:
Static send:
<?php
1 $params = array(
2 'file' => '../hidden/download.tgz',
3 'contenttype' => 'application/x-gzip',
4 'contentdisposition' => array(HTTP_DOWNLOAD_ATTACHMENT, 'latest.tgz'),
5 );
6
7 $error = HTTP_Download::staticSend($params, false);
?>
Send a hidden file:
<?php
1 $dl = &new HTTP_Download();
2 $dl->setFile('../hidden/download.tgz');
3 $dl->setContentDisposition(HTTP_DOWNLOAD_ATTACHMENT, 'latest.tgz');
4 // with ext/magic.mime
5 // $dl->guessContentType();
6 // else:
7 $dl->setContentType('application/x-gzip');
8 $dl->send();
?>
Send arbitrary data:
<?php
1 $dl = &new HTTP_Download();
2 $dl->setData($data);
3 $dl->setLastModified($unix_timestamp);
4 $dl->setContentType('application/x-gzip');
5 $dl->setContentDisposition(HTTP_DOWNLOAD_ATTACHMENT, 'latest.tgz');
6 $dl->send();
?>
Limiting bandwidth:
<?php
1 $dl = &new HTTP_Download();
2 $dl->setFile('huge_file.bin');
3 $dl->setBufferSize(25 * 1024); // 25 K
4 $dl->setThrottleDelay(1); // 1 sec
5 $dl->send();
?>
Sending a PostgreSQL LOB:
<?php
1 require_once 'HTTP/Download.php';
2 require_once 'HTTP/Download/PgLOB.php';
3 $dl = &new HTTP_Download();
4 $dl->setResource(
5 HTTP_Download_PgLOB::open(pg_connect('dbname=lobs'), 12345));
6 $dl->send();
?>
object new HTTP_Download
(
array $params = array()
)
Creates an instance of an HTTP_Download object and sets supplied parameters.
array $params
- An associative array of parameters:
one of:
$params['file']
- filepath
$params['data']
- raw data
$params['resource']
- resource handle
and any of:
$params['gzip']
- whether to gzip the download
$params['cache']
- whether to allow client side caching of the download
$params['lastmodified']
- unix timestamp of last modification
$params['contenttype']
- content type
$params['contentdisposition']
- content disposition
$params['buffersize']
- amount of bytes read at once from files or resources
$params['throttledelay']
- amount of seconds to sleep after each chunk that has been sent
$params['cachecontrol']
- cache privacy and validity
mixed
HTTP_Download::setParams
(
array
$params
)
Set the parameters for the download.
You can use this method as an alternative to passing the parameters in the constructor or calling the setter of each parameter.
array $params
- An associative array of parameters:
one of:
$params['file']
- filepath
$params['data']
- raw data
$params['resource']
- resource handle
and any of:
$params['gzip']
- whether to gzip the download
$params['cache']
- whether to allow client side caching of the download
$params['lastmodified']
- unix timestamp of last modification
$params['contenttype']
- content type
$params['contentdisposition']
- content disposition
$params['buffersize']
- amount of bytes read at once from files or resources
$params['throttledelay']
- amount of seconds to sleep after each chunk that has been sent
$params['cachecontrol']
- cache privacy and validity
See also setFile(), setData(), setResource(), setGzip(), setCache(), setContentType(), setLastModified(), setContentDisposition(), setBufferSize(), setThrottleDelay(), setCacheControl().
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
mixed
HTTP_Download::setFile
(
string
$file
,
bool
$send_404 = true
)
Set the path to the file for the download.
string
$file
- file path
bool
$send_404 = true
- whether to send "HTTP 404 File Not Found",
if file couldn't be found
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
void
HTTP_Download::setData
(
mixed
$data = null
)
Set $data
to null if you want to unset.
Otherwise you can send any arbitrary data ie. from a database BLOB.
mixed
$data = null
- any arbietrary data as string to send or null to unset
This function can not be called statically.
mixed
HTTP_Download::setResource
(
mixed
$handle = null
)
Set the resource handle to retrieve the data for the download.
The resource handle supplied will be closed after sending the download.
Set $handle
to null if you want to unset.
This cannot be used with resources of databases that populate their BLOBs as resource handles like PostgreSQL. A possible solution would be to write a stream wrapper.
Returns a PEAR_Error if $handle
is no valid resource or not null.
mixed
$handle = null
- (int) resource handle or null
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
mixed
HTTP_Download::setGzip
(
bool
$gzip = false
)
Define whether you want to send the download gzipped or not.
Returns a PEAR_Error if ext/zlib is not available.
bool
$gzip = false
- whether to gzip the download on the fly
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
void
HTTP_Download::setCache
(
bool
$cache = true
)
Define whether you want to allow caching of the download on the clients side.
If set to true (default), HTTP_Download will emit some caching headers like Cache-Control, Last-Modified and ETag.
bool
$cache = true
- whether to allow caching of the download
This function can not be called statically.
void
HTTP_Download::setCacheControl
(
string
$cache = "public"
,
int
$maxage = 0
)
Define the contents of the Cache-Control header.
If set to set to "public", proxies are adviced to cache the response, if set to "private", proxies are adviced to do not.
The maxage paramter controls the amount of seconds an entity is suggested to be cached. Many user agents won't even send a request for subsequent requests to the same resource within the specified time frame.
string
$cache = "public"
- whether to allow proxy caching
int
$maxage = 0
- maximum age of the cached entity
This function can not be called statically.
mixed
HTTP_Download::setBufferSize
(
int
$size = 2097152
)
The amount of bytes specified as buffer size is the maximum amount of data read at once from resources or files. The default size is 2M (2097152 bytes).
Be aware that if you enable gzip compression and you set a very low buffer size that the actual file size may grow due to added gzip headers for each sent chunk of the specified size.
Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_PARAM) if $size is not greater than 0 bytes.
int
$size = 2097152
- amount of bytes to buffer
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
void
HTTP_Download::setThrottleDelay
(
float
$seconds = 0
)
Set the amount of seconds to sleep after each chunck that has been sent. One can implement some sort of throttle through adjusting the buffer size and the throttle delay. With a setting of buffersize=25600 and throttledelay=1 HTTP_Download will sleep a second after each 25 K of data sent.
Just be aware that if gzip'ing is enabled, decreasing the chunk size too much leads to proportionally increased network traffic due to added gzip header and bottom bytes around each chunk.
float
$seconds = 0
- amount of seconds to sleep
Returns void.
This function can not be called statically.
mixed
HTTP_Download::setContentType
(
string
$content_type = 'application/x-octetstream'
)
Set a reasonable content type for the download.
Examples:
application/pdf
application/zip
text/css
Returns PEAR_Error if $content_type
doesn't seem to be valid.
string
$content_type = 'application/x-octetstream'
- a reasonable content type
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
void
HTTP_Download::setLastModified
(
int
$last_modified
)
Set the time (unix timestamp) of last modification of the download.
This is usually determined by filemtime($file
) in
setFile().
int
$last_modified
- unix timestamp of last modification time
This function can not be called statically.
void
HTTP_Download::setContentDisposition
(
string
$disposition = HTTP_DOWNLOAD_ATTACHMENT
,
string
$file_name = null
)
Set content disposition of the download.
"Content-Disposition" is not HTTP compliant, but most browsers follow this header, so it was borrowed from MIME standard. It looks like this: "Content-Disposition: attachment; filename=example.tgz".
string
$disposition = HTTP_DOWNLOAD_ATTACHMENT
- the disposition of the download (either 'attachment' or 'inline')
string
$file_name = null
- the file name the browser's download window should show
This function can not be called statically.
mixed
HTTP_Download::guessContentType
(
)
Use only if you send a file.
First we try to use MIME_Type, if installed, to detect the content type, else we check if ext/mime_magic is loaded and properly configured.
Returns PEAR_Error if:
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
mixed
HTTP_Download::send
(
bool
$autoSetContentDisposition = true
)
Send the download.
Returns PEAR_Error if:
bool
$autoSetContentDisposition = true
- automatically sets the Content-Disposition to
HTTP_DOWNLOAD_ATTACHMENT header if not already set
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
mixed
HTTP_Download::staticSend
(
array
$params
,
bool
$guess = false
)
Send a download statically without instantiating an HTTP_Download object.
array $params
- An associative array of parameters:
one of:
$params['file']
- filepath
$params['data']
- raw data
$params['resource']
- resource handle
and any of:
$params['gzip']
- whether to gzip the download
$params['cache']
- whether to allow client side caching of the download
$params['lastmodified']
- unix timestamp of last modification
$params['contenttype']
- content type
$params['contentdisposition']
- content disposition
$params['buffersize']
- amount of bytes read at once from files or resources
$params['throttledelay']
- amount of seconds to sleep after each chunk that has been sent
bool
$guess = false
- whether to call
guessContentType()
See also setFile(), setData(), setResource(), setGzip(), setCache(), setContentType(), setLastModified(), setContentDisposition(), setBufferSize(), setThrottleDelay(), setParams().
Returns TRUE on success, PEAR_Error on failure.
This function should be called statically.
mixed
HTTP_Download::sendArchive
(
string
$name
,
mixed
$files
,
string
$type = HTTP_DOWNLOAD_TGZ
,
string
$add_path = ''
,
string
$strip_path = ''
)
Send an archive created on the fly by Archive_Tar or Archive_Zip.
The parameter $files
can be an array of files/directories
or a space separated string of files/directories which should be packed
to an archive.
The parameter $type
can be one of
HTTP_DOWNLOAD_TAR,
HTTP_DOWNLOAD_TGZ,
HTTP_DOWNLOAD_BZ2 and
HTTP_DOWNLOAD_ZIP.
The usage of this method is deprecated. Use HTTP_Download_Archive::send() instead.
string
$name
- the name the archive should have
mixed
$files
- list of files/directories
string
$type =
- the format of the archive (TAR, TGZ, BZ2 or ZIP)
string
$add_path = ''
- path that should be prepended to the files
string
$strip_path = ''
- path that should be stripped from the files
Returns TRUE on success, PEAR_Error on failure.
This function should be called statically.
The package provides an easy way to perform HTTP requests. It supports GET/POST/HEAD/TRACE/PUT/DELETE, Basic authentication, Proxy, Proxy Authentication, SSL, file uploads etc.
With this package, one can easily perform HTTP request from within PHP scripts. It support GET/POST/HEAD/TRACE/PUT/DELETE, Basic authentication, Proxy, Proxy Authentication, SSL, file uploads etc.
Because of the above mentioned features HTTP_Request makes it possible to mimic big parts of web browsers such as the widely-known Mozilla browser in PHP applications. Possible application areas are:
Fetches yahoo.com and displays it
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://www.yahoo.com/");
if (!PEAR::isError($req->sendRequest())) {
echo $req->getResponseBody();
}
?>
Fetching two website in a row
In this example, two websites are fetched and displayed. To the first one a POST parameter is passed. The POST data stack is cleared before the second website is fetched.
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://www.php.net");
$req->setMethod(HTTP_REQUEST_METHOD_POST);
$req->addPostData("Foo", "bar");
if (!PEAR::isError($req->sendRequest())) {
$response1 = $req->getResponseBody();
} else {
$response1 = "";
}
$req->setMethod(HTTP_REQUEST_METHOD_GET);
$req->setURL("http://pear.php.net");
$req->clearPostData();
if (!PEAR::isError($req->sendRequest())) {
$response2 = $req->getResponseBody();
} else {
$response2 = "";
}
echo $response1;
echo $response2;
?>
Basic Authentication is a challenge-response mechanism described in RFC 2617.
Basic Authentication
The following example assumes that one wants to fetch a page
/protected.html
on the host
example.com
that is protected using Basic
Authentication. The necessary username to pass the authentication is
johndoe
and the appendant password is
foo
.
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://example.com/protected.html");
$req->setBasicAuth("johndoe", "foo");
$response = $req->sendRequest();
if (PEAR::isError($response)) {
echo $response->getMessage();
} else {
echo $req->getResponseBody();
}
?>
Adding a cookie to the request
In this example a cookie named version
is added
to the HTTP request. The value of this cookie is the version string
of the PHP interpreter that is running the instance of
HTTP_Request.
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://example.com/");
$req->addCookie("version", phpversion());
$response = $req->sendRequest();
if (PEAR::isError($response)) {
echo $response->getMessage();
} else {
echo $req->getResponseBody();
}
?>
Reading cookies from a HTTP response
Reading cookies that come with a HTTP response is shown in this following example.
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://example.com/");
$response = $req->sendRequest();
if (PEAR::isError($response)) {
echo $response->getMessage();
} else {
print_r($req->getResponseCookies());
}
?>
Upload a PDF document
In this example a file named
/home/johndoe/text.pdf
is uploaded to the
remote machine upload.example.com
. Additionally Basic
Authentication is used to ensure that John is allowed to upload
something.
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://upload.example.com/upload.php");
$req->setBasicAuth("johndoe", "foo");
$req->setMethod(HTTP_REQUEST_METHOD_POST);
$result = $req->addFile('file_upload_field', '/home/johndoe/text.pdf', 'application/pdf');
if (PEAR::isError($result)) {
echo $result->getMessage();
} else {
$response = $req->sendRequest();
if (PEAR::isError($response)) {
echo $response->getMessage();
} else {
echo $req->getResponseBody();
}
}
?>
Adding a custom request header
In this example a HTTP header X-PHP-Version
is
added to the HTTP request. The value of this header is the version
string of the PHP interpreter that is running the instance of
HTTP_Request.
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://example.com/");
$req->addHeader("X-PHP-Version", phpversion());
$response = $req->sendRequest();
if (PEAR::isError($response)) {
echo $response->getMessage();
} else {
echo $req->getResponseBody();
}
?>
Headers that have been added to the HTTP_Request object can be removed with the method removeHeader(), before sendRequest() has been called.
Using a proxy server with anonymous access
In this example it is assumed that one wants to use the machine with
the hostname proxy.example.com
, where a proxy
server is listening on port 8080
, to proxy the
outgoing connection to example.com.
The second parameter of setProxy() is optional
and defaults to 8080
.
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://example.com/");
$req->setProxy("proxy.example.com", 8080);
?>
Using proxy authorization
This is the same example as above, except that a username/password
tuple is provided, which authorizes the user at the proxy server:
The username is johndoe
and the appendant
password is foo
.
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://example.com/");
$req->setProxy("proxy.example.com", 8080, "johndoe", "foo");
?>
Because HTTP is a protocol based on the Request - Response scheme, every HTTP request is followed by a HTTP response. HTTP_Request offers several methods to evaluate the information from these responses.
A important part of the HTTP response is the response code. The most
well-known response code probably is 404
, which
you may have seen in your browser at several occasions. The meaning
of 404
is that the requested ressource could not
be found. A complete list of status codes can be found in RFC 2616.
Checking the response code
<?php
require_once "HTTP/Request.php";
$urls = array(
"http://www.example.com/",
"http://example.com/thisdoesnotexist.html"
);
$req =& new HTTP_Request("");
foreach ($urls as $url) {
$req->setURL($url);
$req->sendRequest();
$code = $req->getResponseCode();
switch ($code) {
case 404:
echo "Document not found\n";
break;
case 200:
echo "Everything's ok\n";
break;
/* ... */
}
}
?>
Similar to a HTTP request a HTTP response consists of a header and a body. HTTP_Request offers a method to access the header of the response.
Getting all headers from the response
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://example.com/");
$req->sendRequest();
foreach ($req->getResponseHeader() as $name => $value) {
echo $name . " = " . $value . "\n";
}
?>
This will print all headers and the appendant values.
Getting a specific header
<?php
require_once "HTTP/Request.php";
$req =& new HTTP_Request("http://example.com/");
$req->sendRequest();
echo $req->getResponseHeader("Date");
?>
This will print the value of the Date:
header.
Fetching the cookies that are part of the HTTP response is described in the Cookies section.
HTTP_Request_Listener is an abstract class that can be extended to capture events and respond to them as they occur. Included with HTTP_Request is an example of a console-based progress bar. To implement this, the HTTP_Request_DownloadListener class is used, which uses the Console_ProgressBar package to display a download progress meter.
In order to use a listener, it must attach to the specific HTTP_Request or HTTP_Response object that you want to monitor. The attach code is shown at the bottom of the example. As you can see, the event listener is propagated from the HTTP_Request object to any child HTTP_Response objects, and attaching only need happen to the first HTTP_Request object.
Download progress bar with HTTP_Request_Listener
<?php
/**
* An example of Listener usage with HTTP_Request. This downloads and saves
* the file displaying the progress bar in the process.
*
* Note two things:
* 1) The file should be run in console, not in browser;
* 2) You should turn output buffering OFF for this to work properly.
*/
require_once 'HTTP/Request.php';
require_once 'HTTP/Request/Listener.php';
require_once 'Console/ProgressBar.php';
PEAR::setErrorHandling(PEAR_ERROR_DIE);
set_time_limit(0);
class HTTP_Request_DownloadListener extends HTTP_Request_Listener
{
/**
* Handle for the target file
* @var int
*/
var $_fp;
/**
* Console_ProgressBar intance used to display the indicator
* @var object
*/
var $_bar;
/**
* Name of the target file
* @var string
*/
var $_target;
/**
* Number of bytes received so far
* @var int
*/
var $_size = 0;
function HTTP_Request_DownloadListener()
{
$this->HTTP_Request_Listener();
}
/**
* Opens the target file
* @param string Target file name
* @throws PEAR_Error
*/
function setTarget($target)
{
$this->_target = $target;
$this->_fp = @fopen($target, 'wb');
if (!$this->_fp) {
PEAR::raiseError("Cannot open '{$target}'");
}
}
function update(&$subject, $event, $data = null)
{
switch ($event) {
case 'sentRequest':
$this->_target = basename($subject->_url->path);
break;
case 'gotHeaders':
if (isset($data['content-disposition']) &&
preg_match('/filename="([^"]+)"/', $data['content-disposition'], $matches)) {
$this->setTarget(basename($matches[1]));
} else {
$this->setTarget($this->_target);
}
$this->_bar =& new Console_ProgressBar(
'* ' . $this->_target . ' %fraction% KB [%bar%] %percent%', '=>', '-',
79, (isset($data['content-length'])? round($data['content-length'] / 1024): 100)
);
$this->_size = 0;
break;
case 'tick':
$this->_size += strlen($data);
$this->_bar->update(round($this->_size / 1024));
fwrite($this->_fp, $data);
break;
case 'gotBody':
fclose($this->_fp);
break;
case 'connect':
case 'disconnect':
break;
default:
PEAR::raiseError("Unhandled event '{$event}'");
} // switch
}
}
// Try using any other package if you like, but choose the bigger ones
// to be able to see the progress bar
$url = 'http://pear.php.net/get/HTML_QuickForm-stable';
$req =& new HTTP_Request($url);
$download =& new HTTP_Request_DownloadListener();
$req->attach($download);
$req->sendRequest(false);
?>
The HTTP_Request class sends these events:
connect
- upon server connection, this event is sent
sentRequest
- after the request was sent, this event is sent
disconnect
- after server disconnect, this event is sent
The HTTP_Response class sends these events:
gotHeaders
- this event is sent after receiving response headers
(headers are passed in $data as an associative array)
tick
- this event is sent on receiving a part of response body
(the part is passed in $data as a string)
gzTick
- this event is sent on receiving a part of response body
that is gzip-compressed (the part is passed in $data as a string)
gotBody
- this event is sent on after receiving the
complete response body (the decoded body is passed as a string in $data if it was gzipped)
PHP5 rewrite of HTTP_Request package (with some features from HTTP_Client). Provides cleaner API and pluggable Adapters:
Supports POST requests with data and file uploads, basic and digest authentication, cookies, managing cookies across requests, proxies, gzip and deflate encodings, redirects, monitoring the request progress with Observers...
HTTP_Request2 package provides an easy way for PHP applications to perform HTTP requests. It supports a large subset of Hypertext Transfer Protocol features, and can be used for the following:
Performing a request with HTTP_Request2 consists of the following steps
Fetches and displays PEAR website homepage
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2('http://pear.php.net/', HTTP_Request2::METHOD_GET);
try {
$response = $request->send();
if (200 == $response->getStatus()) {
echo $response->getBody();
} else {
echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
$response->getReasonPhrase();
}
} catch (HTTP_Request2_Exception $e) {
echo 'Error: ' . $e->getMessage();
}
?>
HTTP_Request2 constructor and HTTP_Request2::setConfig() method accept the following configuration parameters. You can also use HTTP_Request2::getConfig() to get the values of these parameters. Note that using an unknown parameter name will result in an exception.
Parameter name | Description | Expected type | Default value |
---|---|---|---|
adapter |
Adapter to use, may also be set by HTTP_Request2::setAdapter() method. (See: Adapters) | string|object | 'HTTP_Request2_Adapter_Socket' |
connect_timeout |
Connection timeout in seconds. Exception will be thrown if connecting to remote host takes more than this number of seconds. | integer | 10 |
timeout |
Total number of seconds a request can take. Use 0 for no limit,
should be greater than connect_timeout if set. Exception will be thrown
if execution of HTTP_Request2::send()
takes more than this number of seconds. |
integer | 0 |
use_brackets |
Whether to append [] to array variable names. This is useful when performing a request to a remote PHP page which expects array parameters to have brackets, should be turned off in the other cases. | boolean | TRUE |
protocol_version |
HTTP protocol version to use, '1.0' or '1.1' |
string | '1.1' |
buffer_size |
Buffer size to use for reading and writing. Leave this at the default unless you know what you are doing. | integer | 16384 |
store_body |
Whether to store response body in response object. Set to false if receiving a huge response and using an Observer to save it. (See: Observers) | boolean | TRUE |
digest_compat_ie |
Whether to imitate behaviour of MSIE 5 and 6 in using URL without query string in digest authentication | boolean | FALSE |
local_ip |
Specifies the IP address that will be used for accessing the network, if the computer running HTTP_Request2 has more than one (since 2.2.0) | string | NULL |
Parameter name | Description | Expected type | Default value |
---|---|---|---|
follow_redirects |
Whether to automatically follow HTTP redirects in server response | boolean | FALSE |
max_redirects |
Maximum number of redirects to follow | integer | 5 |
strict_redirects |
Whether to keep request method on redirects via status 301 and
302 (TRUE, needed for compatibility with RFC 2616) or switch to GET
(FALSE, needed for compatibility with most browsers). Issues with Curl Adapter |
boolean | FALSE |
Parameter name | Description | Expected type | Default value |
---|---|---|---|
proxy |
Proxy configuration given as an URL, e.g.
'socks5://localhost:1080' , URL is parsed and separate parameters
described below are set (since 2.1.0) |
string | N/A |
proxy_type |
Proxy type, either 'http' or 'socks5' (since
2.1.0) |
string | 'http' |
proxy_host |
Proxy server host | string | '' |
proxy_port |
Proxy server port | integer | '' |
proxy_user |
User name for proxy authentication | string | '' |
proxy_password |
Password for proxy authentication | string | '' |
proxy_auth_scheme |
Proxy authentication scheme, one of HTTP_Request2::AUTH_* constants | string | HTTP_Request2::AUTH_BASIC |
Parameter name | Description | Expected type | Default value |
---|---|---|---|
ssl_verify_peer |
Whether to verify peer's SSL certificate. Note that this is on by default, to follow
the behaviour of modern browsers and current cURL version.
Peer verification is likely to fail if you don't explicitly provide |
boolean | TRUE |
ssl_verify_host |
Whether to check that Common Name in SSL certificate matches host name. Issues with Socket Adapter. | boolean | TRUE |
ssl_cafile |
Certificate Authority file to verify the peer with (use when
ssl_verify_peer is TRUE)
You can use e.g. cURL's CA Extract tool to get such a file. |
string | NULL |
ssl_capath |
Directory holding multiple Certificate Authority files | string | NULL |
ssl_local_cert |
Name of a file containing local certificate | string | NULL |
ssl_passphrase |
Passphrase with which local certificate was encoded | string | NULL |
While most of the configuration parameters have sane default values and generally can be left alone, you'll definitely need to configure proxy you are using to access websites.
Proxy configuration example
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setConfig(array(
'proxy_host' => 'proxy.example.com',
'proxy_port' => 3128,
'proxy_user' => 'luser',
'proxy_password' => 'sekret',
'proxy_auth_scheme' => HTTP_Request2::AUTH_DIGEST
));
// Now you can perform requests
?>
For SSL peer verification to work, OpenSSL library (used under the hood by both Curl and Socket adapter) needs certificate authority files. However it does not include any such files itself, expecting OS distributions compiling OpenSSL library to provide proper default locations.
Unfortunately OpenSSL extension of PHP below version 5.6 does not
try to use the distribution-default values for CA file / CA path when explicit ones
are not provided. Curl extension, however, does use these defaults, so you can sometimes
be able to use 'verify_peer'
without setting 'ssl_cafile'
for Curl adapter, but not for Socket one (see e.g. bug #18480 and bug #19351).
For versions of PHP below 5.6 the only solution is to provide 'ssl_cafile'
and / or 'ssl_capath'
for every request if using
Socket adapter. Curl adapter will or
will not be able to use the defaults depending on distribution, additionally you can set
curl.cainfo
parameter in php.ini
on PHP 5.3.7+
(it contains the default value for CURLOPT_CAINFO setting to which
'ssl_cafile'
setting is mapped).
PHP version 5.6 will finally enable peer verification by default
for 'ssl'
stream wrapper, so it will also provide more possibilities for
using default values:
openssl.cafile
and openssl.capath
settings in
php.ini
will provide default values for 'cafile'
and 'capath'
SSL stream context options ('ssl_cafile'
and 'ssl_capath'
settings map to these in Socket adapter).
GET
ParametersRequest URL can be set in HTTP_Request2 constructor or via HTTP_Request2::setUrl() method. Both of these accept either a string or an instance of Net_URL2. URL is stored internally as an instance of Net_URL2 that can be accessed via HTTP_Request2::getUrl().
GET
request parameters can be added to URL via Net_URL2::setQueryVariable() and Net_URL2::setQueryVariables():
Setting GET
parameters
<?php
// Explicitly set request method and use_brackets
$request = new HTTP_Request2('http://pear.php.net/bugs/search.php',
HTTP_Request2::METHOD_GET, array('use_brackets' => true));
$url = $request->getUrl();
$url->setQueryVariables(array(
'package_name' => array('HTTP_Request2', 'Net_URL2'),
'status' => 'Open'
));
$url->setQueryVariable('cmd', 'display');
// This will output a page with open bugs for Net_URL2 and HTTP_Request2
echo $request->send()->getBody();
?>
HTTP_Request2 supports both Basic and Digest authentication schemes defined in RFC 2617. Authentication credentials can be set via HTTP_Request2::setAuth() method or given in the request URL (but in the latter case authentication scheme will default to Basic).
Setting authentication credentials
<?php
// This will set credentials for basic auth
$request = new HTTP_Request2('http://user:password@www.example.com/secret/');
// This will set credentials for Digest auth
$request->setAuth('user', 'password', HTTP_Request2::AUTH_DIGEST);
?>
There is currently an issue with Digest authentication support in Curl Adapter due to an underlying PHP cURL extension problem.
Additional request headers can be set via HTTP_Request2::setHeader() method. It also allows removing previosly set headers
Setting request headers
<?php
// setting one header
$request->setHeader('Accept-Charset', 'utf-25');
// setting several headers in one go
$request->setHeader(array(
'Connection' => 'close',
'Referer' => 'http://localhost/'
));
// removing a header
$request->setHeader('User-Agent', null);
?>
Cookies can be added to the request via setHeader() method, but a specialized HTTP_Request2::addCookie() method is also provided
Adding cookies to the request
<?php
$request->addCookie('CUSTOMER', 'WILE_E_COYOTE');
$request->addCookie('PART_NUMBER', 'ROCKET_LAUNCHER_0001');
?>
Since release 2.0.0beta1 the package contains HTTP_Request2_CookieJar class that can be used manage cookies across requests. You can enable this functionality by passing either an existing instance of HTTP_Request2_CookieJar or TRUE to create a new instance to HTTP_Request2::setCookieJar() method. Cookie Jar can be later accessed by HTTP_Request2::getCookieJar().
Cookie jar will automatically store cookies set in HTTP response and pass them to requests with URLs matching cookie parameters. You can also manually add cookies to jar by using HTTP_Request2_CookieJar::store() method.
By default Public Suffix List is used to check whether a cookie domain matches the given URL. The same list is used to restrict cookie setting by Firefox, Chrome and Opera browsers. It can be disabled if needed by HTTP_Request2_CookieJar::usePublicSuffixList().
HTTP_Request2_CookieJar implements Serializable interface and thus can be easily serialized and stored somewhere. You can control whether session cookies stored in a jar should be serialized by calling HTTP_Request2_CookieJar::serializeSessionCookies().
When HTTP_Request2 instance has a cookie jar set, HTTP_Request2::addCookie() method will add a cookie to jar, rather than directly to
'Cookie'
header, using current request URL for setting its'domain'
and'path'
components.
If you are doing a POST
request with Content-Type
'application/x-www-form-urlencoded'
or
'multipart/form-data'
(in other words, emulating POST
form
submission), you can add parameters via HTTP_Request2::addPostParameter() and file uploads via HTTP_Request2::addUpload().
HTTP_Request2
will take care of generating proper request body. File
uploads will be streamed from disk by HTTP_Request2_MultipartBody to reduce memory consumption.
Emulating POST
form submission
<?php
$request = new HTTP_Request2('http://www.example.com/profile.php');
$request->setMethod(HTTP_Request2::METHOD_POST)
->addPostParameter('username', 'vassily')
->addPostParameter(array(
'email' => 'vassily.pupkin@mail.ru',
'phone' => '+7 (495) 123-45-67'
))
->addUpload('avatar', './exploit.exe', 'me_and_my_cat.jpg', 'image/jpeg');
?>
addUpload() can accept either a string with a local file name or a pointer to
an open file, as returned by fopen(). You currently can't
directly pass a string with the file contents, however you can pass a pointer to php://memory
or
php://temp
:
<?php
$fp = fopen('php://temp/maxmemory:1048576', 'r+');
fwrite($fp, generateFileUploadData());
$request->addUpload('stuff', $fp, 'custom.name', 'application/octet-stream');
?>
HTTP request body can also be set directly, by providing a string or a filename to HTTP_Request2::setBody() method. This is the only way to set a request body for
non-POST
request.
Setting "raw" request body
<?php
$request = new HTTP_Request2('http://rpc.example.com');
$request->setMethod(HTTP_Request2::METHOD_POST)
->setHeader('Content-type: text/xml; charset=utf-8')
->setBody(
"<?xml version=\"1.0\" encoding=\"utf-8\"?" . ">\r\n" .
"<methodCall>\r\n" .
" <methodName>foo.bar</methodName>\r\n" .
" <params>\r\n" .
" <param><value><string>Hello, world!</string></value></param>\r\n" .
" <param><value><int>42</int></value></param>\r\n" .
" </params>\r\n" .
"</methodCall>"
);
?>
Since release 0.5.0 HTTP_Request2 can automatically follow HTTP redirects
if follow_redirects
parameter is set to TRUE.
HTTP_Request2 will only follow redirects to HTTP(S) URLs, redirects to other protocols will result in an Exception.
HTTP_Request2::send will return only the final response, if you are interested in the intermediate ones you should use Observers.
Adapters in HTTP_Request2 package are classes responsible for establishing the actual connection to the remote server, writing requests and reading responses. Adapter can be set either via a configuration parameter (see: Configuration) or by HTTP_Request2::setAdapter() method.
The package currently contains three adapters:
This Adapter uses PHP's builtin stream_socket_client()
function and does not require any special extensions or configuration. Note that this is a
pure PHP implementation of HTTP protocol, it does not use http
stream
wrapper.
This is the default Adapter, it will be used by HTTP_Request2 unless you explicitly set the other one.
Curl adapter wraps around PHP's cURL extension and thus allows using extremely sophisticated cURL library. It is a good choice if you have cURL extension available in PHP.
This adapter allows testing packages and applications using HTTP_Request2 without depending on a network connection and remote server availability.
You can replace the default adapter in HTTP_Request2 by an instance of HTTP_Request2_Adapter_Mock populated via its HTTP_Request2_Adapter_Mock::addResponse() method. The adapter will return prepared responses or throw prepared exceptions when its sendRequest() method is called.
Other adapters may be created by subclassing an abstract HTTP_Request2_Adapter class and implementing its HTTP_Request2_Adapter::sendRequest() method.
Due to PHP bug
#47030 ssl_verify_host
configuration parameter is useless without
ssl_verify_peer
. When the latter is switched off no host validation
will be performed.
When doing a POST
request with file uploads or reading the request body from
file, HTTP_Request2 streams files from disk to reduce memory consumption
and to allow monitoring the request progress. This is done by setting up a
CURLOPT_READFUNCTION callback. PHP does not allow setting another callback,
CURLOPT_IOCTLFUNCTION (see PHP bug #47204) so the request body can not be
"rewound" when another request should be performed. Thus a request with a non-empty
body to a resource protected by Digest authentication or to a page that does a redirect when
follow_redirects
is enabled will fail.
Since release 0.5.0 HTTP_Request2 works around this problem by reading the whole request body into memory. Of course this may be an issue when the request body is huge, so consider using Socket Adapter.
Setting strict_redirects
configuration parameter to TRUE will only have
effect on Curl Adapter if CURLOPT_POSTREDIR (see PHP bug #49571) is available in cURL extension.
It is available in PHP 5.3.2 and higher.
HTTP_Request2_Adapter_Mock::addResponse() method can accept either a HTTP response (instance of HTTP_Request2_Response, a string, a pointer to an open file) or an Exception. The latter may be useful to test that your application degrades gracefully when the remote server is not available due to some network problems. Several responses may be added via addResponse() to simulate a HTTP transaction consisting of multiple requests and responses. They will be returned by the Adapter in the order they were added.
Since release 2.1.0 it is possible to specify a request URL to addResponse(). sendRequest() will only return responses without an explicit URL set or having the same URL as the request. It is recommended that you set explicit URLs either for all responses or for none of them, otherwise the behaviour will be difficult to predict.
Returning responses and throwing exceptions
<?php
require_once 'HTTP/Request2.php';
require_once 'HTTP/Request2/Adapter/Mock.php';
$mock = new HTTP_Request2_Adapter_Mock();
$mock->addResponse(
"HTTP/1.1 200 OK\r\n" .
"Connection: close\r\n" .
"\r\n" .
"Explicit response for example.org",
'http://example.org/'
);
$mock->addResponse(
"HTTP/1.1 200 OK\r\n" .
"Content-Length: 32\r\n" .
"Connection: close\r\n" .
"\r\n" .
"Nothing to see here, move along."
);
$mock->addResponse(
new HTTP_Request2_Exception("The server is on fire!")
);
// this request will succeed
$request1 = new HTTP_Request2(
'http://ok.example.com/', HTTP_Request2::METHOD_GET,
array('adapter' => $mock)
);
echo "Response: " . $request1->send()->getBody() . "\r\n";
// this request will fail
$request2 = new HTTP_Request2('http://burning.example.com/');
$request2->setAdapter($mock);
try {
echo "Response: " . $request2->send()->getBody() . "\r\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\r\n";
}
// The response for example.org was ignored by previous requests
$requestOrg = new HTTP_Request2(
'http://example.org/', HTTP_Request2::METHOD_GET,
array('adapter' => $mock)
);
echo "Response: " . $requestOrg->send()->getBody() . "\r\n";
?>
The above code will output:
Response: Nothing to see here, move along.
Error: The server is on fire!
Response: Explicit response for example.org
Mock Adapter also has two static helper methods HTTP_Request2_Adapter_Mock::createResponseFromString() and HTTP_Request2_Adapter_Mock::createResponseFromFile() which build HTTP_Request2_Response objects from either a string containing the complete HTTP response or a pointer to an open file with such response, respectively.
HTTP_Request2_Response encapsulates a HTTP response message and provides
easy access to different parts of it. It also contains static helper methods
HTTP_Request2_Response::decodeGzip() and
HTTP_Request2_Response::decodeDeflate()
for decoding response bodies encoded by Content-Encoding: gzip
(as defined in
RFC 1952) and
Content-Encoding: deflate
(RFC 1950), respectively.
An instance of this class will usually be returned by HTTP_Request2::send() method. You can also build an instance yourself using either helper methods of HTTP_Request2_Adapter_Mock or manually (see below).
You can use the following methods for accessing various parts of HTTP response:
getStatus(),
getReasonPhrase(),
getVersion(),
getHeader(),
getCookies(),
getBody().
Note that the response object will not contain the response body if
'store_body'
configuration parameter was set to FALSE. Also note that
getBody() always returns body completely decoded, if for some reason you want
to access body still encoded by gzip
/ deflate
you'll need
to use Observers and Socket adapter.
Accessing response parts
<?php
$request = new HTTP_Request2('http://www.example.com/');
$response = $request->send();
echo "Response status: " . $response->getStatus() . "\n";
echo "Human-readable reason phrase: " . $response->getReasonPhrase() . "\n";
echo "Response HTTP version: " . $response->getVersion() . "\n";
echo "Response headers:\n";
foreach ($response->getHeader() as $k => $v) {
echo "\t{$k}: {$v}\n";
}
echo "Value of a specific header (Content-Type): " . $response->getHeader('content-type') . "\n";
echo "Cookies set in response:\n";
foreach ($response->getCookies() as $c) {
echo "\tname: {$c['name']}, value: {$c['value']}" .
(empty($c['expires'])? '': ", expires: {$c['expires']}") .
(empty($c['domain'])? '': ", domain: {$c['domain']}") .
(empty($c['path'])? '': ", path: {$c['path']}") .
", secure: " . ($c['secure']? 'yes': 'no') . "\n";
}
echo "Response body:\n" . $response->getBody();
?>
The class is designed to be used in "streaming" scenario, building the response as it is being received:
Building the response (pseudocode)
<?php
$statusLine = read_status_line();
$response = new HTTP_Request2_Response($statusLine);
do {
$headerLine = read_header_line();
$response->parseHeaderLine($headerLine);
} while ($headerLine != '');
while ($chunk = read_body()) {
$response->appendBody($chunk);
}
?>
Everything is straightforward enough, but a few things need considering:
Observers are classes that can be attached to an instance of HTTP_Request2 and notified of request's progress. Possible uses:
HTTP_Request2 implements SplSubject
interface, so Observers should implement SplObserver. When
Observer is notified of an event, it should use HTTP_Request2::getLastEvent() to access event details. That method returns an
associative array with 'name'
and 'data'
keys. Possible
event names are
'connect'
'data'
is the destination (string).
'disconnect'
'sentHeaders'
'data'
is the headers sent (string).
'sentBodyPart'
'data'
is the length of that part
(integer).
'sentBody'
(since release 2.0.0beta1)'data'
is the
length of request body (integer).'receivedHeaders'
'data'
is
HTTP_Request2_Response object containing these headers.
'receivedBodyPart'
'data'
is the received part
(string).
'receivedEncodedBodyPart'
'receivedBodyPart'
, but 'data'
is still encoded by
relevant Content-Encoding
.
'receivedBody'
As the events are actually sent by request Adapters, you can receive fewer or different events if you switch to another Adapter. Curl Adapter does not notify of
'connect'
,'disconnect'
and'receivedEncodedBodyPart'
events (it always uses'receivedBodyPart'
as cURL extension takes care of decoding). Mock Adapter does not send any notifications at all.
The following example shows how an Observer can be used to save response body to disk without storing it in memory. Note that only events relevant to that task are handled in its update() method, the others can be safely ignored.
The package contains another Observer implementation: HTTP_Request2_Observer_Log class that allows logging request progress to a file or an instance of Log.
Saving response body to disk
<?php
class HTTP_Request2_Observer_Download implements SplObserver
{
protected $dir;
protected $fp;
public function __construct($dir)
{
if (!is_dir($dir)) {
throw new Exception("'{$dir}' is not a directory");
}
$this->dir = $dir;
}
public function update(SplSubject $subject)
{
$event = $subject->getLastEvent();
switch ($event['name']) {
case 'receivedHeaders':
if (($disposition = $event['data']->getHeader('content-disposition'))
&& 0 == strpos($disposition, 'attachment')
&& preg_match('/filename="([^"]+)"/', $disposition, $m)
) {
$filename = basename($m[1]);
} else {
$filename = basename($subject->getUrl()->getPath());
}
$target = $this->dir . DIRECTORY_SEPARATOR . $filename;
if (!($this->fp = @fopen($target, 'wb'))) {
throw new Exception("Cannot open target file '{$target}'");
}
break;
case 'receivedBodyPart':
case 'receivedEncodedBodyPart':
fwrite($this->fp, $event['data']);
break;
case 'receivedBody':
fclose($this->fp);
}
}
}
$request = new HTTP_Request2(
'http://pear.php.net/distributions/manual/pear_manual_en.tar.bz2',
HTTP_Request2::METHOD_GET, array('store_body' => false)
);
$request->attach(new HTTP_Request2_Observer_Download('.'));
// This won't output anything since body isn't stored in the response
echo $request->send()->getBody();
?>
All exceptions thrown in HTTP_Request2 are instances of HTTP_Request2_Exception. Since release 2.0.0beta1, HTTP_Request2 tries to throw a specialized subclass of that class and provide an error code when possible.
Checking for exception subclass and error code can help your application identify transient failures (e.g. HTTP_Request2_MessageException with error code HTTP_Request2_Exception::TIMEOUT). You can also use this information to display a user friendly error message instead of displaying Exception message that is more programmer friendly.
The following HTTP_Request2_Exception subclasses are available:
Subclasses of HTTP_Request2_Exception can contain two error codes:
The following package error codes are currently used:
Constant | Meaning |
---|---|
HTTP_Request2_Exception::INVALID_ARGUMENT | An invalid argument was passed to a method. |
HTTP_Request2_Exception::MISSING_VALUE | Some required value was not available. |
HTTP_Request2_Exception::MISCONFIGURATION | Request cannot be processed due to errors in PHP configuration (e.g. trying to use disabled PHP extension). |
HTTP_Request2_Exception::READ_ERROR | Error reading the local file. |
Constant | Meaning |
---|---|
HTTP_Request2_Exception::MALFORMED_RESPONSE | Server returned a response that does not conform to HTTP protocol. This means that even status line of response message could not be parsed. |
HTTP_Request2_Exception::DECODE_ERROR | Failure decoding Content-Encoding or Transfer-Encoding of response. |
HTTP_Request2_Exception::TIMEOUT | Operation timed out. |
HTTP_Request2_Exception::TOO_MANY_REDIRECTS | Number of redirects exceeded 'max_redirects' configuration parameter. |
HTTP_Request2_Exception::NON_HTTP_REDIRECT | Redirect to a protocol other than http(s):// . |
The package provides an Object-oriented interface to the session_* family functions. It provides extra features such as database storage for session data using the DB, MDB and MDB2 package. It introduces new methods like isNew(), useCookies(), setExpire(), setIdle(), isExpired(), isIdled() and others.
This package provides access to session-state values as well as session-level settings and lifetime management methods. Based on the standart PHP session handling mechanism it provides more advanced features such as database containers, idle and expire timeouts, etc.
Setting some options and detection of a new session
<?php
HTTP_Session::setCookieless(false);
HTTP_Session::start('MySessionID');
HTTP_Session::set('variable', 'Tet string');
if (HTTP_Session::isNew()) {
echo('new session was created with the current request');
$visitors++; // Increase visitors count
}
// after successful login use: HTTP_Session::regenerateId();
?>
Setting timeouts
<?php
HTTP_Session::start();
HTTP_Session::setExpire(time() + 60 * 60); // expires in one hour
HTTP_Session::setIdle(time() + 10 * 60); // idles in ten minutes
// expired
if (HTTP_Session::isExpired()) {
echo('Your session is expired!');
HTTP_Session::destroy();
}
// idled
if (HTTP_Session::isIdle()) {
echo('You've been idle for too long!');
HTTP_Session::destroy();
}
HTTP_Session::updateIdle();
?>
HTTP_Session lets you store the session data in a database making use of the DB, MDB and MDB2 packages.
Using MDB2 to store session data in a database
<?php
/* create the following table in your database
CREATE TABLE sessiondata (
id varchar(32) NOT NULL,
expiry int(10),
data text,
PRIMARY KEY (id)
);
*/
require_once 'HTTP/Session.php';
HTTP_Session::useTransSID(false);
HTTP_Session::useCookies(false);
// enter your DSN
HTTP_Session::setContainer('MDB2', array('dsn' => 'mysql://root:password@localhost/database',
'table' => 'sessiondata'));
/*
// using an existing MDB2 connection
HTTP_Session::setContainer('MDB2', array('dsn' => &$db,
'table' => 'sessiondata'));
*/
HTTP_Session::start('s');
HTTP_Session::setExpire(time() + 60); // set expire to 60 seconds
HTTP_Session::setIdle(time() + 5); // set idle to 5 seconds
if (HTTP_Session::isExpired()) {
//HTTP_Session::replicate('sessiondata_backup'); // Replicate data of current session to specified table
HTTP_Session::destroy();
}
if (HTTP_Session::isIdle()) {
//HTTP_Session::replicate('sessiondata_backup'); // Replicate data of current session to specified table
HTTP_Session::destroy();
}
HTTP_Session::updateIdle();
?>
The HTTP_Session2 package provides an Object-oriented interface to the session_* family functions. It's a PHP5 port of the HTTP_Session package.
HTTP_Session2 provides extra features such as database storage for session data using the DB and MDB2 package. It also introduces new methods, such as isNew(), useCookies(), setExpire(), setIdle(), isExpired(), isIdled() and others.
This package provides access to session-state values as well as session-level settings and lifetime management methods.
Based on the standart PHP session handling mechanism HTTP_Session2 provides more advanced features such as database containers, idle and expire timeouts and more.
Setting options and detecting a new session
<?php
HTTP_Session2::useCookies(false);
HTTP_Session2::start('MySessionID');
HTTP_Session2::set('variable', 'The string');
if (HTTP_Session2::isNew()) {
echo 'new session was created with the current request';
$visitors++; // Increase visitors count
}
// continue
?>
Setting timeouts
<?php
HTTP_Session2::start();
HTTP_Session2::setExpire(time() + 60 * 60); // expires in one hour
HTTP_Session2::setIdle(time() + 10 * 60); // idles in ten minutes
// the session expired
if (HTTP_Session2::isExpired()) {
echo 'Your session has expired!';
HTTP_Session2::destroy();
}
// the session is idle
if (HTTP_Session2::isIdle()) {
echo "You've been idle for too long!";
HTTP_Session2::destroy();
}
HTTP_Session2::updateIdle();
?>
For a more comprehensive example of persistent sessions, please check the cookie section.
HTTP_Session lets you store the session data in a database making use of the DB and MDB2 packages.
Using MDB2 to store session data in a database
<?php
/* create the following table in your database
CREATE TABLE sessiondata (
id varchar(32) NOT NULL,
expiry int(10),
data text,
PRIMARY KEY (id)
);
*/
require_once 'HTTP/Session2.php';
HTTP_Session2::useTransSID(false);
HTTP_Session2::useCookies(false);
// enter your DSN
HTTP_Session2::setContainer('MDB2',
array('dsn' => 'mysql://root:password@localhost/database',
'table' => 'sessiondata'));
/*
// using an existing MDB2 connection
HTTP_Session2::setContainer('MDB2',
array('dsn' => &$db, 'table' => 'sessiondata'));
*/
HTTP_Session2::start('s');
HTTP_Session2::setExpire(time() + 60); // set expire to 60 seconds
HTTP_Session2::setIdle(time() + 5); // set idle to 5 seconds
if (HTTP_Session2::isExpired()) {
HTTP_Session2::destroy();
}
if (HTTP_Session2::isIdle()) {
HTTP_Session2::destroy();
}
HTTP_Session2::updateIdle();
?>
When you create a session it needs to be referenced using a sessionID. This sessionID is generally saved either into a cookie or passed along in the URI.
To remember the sessionID across browser sessions, the cookie method is favoured.
To extend the lifetime of this cookie (the standard is that it's only valid until the browser is closed) we use PHP's session_set_cookie_params().
Use of session_set_cookie_params
<?php
require_once 'HTTP/Session2.php';
/**
* set cookie params
*/
session_set_cookie_params(60*60*24, // expire in 24 hours
'/', // path
'.example.org', // domain
false, // "secure only"
true); // only over http
HTTP_Session2::useCookies(true);
HTTP_Session2::start('s');
HTTP_Session2::setExpire(60*60*24); // set expire in 24 hours
HTTP_Session2::setIdle(60*60); // set idle to 1 hour
if (HTTP_Session2::isExpired()) {
HTTP_Session2::destroy();
}
if (HTTP_Session2::isIdle()) {
HTTP_Session2::destroy();
}
HTTP_Session2::updateIdle();
?>
Easy and secure managment of files submitted via HTML forms.
This package provides an advanced system for managing uploads of
files via HTML <input type="file" />
fields. Features include:
In the following examples it is assumed that you are using an
HTML form field <input type="file" name="f" />
in order to upload files. For example:
HTML form for simple file upload
The following form can be used in order to test the single file upload example.
<?php // sample code from below goes here ?> <html> <head> </head> <body> <form name="fileuploadexample" method="post" enctype="multipart/form-data" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']) ?>"> <input type="file" name="f" /> <input type="submit" name="submit" value="Submit" /> </form> </body> </html>
Simple file upload
The following code looks at the request and checks if a valid
file was uploaded through the form. If that is the case, the
file is moved to the subdirectory uploads
.
<?php
require_once "HTTP/Upload.php";
$upload = new HTTP_Upload("en");
$file = $upload->getFiles("f");
if ($file->isValid()) {
$moved = $file->moveTo('uploads/');
if (!PEAR::isError($moved)) {
echo 'File was moved to uploads/' . $file->getProp('name');
} else {
echo $moved->getMessage();
}
} elseif ($file->isMissing()) {
echo "No file was provided.";
} elseif ($file->isError()) {
echo $file->errorMsg();
}
?>
Internally, HTTP_Upload_File::moveTo() calls
$file->setName('safe');
which replaces special characters in the name with an underscore. You should always use$file->getProp('name')
after moving to retrieve the new filename.
HTML form for multiple file upload
The following form can be used in order to test the multiple file upload example.
<?php // sample code from below goes here ?> <html> <head> </head> <body> <form name="fileuploadexample2" method="post" enctype="multipart/form-data" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']) ?>"> <input type="file" name="f1" /> <input type="file" name="f2" /> <input type="submit" name="submit" value="Submit" /> </form> </body> </html>
Multiple files, more extensive checks
Multiple files can uploaded by replacing the name of the form
field (f
) with f[]
and
creating multiple <input />
fields
with this name.
<?php
$upload = new HTTP_Upload("en");
$files = $upload->getFiles();
foreach($files as $file){
if (PEAR::isError($file)) {
echo $file->getMessage();
}
if ($file->isValid()) {
$file->setName("uniq");
$dest_name = $file->moveTo("uploads/");
if (PEAR::isError($dest_name)) {
echo $dest_name->getMessage();
}
$real = $file->getProp("real");
} elseif ($file->isMissing()) {
echo "No file was provided.";
} elseif ($file->isError()) {
echo $file->errorMsg();
}
print_r($file->getProp());
}
?>
HTTP_Upload provides extensive information about uploaded files via the getProp() method:
mixed HTTP_Upload_File::getProp
(
[name]
)
If no value for name
is provided, then this
method will return an array containing all available information
about the uploaded file. Otherwise the information identified by
the value of this parameter will be returned as a string.
The list of possible values is determined by the contents of the
$_FILES
array, but is customized for the purposes
of HTTP_Upload. Here are the possible properties:
'name'
: destination file name
'tmp_name'
: temporary uploaded file name (assigned by PHP)
'form_name'
: name of the HTML form that submitted the uploaded file
'type'
: Mime type of the file
'size'
: size of the file
'error'
: if there was an error on upload, this contains a string
representing the kind of error. The errorCode() method can be used
to retrieve a localized error message from this property.
Extensive information via getProp()
<?php
require_once "HTTP/Upload.php";
$upload = new HTTP_Upload("en");
$file = $upload->getFiles("f");
if ($file->isValid()) {
echo "<pre>";
print_r($file->getProp());
echo "</pre>";
printf("The uploaded file has the extension %s.", $file->getProp("ext"));
}
?>
Another handy feature of HTTP_Upload is support for internationalized error messages. This means that if an error (like an invalid file upload) is detected, the programmer can choose in which the language the error messages should be returned by HTTP_Upload.
The first parameter of the constructor method for HTTP_Upload determines the language to be used. This is illustrated in the following example:
Example
<?php
// German error messages
$language = "de";
require_once "HTTP/Upload.php";
$upload = new HTTP_Upload($language);
$file = $upload->getFiles("f");
if ($file->isValid()) {
$moved = $file->moveTo("uploads/");
if (!PEAR::isError($moved)) {
echo "File was moved to uploads/";
} else {
// This will print a german error message
echo "An error was detected: " . $moved->getMessage() . "<br />";
}
}
?>
Provides Packages for working with images
Image_Barcode lets you create barcode representations of strings. The package supports multiple drivers, letting you create barcodes of the following type:
Code 39
Code 128
EAN 13
INT 25
PostNet
UPCA
Creating a barcode is as simple as calling the classes
static draw() method after including
Image/Barcode.php
.
The method has four parameters: The first is the string that shall be
converted into barcode representation. The second parameter determines
which driver/type should be used; it is one of:
Code39
, code128
,
ean13
, int25
,
postnet
or upca
.
The case difference between
Code39
andcode128
has no reason and makes no sense, but is kept because of backward compatibility.
The third parameter of draw() determines
the type of the image that is generated; one of
jpg
, png
and
gif
.
By default, the image generated by the barcode driver is directly
output to the browser. If you do not want this, pass
false
as fourth parameter and
draw() will return the GD image resource
object; allowing you to do further things with it.
<?php
require_once 'Image/Barcode.php';
Image_Barcode::draw('1234', 'int25', 'png');
?>
As with all PEAR packages, installing the package also installs
some more examples in the docs/Image_Barcode
directory of your PEAR installation.
This package provides a common interface to image drawing, making image source code independent on the library used. It includes output drivers for JPEG and PNG (using the GD library) as well as for PDF and SVG.
A list of the supported drawing functions is available on an external site.
Generation of a sample image in PNG format
<?php
require_once 'Image/Canvas.php';
// change the output format with the first parameter of factory()
$Canvas =& Image_Canvas::factory('png', array('width' => 400, 'height' => 300));
$Canvas->setLineColor('black');
$Canvas->rectangle(array('x0' => 0, 'y0' => 0, 'x1' => 399, 'y1' => 299));
$Canvas->setGradientFill(array('direction' => 'horizontal', 'start' => 'red', 'end' => 'blue'));
$Canvas->setLineColor('black');
$Canvas->ellipse(array('x' => 199, 'y' => 149, 'rx' => 50, 'ry' => 50));
$Canvas->setFont(array('name' => 'Arial', 'size' => 12));
$Canvas->addText(array('x' => 0, 'y' => 0, 'text' => 'Demonstration of what Image_Canvas do!'));
$Canvas->setFont(array('name' => 'Times New Roman', 'size' => 12));
$Canvas->addText(array('x' => 399, 'y' => 20, 'text' => 'This does not demonstrate what is does!', 'alignment' => array('horizontal' => 'right')));
$Canvas->setFont(array('name' => 'Courier New', 'size' => 7, 'angle' => 270));
$Canvas->addText(array('x' => 350, 'y' => 50, 'text' => 'True, but it\'s all independent of the format!', 'alignment' => array('horizontal' => 'right')));
$Canvas->setFont(array('name' => 'Garamond', 'size' => 10));
$Canvas->addText(array('x' => 199, 'y' => 295, 'text' => '[Changing format is done by changing 3 letters in the source]', 'alignment' => array('horizontal' => 'center', 'vertical' => 'bottom')));
$Canvas->addVertex(array('x' => 50, 'y' => 200));
$Canvas->addVertex(array('x' => 100, 'y' => 200));
$Canvas->addVertex(array('x' => 100, 'y' => 250));
$Canvas->setFillColor('red@0.2');
$Canvas->polygon(array('connect' => true));
$Canvas->image(array('x' => 398, 'y' => 298, 'filename' => './pear-icon.png', 'alignment' => array('horizontal' => 'right', 'vertical' => 'bottom')));
$Canvas->show();
?>
Due to the fact that the Image_Graph is currently in development phase, there is no documentation available in the manual. Amongst others a "Getting Started Guide" is available on an external site.
Extensive example code is available on an external site.
Article on PHPBuilder : More about PEAR's Image_Graph by Ian Gilfillan.
Image_GraphViz provides an object oriented wrapper to generate and output dot files.
Image_GraphViz is a class that provides methods to generate graphviz (dot) files. It also calls graphviz with all necessary parameters and outputs the generated image to the browser, or stores it into a file.
The package supports clusters, nodes, edges and all attributes to either of them. Apart from that, Image_GraphViz is able to store and load its own graph definition files.
While it abstracts the actual generation of dot files, one still needs to know the dot attributed graph language to take full advantage of the package - especially for style and layout adjustments.
The following code shows how to use Image_GraphViz the easiest way possible - generate a simple directed graph and send it as SVG image to the browser.
<?php
require_once 'Image/GraphViz.php';
$gv = new Image_GraphViz();
$gv->addEdge(array('wake up' => 'visit bathroom'));
$gv->addEdge(array('visit bathroom' => 'make coffee'));
$gv->image();
?>
The example above will display in your browser as follows:
The constructor does not need any parameters, but one may tell it if the graph is directed, an array of attributes and the name of the graph.
To generate content, serveral methods are available: addEdge(), addNode(), and addCluster().
Calling graphviz' dot or neato
is being done by Image_GraphViz implicitly
when calling
image()
or
fetch().
Both can be used to generate and display/return image data in
a large number of image formats, including
svg
, png
, pdf
and plain
text.
Image_Text provides advanced text manipulation facilities for GD2 image generation with PHP. Simply add text clippings to your images, let the class automatically determine lines, rotate text boxes around their center or top left corner. These are only a couple of features Image_Text provides.
Image_Text
void constructor Image_Text::Image_Text (
string $text
, array $options
= null
)
Set the text and options. This initializes a new Image_Text object. You must set your text here. Optinally you can set all options here using the $options parameter. If you finished switching all options you have to call the init() method first befor doing anything further! See Image_Text::set() for further information.
$text
Text to print.
$options
Options.
throws no exceptions thrown
This function can not be called statically.
int Image_Text::autoMeasurize (
int $start
= false
, int $end
= false
)
Automatically determines the greatest possible font size to fit the text into the text box. This method may be very resource intensive on your webserver. A good tweaking point are the $start and $end parameters, which specify the range of font sizes to search through. Anyway, the results should be cached if possible. You can optionally set $start and $end here as a parameter or the settings of the options array are used.
$start
Fontsize to start testing with.
$end
Fontsize to end testing with.
returns Fontsize measured or PEAR::Error.
throws no exceptions thrown
This function can not be called statically.
bool Image_Text::display (
bool $save
= false
, bool $free
= false
)
This will output the image to the users browser. You can use the standard IMAGETYPE_* constants to determine which image type will be generated. Optionally you can save your image to a destination you set in the options.
$save
Save or not the image on printout.
$free
Free the image on exit.
returns True on success, otherwise PEAR::Error.
throws no exceptions thrown
This function can not be called statically.
resource& Image_Text::getImg (
)
Get the image canvas.
returns Used image resource
throws no exceptions thrown
This function can not be called statically.
bool Image_Text::init (
)
This method has to be called after setting the options for your Image_Text object. It initializes the canvas, normalizes some data and checks important options. Be shure to check the initialization after you switched some options. The set() method may force you to reinitialize the object.
returns True on success, otherwise PEAR::Error.
throws no exceptions thrown
This function can not be called statically.
array Image_Text::measurize (
bool $force
= false
)
This method makes your text fit into the defined textbox by measurizing the lines for your given font-size. You can do this manually before rendering (or use even Image_Text::autoMeasurize()) or the renderer will do measurizing automatically.
$force
Optionally, default is FALSE, set TRUE to force measurizing.
returns Array of measured lines or PEAR::Error.
throws no exceptions thrown
This function can not be called statically.
bool
Image_Text::render
(
bool
$force
= false
)
This renders the measurized text or automatically measures it first. The $force parameter can be used to switch of measurizing problems (this may cause your text being rendered outside a given text box or destroy your image completely).
$force
Optional, initially FALSE, set TRUE to silence measurize errors.
returns True on success, otherwise PEAR::Error.
throws no exceptions thrown
This function can not be called statically.
bool Image_Text::save (
string $destFile
= false
)
Saves the image to a given destination. You can leave out the destination file path, if you have the option for that set correctly. Saving is possible with the save() method, too.
$destFile
The destination to save to (optional, uses options value else).
returns True on success, otherwise PEAR::Error.
throws no exceptions thrown
This function can not be called statically.
bool Image_Text::set (
mixed $option
, mixed $value
= null
)
Set a single or multiple options. It may happen that you have to reinitialize the Image_Text object after changing options.
Option | Description |
---|---|
x |
This sets the top left coordinates (using x/y) or the center point coordinates (using cx/cy) for your text box. The values from cx/cy will overwrite x/y. |
y |
|
cx |
|
cy |
|
canvas |
You can set different values as a canvas:
|
antialias |
This is usually |
width |
The width and height for your text box. |
height |
|
halign |
Alignment of your text inside the textbox. Use alignment constants to define vertical and horizontal alignment. |
valign |
|
angle |
The angle to rotate your text box. |
color |
An array of color values. Colors will be rotated in the mode you choose (linewise or paragraphwise). Can be in the following formats:
|
color_mode |
The color rotation mode for your color sets. Does only apply if you defined multiple colors. Use 'line' or 'paragraph'. |
background_color |
Defines the background color. Use NULL if you would like to
have the background transparent. Default is
|
enable_alpha |
If the alpha channel should be enabled. Automatically enabled when background_color is set to NULL. Default is FALSE. |
font_path |
Location of the font to use. |
font_file |
|
font_size |
The font size to render text in (will be overwriten if you use automeasurize). |
line_spacing |
Measure for the line spacing to use. Default is
|
min_font_size |
Automeasurize settings. Try to keep this area as small as possible to get better performance. |
max_font_size |
|
max_lines |
The maximum number of lines to render. Default is
|
image_type |
The type of image (use image type constants).
Is default set to |
dest_file |
The destination to (optionally) save your file. |
$option
A single option name or the options array.
$value
Option value if $option is string.
returns True on success, otherwise PEAR_Error.
throws no exceptions thrown
This function can not be called statically.
bool Image_Text::setColor (
mixed $color
, mixed $id = 0
)
This method is used to set a color at a specific color ID inside the color cycle.
The following colors syntaxes are understood by this method:
"#ffff00" hexadecimal format (HTML style), with and without #.
"#08ffff00" hexadecimal format (HTML style) with alpha channel (08), with and without #.
array with 'r','g','b' and (optionally) 'a' keys, using int values.
$color
Color value.
$id
ID (in the color array) to set color to.
returns True on success, otherwise PEAR::Error.
throws no exceptions thrown
This function can not be called statically.
bool Image_Text::setColors (
mixed $colors
)
Using this method you can set multiple colors for your text. Use a simple numeric array to determine their order and give it to this function. Multiple colors will be cycled by the options specified 'color_mode' option. The given array will overwrite the existing color settings!
The following colors syntaxes are understood by this method:
"#ffff00" hexadecimal format (HTML style), with and without #.
"#08ffff00" hexadecimal format (HTML style) with alpha channel (08), with and without #.
array with 'r','g','b' and (optionally) 'a' keys, using int values.
a GD color special color (tiled,...).
A single color or an array of colors are allowed here.
$colors
Single color or array of colors.
returns True on success, otherwise PEAR::Error.
throws no exceptions thrown
This function can not be called statically.
Name | Value | Line Number |
---|---|---|
IMAGE_TEXT_ALIGN_BOTTOM | "bottom",true | 56 |
IMAGE_TEXT_ALIGN_CENTER | "center",true | 43 |
IMAGE_TEXT_ALIGN_JUSTIFY | "justify",true | 61 |
IMAGE_TEXT_ALIGN_LEFT | "left",true | 35 |
IMAGE_TEXT_ALIGN_MIDDLE | "middle",true | 52 |
IMAGE_TEXT_ALIGN_RIGHT | "right",true | 39 |
IMAGE_TEXT_ALIGN_TOP | "top",true | 48 |
IMAGE_TEXT_REGEX_HTMLCOLOR | ,true"/^/^[#|]([a-f0-9]{2})?([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i" | 30 |
Image_Transform main use case is to create thumbnails of images.
The first step to do when using Image_Transform is to create an Image_Transform_Driver instance via the static factory() method. Just pass the driver name and you've got your object.
You may omit the driver name. In that case, Image_Transform checks for Imagick2, GD and Imlib and uses whatever driver is found first.
Now you can load() your image by passing the filename to this function. Use one of the scaling methods and then save().
You may not execute several scaling functions in a row without saving in between.
Scaling an image down
<?php
require_once 'Image/Transform.php';
//create transform driver object
$it = Image_Transform::factory('GD');
//load the original file
$it->load('beach-large.jpg');
//scale it to 150px
$it->scaleMaxLength(150);
//save it into a different file
$it->save('beach-150px.jpg');
?>
The example above did not do any error checking. Principially, any method may return a PEAR_Error object. Either you check each return value or - in PHP 5 - set the global PEAR error handler to throw exceptions as soon as an error occurs. This may interfere with other errors that are expected and can be hidden, so be careful with this option (especially when using other packages).
Scaling an image and checking for all possible errors
<?php
require_once 'Image/Transform.php';
//create transform driver object
$it = Image_Transform::factory('GD');
if (PEAR::isError($it)) {
die($it->getMessage());
}
//load the original file
$ret = $it->load('beach-large.jpg');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
//scale it to 150px
$ret = $it->scaleByLength(150);
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
//save it into a different file
$ret = $it->save('beach-150px.jpg');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
?>
The Image_Transform package is useless without a driver that encapsulates some graphic libraries' methods. As of April 2008, the package has the following drivers you can install and use:
Image_Transform_Driver_GD - uses methods of the GD 2 extension
Image_Transform_Driver_Imagick2 - for imagick PECL extension version < 2.0
Image_Transform_Driver_Imagick3 - for imagick PECL extension version >= 2.0
Image_Transform_Driver_Imlib - uses PHP imlib extension
Image_Transform_Driver_IM - Imagick binary executables (if you don't have the extension)
Image_Transform_Driver_NetPBM
- uses the binary executable
pnmscale
of the NetPBM software package.
Image_Transform brings you a lot of methods to scale images. Most of them call the basic resizing method with different parameters, but still have their right to exist because they make your life convenient. Here is a short list:
resize
- generic resizing method. Pass any size, percentage string
(with %
) or a scaling factor for both
x and y values. Keep it 0 to keep that size. Does not necessarily
keep the aspect ratio.
scaleByX - scale the image by resizing the width of the image to the given pixel size. Preserves aspect ratio.
scaleByY - scale the image by resizing the height of the image to the given pixel size. Preserves aspect ratio.
scale
- generig scaling function. Pass a pixel value, percentage (with
%
) or scaling factor (<1). Keeps aspect ratio.
scaleByPercentage - scales by the given percentage.
scaleByFactor - same as scaleByPercentage(), just that the numbers are factor 100 smaller.
scaleByLength - scale so that the longest size has the desired size.
fit - scale the image so that it fits into the given box (width and height). Nothing is done if the image is already smaller or equal than the box size.
fitX - scale the image so that the width is the given size. If the width is already smaller, nothing is done.
fitY - scale the image so that the height is the given size. If the height is already smaller, nothing is done.
The save() method requires at least one parameter, the filename as which the scaled image is to be saved as. With only one parameter, the type of the new image is the same as the original type.
File extensions are not appended if left out.
The second parameter can be the extension of the file type you want to
save the image as, for example
png
or jpg
.
In case of an error - for example if the driver does not support to write the file type - a PEAR_Error object is returned.
Instead of saving, you can directly put out the image to the browser using display().
Method | GD | Imagick2 | Imagick3 | Imlib | IM | NetPBM |
---|---|---|---|---|---|---|
_resize() | yes | yes | yes | yes | yes | yes |
save() | yes | yes | yes | yes | yes | yes |
display() | yes | yes | yes | yes | yes | yes |
free() | yes | yes | yes | yes | yes | yes |
addText() | yes | yes | yes | yes | yes | yes |
addDropShadow() | - | - | - | - | - | - |
addBorder() | yes | - | - | - | - | - |
crop() | yes | yes | yes | yes | yes | yes |
canvasResize() | - | - | - | - | - | - |
fitOnCanvas() | - | - | - | - | - | - |
flip() | yes | yes | yes | yes | yes | yes |
gamma() | yes | yes | yes | - | yes | yes |
greyscale() | yes | - | yes | - | yes | yes |
mirror() | yes | yes | yes | yes | yes | yes |
normalize() | - | - | - | - | - | - |
rotate() | yes | yes | yes | yes | yes | yes |
Provides packages for internationalization and localization.
The static I18Nv2 class currently provides routines for unified (ment as OS independent) locale setting, retrieving locale specific information like the "thousands separator" and automatic characterset conversion for output.
This documentation is outdated and needs an overhaul.
The work on I18Nv2 has actually started as a refactoring of I18N, but after some time I figured out that too many BC breaking changes were applied, so the release of a new major version was suggested.
I18N was modeled after Java OO practice and it provides a bunch of classes for each formatting action (numbers, currencies, dates).
I18Nv2 takes another approach and provides all formatting functionality within one class, which is I18Nv2_Locale.
I18Nv2_Locale is based upon PHPs builtin functionality of setlocale() , localeconv () and iconv related functions, while I18N completely depends on user contributed formatting rules.
I wouldn't say I18Nv2's approach is the better one, because it depends on the internationalization capabilities of the underlying operating system, but it's simpler and faster - though it is in need of user contributed date and time formatting rules.
I18N's translation functionality was dropped in favour of the new and shiny Translation2.
I18Nv2 still provides an HTTP negotiator to reveal the users preferred language/locale and charset.
I18Nv2 has translated lists of ISO country and language names for about 50 different languages. See I18Nv2_Country and I18Nv2_Language.
Please see PEARs API Documentation for details on the API provided by I18Nv2.
Because Un*x and Windows use different locale codes, PHPs setLocale() is not easily portable - I18Nv2::setLocale() attempts to provide this portability.
With I18Nv2 you can use standard locale codes like 'en_US' on both, Linux and Windows, though the list is far not complete yet, so if you stumble over a not covered locale (I18Nv2::$locales in I18Nv2::_main()), just drop a mail to the maintainer with the missing locale and its corresponding Win32 code.
I18Nv2::setLocale()
<?php
require_once 'I18Nv2.php';
foreach (array('en_US', 'de', 'fr_CN') as $locale) {
if (!$syslocale = I18Nv2::setLocale($locale)) {
echo "Locale '$locale' not available!\n";
} else {
echo "Systems locale for '$locale': '$syslocale'\n";
}
}
?>
I18Nv2 holds locale conventions returned by localeConv() stored statically, so they are easily accessible through I18Nv2::getInfo(). Have a look at the documentation of PHPs localeConv() for all available information.
I18Nv2::getInfo()
<?php
require_once 'I18Nv2.php';
I18Nv2::setLocale('fr');
$dec_point = I18Nv2::getInfo('decimal_point');
echo "The decimal point for the french locale is '$dec_point'.\n";
echo "I18Nv2::getInfo() called without parameter returns all available information:\n";
print_r(I18Nv2::getInfo());
?>
I18Nv2 provides an easy way to utilize the ob_iconv_handler() through I18Nv2::autoConv().
I18Nv2::autoConv()
<?php
require_once 'I18Nv2.php';
// Writing a shell app that should also display nicely in a DOS box
if (I18Nv2_WIN) {
I18Nv2::autoConv('CP850');
}
// output some latin1 stuff
echo "äüöß\n";
?>
I18Nv2_Locale is a formatter object that provides functionality to format dates, times, numbers and currencies in locale dependent conventions.
I18Nv2_Locale
<?php
require_once 'I18Nv2.php';
$locale = &I18Nv2::createLocale('de_AT');
echo "Format a currency value of 2000: ",
$locale->formatCurrency(2000, I18Nv2_CURRENCY_INTERNATIONAL), "\n";
echo "Format todays date: ",
$locale->formatDate(null, I18Nv2_DATETIME_FULL), "\n";
echo "Format current time: ",
$locale->formatTime(null, I18Nv2_DATETIME_SHORT), "\n";
?>
I18Nv2 provides a language, charset and locale negotiator for HTTP.
I18Nv2_Negotiator
<?php
require_once 'I18Nv2/Negotiator.php';
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en-US,en-GB,en;q=0.5,de';
$_SERVER['HTTP_ACCEPT_CHARSET'] = 'utf-8,iso-8859-1;q=0.5';
$neg = &new I18Nv2_Negotiator;
echo "User agents preferred language: ",
$lang = $neg->getLanguageMatch(), "\n";
echo "User agents preferred country for language '$lang': ",
$neg->getCountryMatch($lang), "\n";
echo "User agents preferred locale: ",
$neg->getLocaleMatch(), "\n";
echo "User agents preferred charset: ",
$neg->getCharsetMatch(), "\n";
?>
I18Nv2 provides translated lists of ISO language names.
I18Nv2_Language
<?php
require_once 'I18Nv2/Language.php';
$lang = &new I18Nv2_Language('it', 'iso-8859-1');
echo "Italian name for English: ",
$lang->getName('en'), "\n";
echo "Italian name for French: ",
$lang->getName('fr'), "\n";
?>
I18Nv2 provides translated lists of ISO country names.
I18Nv2_Country
<?php
require_once 'I18Nv2/Country.php';
$country = &new I18Nv2_Country('de', 'iso-8859-1');
echo "German name for United States: ",
$country->getName('us'), "\n";
echo "German name for Italia: ",
$country->getName('it'), "\n";
?>
I18Nv2 provides decorated classes for country and language lists.
I18Nv2_DecoratedList
<?php
require_once 'I18Nv2/Country.php';
require_once 'I18Nv2/DecoratedList/HtmlSelect.php';
require_once 'I18Nv2/DecoratedList/HtmlEntities.php';
$c = &new I18Nv2_Country('it', 'iso-8859-1');
$e = &new I18Nv2_DecoratedList_HtmlEntities($c);
$s = &new I18Nv2_DecoratedList_HtmlSelect($e);
// set some attributes
$s->attributes['select']['name'] = 'CountrySelect';
$s->attributes['select']['onchange'] = 'this.form.submit()';
// set a selected entry
$s->selected['DE'] = true;
// print a HTML safe select box
echo $s->getAllCodes();
?>
I18Nv2_CommonList::toDecoratedList()
<?php
require_once 'I18Nv2/Country.php';
$c = &new I18Nv2_Country('it', 'iso-8859-1');
$s = &$c->toDecoratedList('HtmlSelect');
// set some attributes
$s->attributes['select']['name'] = 'CountrySelect';
$s->attributes['select']['onchange'] = 'this.form.submit()';
// set a selected entry
$s->selected['IT'] = true;
// print a HTML select box
echo $s->getAllCodes();
?>
Name | Value |
---|---|
I18Nv2_CURRENCY | 20 |
I18Nv2_CURRENCY_INTERNATIONAL | 22 |
I18Nv2_CURRENCY_LOCAL | 21 |
I18Nv2_DATETIME | 30 |
I18Nv2_DATETIME_DEFAULT | 32 |
I18Nv2_DATETIME_FULL | 35 |
I18Nv2_DATETIME_LONG | 34 |
I18Nv2_DATETIME_MEDIUM | 33 |
I18Nv2_DATETIME_SHORT | 31 |
I18Nv2_NUMBER | 10 |
I18Nv2_NUMBER_FLOAT | 11 |
I18Nv2_NUMBER_INTEGER | 12 |
mixed I18Nv2::setLocale (
string $locale
, int $cat = LC_ALL
)
Set environment to the specified locale.
Set a locale:
<?php
1 require_once 'I18Nv2.php';
2 I18Nv2::setLocale('en_GB');
?>
$locale
a valid locale like en_US or de_DE
$cat
the locale category - usually LC_ALL
Returns string used system locale or false on failure.
This function should be called statically.
See also I18Nv2::setLocale() example, I18Nv2::getInfo(), I18Nv2::lastLocale(), PHPs setlocale().
string I18Nv2::lastLocale (
int $prior = 0
, bool $full
= false
)
Retrieve kinda history of locales that have been already set.
This only works, if I18Nv2::setLocale() has already been called.
$prior
if 0, the current otherwise n prior to the current locale
$full
whether to return the array with locale, language and actually used system locale
Returns mixed prior locale.
This function should be called statically.
See also I18Nv2::setLocale().
mixed I18Nv2::getInfo (
string $part
= null
)
Get several locale specific information like thousands separator. The provided information debends on the local libc implementation and thus is not always reliable - especially on Microsoft Windows.
Retrieving specific locale information:
<?php
1 require_once 'I18Nv2.php';
2 $locale = I18Nv2::setLocale('en_US');
3 $thsep = I18Nv2::getInfo('thousands_separator');
4 $point = I18Nv2::getInfo('decimal_point');
?>
$part
specific part of locale information
Returns mixed locale specific information or array all available locale specific information if called without parameter.
This function should be called statically.
See also I18Nv2::setLocale(), PHPs localeconv().
mixed I18Nv2::autoConv (
string $ocs = 'UTF-8'
, string $ics = 'ISO-8859-1'
)
This method utilizes ob_iconv_handler(), so you should call it at the beginning of your script (prior to any output).
<?php
1 require_once 'I18Nv2.php';
2 I18Nv2::autoConv('iso-8859-1', 'utf-8');
3 // ...
?>
$ocs
desired output character set
$ics
current intput character set
Returns TRUE on success, PEAR_Error on failure.
Returns PEAR_Error if output buffering couldn't be started.
This function should be called statically.
See also PHPs ob_iconv_handler().
object
I18Nv2_Locale &I18Nv2::createLocale
(
string $locale
= null
)
Create a new I18Nv2_Locale object with the specified locale.
$locale
a locale like en_US or de_DE
Returns new object I18Nv2_Locale.
This function should be called statically.
See also I18Nv2_Locale.
object
I18Nv2_Negotiator
&I18Nv2::createNegotiator
(
string $defLang = 'en'
, string $defCharset = 'iso-8859-1'
)
Create a new I18Nv2_Negotiator object with the specified default language and default character set.
$defLang
default language
$defCharset
default character set
Returns new object I18Nv2_Negotiator.
This function should be called statically.
See also I18Nv2_Negotiator.
array I18Nv2::langs2locales (
array $languages
)
Transforms language codes like en-US and de-DE to locale codes like en_US and de_DE.
$languages
array of language codes
Returns array transformed language codes as locale codes.
This function should be called statically.
See also I18Nv2::locales2langs().
array I18Nv2::locales2langs (
array $locales
)
Transforms locale codes like en_US and de_DE to language codes like en-US and de-DE.
$locales
array of locale codes
Returns array transformed loclae codes as language codes.
This function should be called statically.
See also I18Nv2::langs2locales().
Represents a specific locale and provides routines for formatting date and time, numbers and currency.
object I18Nv2_Locale::I18Nv2_Locale (
string $locale
= null
)
Instantiate a new I18Nv2_Locale object with the specified locale.
$locale
the desired locale like en_US or de_DE
Returns new object I18Nv2_Locale.
void I18Nv2_Locale::initialize (
)
Initializes the I18Nv2_Locale object.
This method gets aumatically called by the constructor.
This function can not be called statically.
void I18Nv2_Locale::loadExtension (
)
Load available locale extensions.
This method gets called autmatically by the constructor.
This function can not be called statically.
mixed I18Nv2_Locale::setLocale (
string $locale
)
Transforms the I18Nv2_Locale object to the specified locale.
$locale
the locale like en_US or de_DE
Returns TRUE on success, PEAR_Error on failure.
Returns a PEAR_Error if the supplied locale was invalid.
This function can not be called statically.
void I18Nv2_Locale::setDefaults (
)
Reset used formats to the default values.
This function can not be called statically.
void I18Nv2_Locale::setCustomFormat (
mixed $type
= null
, mixed $format
= null
)
Set a custom format.
If $format
is omitted, the custom format for
$type
will be discarded - if both vars are
omitted all custom formats will be discarded.
$type
the I18Nv2 format category for which to set the custom format
$format
the custom format
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
mixed I18Nv2_Locale::setCurrencyFormat (
int $format
, bool $custom
= false
)
Set the currency format to use.
Either I18Nv2_CURRENCY_LOCAL, I18Nv2_CURRENCY_INTERNATIONAL or a custom currency format.
$format
a I18Nv2_CURRENCY constant
$custom
whether to use a defined custom format
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
mixed I18Nv2_Locale::setDateFormat (
int $format
, bool $custom
= false
)
Set the date format to use.
Either a I18Nv2_DATETIME constant or a custom date format.
$format
a I18Nv2_DATETIME constant
$custom
whether to use a defined custom format
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
mixed I18Nv2_Locale::setNumberFormat (
int $format
, bool $custom
= false
)
Set the number format to use.
Either a I18Nv2_NUMBER constant or a custom number format.
$format
a I18Nv2_NUMBER constant
$custom
whether to use a defined custom format
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
mixed I18Nv2_Locale::setTimeFormat (
int $format
, bool $custom
= false
)
Set a time format to use.
Either a I18Nv2_DATETIME constant or a custom time format.
$format
a I18Nv2_DATETIME constant
$custom
whether to use a defined custom format
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
string I18Nv2_Locale::formatDate (
int $timestamp
= null
)
Format a date corresponding to the current locale.
$timestamp
timestamp which should be formatted
Returns string formatted date.
This function can not be called statically.
string I18Nv2_Locale::formatTime (
int $timestamp
= null
)
Format a time corresponding to the current locale.
$timestamp
timestamp which should be formatted
Returns string formatted time.
This function can not be called statically.
string I18Nv2_Locale::formatNumber (
numeric $value
)
Format a number corresponding to the current locale.
$value
numeric value which should be formatted
Returns string formatted number.
This function can not be called statically.
string I18Nv2_Locale::formatCurrency (
numeric $value
)
Format a currecy value corresponding to the current locale.
$value
numeric value which should be formatted
Returns string formatted currency value.
This function can not be called statically.
string I18Nv2_Locale::date (
int $timestamp
= null
)
Get the local date as returned by strftime('%x').
$timestamp
timestamp which should be formatted
Returns string local date.
This function can not be called statically.
string I18Nv2_Locale::time (
int $timestamp
= null
)
Get the local textual representation of a time as returned by strftime('%X').
$timestamp
timestamp which should be formatted
Returns string formatted time.
This function can not be called statically.
mixed I18Nv2_Locale::dayName (
int $weekday
, bool $short
= false
)
Get the local textual representation of a weekday.
$weekday
numerical representation of weekday (0 = Sunday, 1 = Monday, ...)
$short
whether to return the abbreviation
Returns string name of weekday on success or PEAR_Error on failure.
This function can not be called statically.
mixed I18Nv2_Locale::monthName (
int $month
, bool $short
= false
)
Get the local textual representation of a month.
$month
numerical representation of month (0 = January, 1 = February, ...)
$short
whether to return the abbreviation
Returns string name of month on success or PEAR_Error on failure.
This function can not be called statically.
Provides basic negotiation of user preferred locale, language and characterset.
object
I18Nv2_Negotiator
I18Nv2_Negotiator::I18Nv2_Negotiator (
string $defaultLanguage = 'en'
, string $defaultCharset = 'iso-8859-1'
, string $defaultCountry = ''
)
Find language code, country code, charset code, and dialect or variant of locale setting in HTTP request headers.
$defaultLanguage
default language
$defaultCharset
default character set
$defaultCountry
default country
Returns object I18Nv2_Negotiator.
string I18Nv2_Negotiator::getCharsetMatch (
array $charsets
= null
)
Get a matching character set.
$charsets
array of character sets
Returns string matched character set.
This function can not be called statically.
string I18Nv2_Negotiator::getCountryMatch (
string $lang
, array $countries
= null
)
Get a matching country code.
$lang
language code
$countries
array of country codes
Returns string matching country.
This function can not be called statically.
string I18Nv2_Negotiator::getLanguageMatch (
array $langs
= null
)
Get a matching language code.
$langs
array of language codes
Returns string matching language code.
This function can not be called statically.
string I18Nv2_Negotiator::getLocaleMatch (
array $langs
= null
, array $countries
= null
)
Get a matching locale code.
This method is a combination of I18Nv2::getLanguageMatch() and I18Nv2::getCountryMatch()
$langs
array of language codes
$countries
array of country codes
Returns string matching locale code.
This function can not be called statically.
string I18Nv2_Negotiator::getVariantInfo (
string $lang
)
This package is not documented yet.
$lang
throws no exceptions thrown
This function can not be called statically.
object &I18Nv2_Negotiator::singleI18NCountry (
)
Singleton for I18Nv2_Country.
Returns object I18Nv2_Country.
This function can not be called statically.
object &I18Nv2_Negotiator::singleI18NLanguage (
)
Singleton for I18Nv2_Language.
Returns object I18Nv2_Language.
This function can not be called statically.
List of two letter country code to country name mapping.
object
I18Nv2_Country
I18Nv2_Country::I18Nv2_Country
(
)
Instantiate a new I18Nv2_Country object.
Returns object I18Nv2_Country.
array I18Nv2_Country::getAllCodes (
)
Get all country codes as associative array indexed by their two letter codes.
Returns array all country codes.
This function can not be called statically.
string I18Nv2_Country::getName (
string $code
)
Get the corresponding country name of the supplied two letter country code.
$code
two letter country code
Returns string country name.
This function can not be called statically.
bool I18Nv2_Country::isValidCode (
string $code
)
Check if the supplied two letter country code is valid.
$code
two letter country code
Returns boolean whether the two letter country code is valid.
This function can not be called statically.
List of ISO-639-1 two letter resp. ISO-639-2 three letter language code to language name mapping.
object
object I18Nv2_Language
I18Nv2_Language::I18Nv2_Language
(
bool $threeLetters
= false
)
Instantiate a new I18Nv2_Language object.
$threeLetters
whether to use ISO-639-2 three letters or ISO-639-1 two letters language code format.
Returns new object I18Nv2_Language object.
array I18Nv2_Language::getAllCodes (
)
Get all language codes as associative array indexed by their two/three letter codes.
Returns array all language codes.
This function can not be called statically.
string I18Nv2_Language::getName (
string $code
)
Get the language name for the two/three letter language code.
$code
two/three letter language code
Returns string name of the language.
This function can not be called statically.
boolean I18Nv2_Language::isValidCode (
string $code
)
Check if the supplied two/three letter language code is valid.
$code
two/three letter language code
Returns boolean whether the two/three letter language code is valid.
This function can not be called statically.
Class for multilingual applications management
Translation2
Class | Summary |
---|---|
Translation2_Admin | Administration utilities for translation string management |
Translation2 is a class for multilingual applications management. It provides an easy way to retrieve all the strings for a multilingual site from a data source (i.e. db). The API is designed to be clean, simple to use, yet powerful and extensible. A Translation2_Admin class is provided to easily manage translations (add/remove a language, add/remove a string).
The following containers (data source drivers) are provided:
PEAR::DB
PEAR::MDB
PEAR::MDB2
gettext
PEAR::DB_DataObject (experimental, used by PEAR:: HTML_Template_Flexy)
XML
Some decorator classes will help in various tasks. They can be layered/stacked one on top of the other, in any number. This approach should suit everyone's needs. Currently, the following decorators are provided:
CacheLiteFunction (for fast file-based caching)
CacheMemory (for memory-based caching)
DefaultText (to replace empty strings with their keys or a default text)
ErrorText (to replace empty strings with an "error_text" fallback message)
Iconv (to switch from/to different encodings)
Lang (resort to fallback languages for empty strings)
SpecialChars (replace html entities with their hex codes)
UTF-8 (convert UTF-8 strings to ISO-8859-1)
Translation2 can use different storage containers. Have a look in the /docs/examples dir of the package for some example setups (gettext ini files, SQL DDLs, PHP configuration files, etc).
This simple example will show how you can instanciate a Translation2 object and use it to retrieve your translated strings from a db, using the MDB2 driver:
<?php
// set the parameters to connect to your db
$dbinfo = array(
'hostspec' => 'host',
'database' => 'dbname',
'phptype' => 'mysql',
'username' => 'user',
'password' => 'pwd'
);
define('TABLE_PREFIX', 'mytable_');
// tell Translation2 about your db-tables structure,
// if it's different from the default one.
// NB: the default db structure is:
//
// Table "langs" (available languages and meta info)
// +----+------+------+----------+------------+
// | ID | name | meta | encoding | error_text |
// +----+------+------+----------+------------+
//
// Table "strings" (translations)
// +----+---------+----+----+----+-----+
// | ID | page_id | en | de | it | ... |
// +----+---------+----+----+----+-----+
// You can have one table per translation, instead
// of one table for all the languages
//
$params = array(
'langs_avail_table' => TABLE_PREFIX.'langs_avail',
'lang_id_col' => 'ID',
'lang_name_col' => 'name',
'lang_meta_col' => 'meta',
'lang_errmsg_col' => 'error_text',
'strings_tables' => array(
'en' => TABLE_PREFIX.'i18n',
'it' => TABLE_PREFIX.'i18n',
'de' => TABLE_PREFIX.'i18n'
),
'string_id_col' => 'ID',
'string_page_id_col' => 'pageID',
'string_text_col' => '%s' //'%s' will be replaced by the lang code
);
$driver = 'MDB2';
require_once 'Translation2.php';
$tr =& Translation2::factory($driver, $dbinfo, $params);
//always check for errors. In this examples, error checking is omitted
//to make the example concise.
if (PEAR::isError($tr)) {
//deal with it
}
// you can set the charset that the database must use, for instance 'utf8'
$tr->setCharset('iso-8859-1');
// set primary language
$tr->setLang('it');
// set the group of strings you want to fetch from
$tr->setPageID('defaultGroup');
// add a Lang decorator to provide a fallback language
$tr =& $tr->getDecorator('Lang');
$tr->setOption('fallbackLang', 'en');
// add another Lang decorator to provide another fallback language,
// in case some strings are not translated in Italian or English
$tr =& $tr->getDecorator('Lang');
$tr->setOption('fallbackLang', 'de');
// fetch the string with the 'test' stringID
echo $tr->get('test');
// fetch a string not translated into Italian (test fallback language)
echo $tr->get('only_english');
// fetch the whole group of strings, without resorting to the fallback lang
// and without any "decoration"
$rawPage = $tr->getRawPage();
print_r($rawPage);
// fetch the whole group of strings, but applying the decorators
$page = $tr->getPage();
print_r($page);
// you can force the lang and the group of the string you're requesting
echo $tr->get('month_01', 'calendar', 'it');
// the same is true for getRawPage() and getPage()
$page = $tr->getPage('calendar', 'de');
print_r($page);
?>
As you can see, the main methods are get(), getPage() and getRawPage(). The full syntax is
<?php
get($stringID, $pageID, $langID);
?>
but if you set the pageID and the langID beforehand, you won't need to specify them at each get() invocation.
NB: you have to check for errors at least on the first invocation of one of these methods, since the db connection is only estabilished at this point, so the chances of failures are higher here.
Now let's see how we can extract some meta info from the db:
<?php
$tr->getLang(); // no langID => get current lang
$tr->getLang('it'); // same as above, if the current lang is Italian
// the first parameter is the lang code,
// with the second parameter you can filter the info you need
$tr->getLang('it', 'error_text');
$tr->getLang('en', 'name');
$tr->getLang('de', 'meta');
$tr->getLang('de', 'encoding');
?>
Translation2 uses decorators to filter/change the retrieved strings. You can have a chain of decorators (filters), and you can also add yours.
<?php
$tr =& Translation2::factory($driver, $dbinfo, $params);
$tr->setLang('en');
$tr->setPageID('calendar');
// add a memory-based cache decorator, to do some basic prefetching and
// reduce the load on the db
$tr = & $tr->getDecorator('CacheMemory');
// add a file-based cache decorator, to cache the query results through pages
$tr =& $tr->getDecorator('CacheLiteFunction');
$tr->setOption('cacheDir', 'cache/');
$tr->setOption('lifeTime', 3600*24);
// add a fallback lang decorator
$tr = & $tr->getDecorator('Lang');
$tr->setOption('fallbackLang', 'it');
// add a special chars decorator to replace special characters with the html entity
$tr = & $tr->getDecorator('SpecialChars');
// control the charset to use
$tr->setOption('charset', 'ISO-8859-2');
// add a UTF-8 decorator to automatically decode UTF-8 strings
$tr = & $tr->getDecorator('UTF8');
// add a default text decorator to deal with empty strings
$tr = & $tr->getDecorator('DefaultText');
// replace the empty string with its stringID
echo $tr->get('emptyString');
// use a custom fallback text
echo $tr->get('emptyString', 'stringGroup', 'en', 'show this default text');
?>
The getStringID() method is the reverse of get(). If you want to translate a string to another language, but you don't know the associated stringID, you can retrieve it with this method:
<?php
$tr->setLang('it');
// translate the Italian string "gennaio" into the English "january"
$stringID = $tr->getStringID('gennaio', 'calendar');
echo $translatedString = $tr->get($stringID, 'calendar', 'en');
?>
Translation2 can handle parametric strings, and replace them with parameters passed at runtime (they can be numeric or associative arrays).
<?php
// "hello_user" = "hello &&user&&, today is &&weekday&&, &&day&&th &&month&& &&year&&"
$tr->setParams(array(
0 => '',
'user' => 'Joe',
'day' => '15',
'month' => $tr->get('month_01', 'calendar', 'en'),
'year' => '2004',
'weekday' => $tr->get('day_5', 'calendar', 'en')
));
echo $tr->get('hello_user');
// the above line will print "hello Joe, today is Friday, 15th January 2004"
?>
If your site structure is organized in sections, like a header, a body and a footer, you may use those units as "pages" (or groups of translations), and then fetch them one by one:
<?php
$header_trans = $tr->getPage('header');
$body_trans = $tr->getPage('body');
$footer_trans = $tr->getPage('footer');
?>
If you want them all in a single result, just merge them into a single array:
<?php
$translations = array_merge(
$tr->getPage('header'),
$tr->getPage('body'),
$tr->getPage('footer')
);
?>
Translation2_Admin is a class meant to help with translation management (add/remove a language, add/remove a string).
Translation2 can use different storage containers. Have a look in the /docs/examples dir of the package for some example setups (gettext ini files, SQL DDLs, PHP configuration files, etc).
This simple example will show how you can add a new language [addLang()], using the MDB2 driver:
<?php
// set the parameters to connect to your db
$dbinfo = array(
'hostspec' => 'host',
'database' => 'dbname',
'phptype' => 'mysql',
'username' => 'user',
'password' => 'pwd'
);
// tell Translation2 about your db-tables structure,
// if it's different from the default one.
$params = array(
'langs_avail_table' => 'langs_avail',
'lang_id_col' => 'id',
'lang_name_col' => 'name',
'lang_meta_col' => 'meta',
'lang_errmsg_col' => 'error_text',
'lang_encoding_col' => 'encoding',
'strings_tables' => array(
'it' => 'i18n',
'de' => 'i18n'
),
//OR, if you use only one table,
//'strings_default_table' => 'i18n',
'string_id_col' => 'id',
'string_page_id_col' => 'page_id',
'string_page_id_col_length' => 50, // db field size
'string_text_col' => '%s' //'%s' will be replaced by the lang code
);
$driver = 'MDB2';
require_once 'Translation2/Admin.php';
$tr =& Translation2_Admin::factory($driver, $dbinfo, $params);
// set some info about the new lang
$newLang = array(
'lang_id' => 'en',
'table_name' => 'i18n',
'name' => 'english',
'meta' => 'some meta info',
'error_text' => 'not available',
'encoding' => 'iso-8859-1',
);
$tr->addLang($newLang);
?>
That's it. If you specified a new table, it will be created, if you used the same table name as the one of your other translations, it will be ALTERed to host the new language too.
This simple example will show how you can update an existing language [updateLang()]:
<?php
// set some info about the new lang
$langData = array(
'lang_id' => 'en',
'table_name' => 'i18n',
'name' => 'English',
'meta' => 'some updated meta info',
'error_text' => 'this text is not available in English',
'encoding' => 'iso-8859-15',
);
$tr->updateLang($langData);
?>
If you want to remove all the translated strings and the info for a certain language, all you have to do is
<?php
$tr->removeLang('fr');
?>
removeLang()
can accept a 2nd parameter ($force
):
if you want to remove the whole strings table (regardless it being used
for other languages as well), you can do it this way:
<?php
$tr->removeLang('fr', true);
?>
Be warned it won't do any check, so you're responsible for what you do ;-)
Now let's see how we can add() a new translation for a new or an existing string.
<?php
$stringArray = array(
'en' => 'sample',
'it' => 'esempio',
);
// add the English and Italian translations associated to
// the 'smallTest' stringID and to the 'testGroup' pageID
$tr->add('smallTest', 'testGroup', $stringArray);
?>
You can remove() the translations for a certain stringID:
<?php
$tr->remove('smallTest', 'testGroup');
?>
Translation2 supports different storage drivers; this page is meant to highlight the differences among them.
Translation2 can work with any of these database abstraction layers, just pass the appropriate connection options. These three containers are absolutely identical in what they do and in how they work (wrt Translation2, of course).
<?php
// connection options
$dbinfo = array(
'hostspec' => 'host',
'database' => 'dbname',
'phptype' => 'mysql',
'username' => 'user',
'password' => 'pwd'
);
//select the preferred driver
$driver = 'MDB2'; //switch to 'DB' or 'MDB' as needed
require_once 'Translation2.php';
$tr =& Translation2::factory($driver, $dbinfo, $params);
?>
If your table definition is different from the default one, you need to
specify it in the
$params
array.
The dataobjectsimple
container is the natural choice
for those using
DB_DataObject,
as it is tightly tied to the DAO. This storage driver can use all databases supported by
the PEAR::DB
abstraction layer to fetch data.
For this container, you can't specify a custom table definition, since this feature is not supported yet. You must create a table with the following structure:
Here's the MySQL query that can be used to create the table:
then just run the dataobjects createtables script.
This is a wrapper around the gettext base functions, and thanks to File_Gettext you can retrieve an entire domain or write to an existing/new domain without calling the command line compiler utility.
The gettext container requires PEAR::File_Gettext and PEAR::I18Nv2 0.9.1 or newer, make sure you have them installed.
The construction parameters are a bit different from the db ones. To make things as simple as possible, the domain definitions and the available language list are read from two INI files.
langs.ini example:
domains.ini example:
Sample code to use Translation2 with the gettext container:
<?php
require_once 'Translation2.php';
$params = array(
'prefetch' => false,
'langs_avail_file' => 'path/to/langs.ini',
'domains_path_file' => 'path/to/domains.ini',
'default_domain' => 'messages',
//'file_type' => 'po',
);
// Better set prefetch to FALSE for the gettext container, so we don't need
// to read in the whole MO file with File_Gettext on every request.
$tr =& Translation2::factory('gettext', $params);
$tr->setLang('en');
// Note that, if there is no translation available for a string, the gettext
// container will return the string ID! This behaviour emulates native gettext.
echo $tr->get('mystring');
print_r($tr->getPage('3rddomain'));
?>
The XML container requires PEAR::XML_Serializer 0.13.0 or newer, make sure you have it installed.
<?php
$driver = 'XML';
$options = array(
'filename' => 'i18n.xml',
'save_on_shutdown' => true, //set to FALSE to save in real time
);
require_once 'Translation2.php';
$tr =& Translation2::factory($driver, $options);
?>
void constructor Translation2::Translation2 (
string $storageDriver
, mixed $options = ''
, array $params = array()
)
This constructor is deprecated in favour of the factory() method.
This function can not be called statically.
void Translation2::factory (
string $storageDriver
, mixed $options = ''
, array $params = array()
)
This is the Translation2 factory
$storageDriver
Type of the storage driver ('db', 'mdb', 'mdb2', 'gettext', 'dataobjectsimple')
$options
Additional options for the storage driver (example: if you are using DB as the storage driver, you have to pass the dsn string here)
$params
Array of parameters for the adapter class (i.e. you can set here the mappings between your table/field names and the ones used by this class)
This function can not be called statically.
string Translation2::get (
string $stringID
, string $pageID = TRANSLATION2_DEFAULT_PAGEID
, string $langID
= null
, string $defaultText = ''
)
Fetch the string from the container. If the string is empty and the DefaultText decorator is used, then return the $defaultText.
$stringID
$pageID
$langID
$defaultText
Text to display when the string is empty. NB: This parameter is only used in the DefaultText decorator
This function can not be called statically.
object Decorator& Translation2::getDecorator (
string $decorator
, object [optional] 1
)
This method is used to get a decorator instance. A decorator can be seen as a filter, i.e. something that can change or handle the values of the objects/vars that pass through.
$decorator
Name of the decorator
$obj
[optional]
Object to decorate (the default object being $this)
returns object Reference of a Translation2_Decorator subclass
This function can not be called statically.
mixed Translation2::getLang (
string $langID
= null
, string $format = 'name'
)
Get some extra information about the language (its full name, the localized error text, ...)
$langID
$format
['name', 'meta', 'error_text', 'array']
returns [string | array], depending on $format
This function can not be called statically.
array Translation2::getLangs (
string $format = 'name'
)
Get some extra information about the languages (their full names, the localized error text, their codes...)
$format
['ids', 'names', 'array']
This function can not be called statically.
array Translation2::getPage (
string $pageID = TRANSLATION2_DEFAULT_PAGEID
, string $langID
= null
)
Same as getRawPage(), but resort to fallback language and replace parameters when needed.
NB: in Translation2 lingo, a "page" is just a logical "group of strings", it doesn't have to be a "phisical" (HTML or whatever) page.
$pageID
$langID
This function can not be called statically.
string Translation2::getRaw (
string $stringID
, string $pageID = TRANSLATION2_DEFAULT_PAGEID
, string $langID
= null
, string $defaultText = ''
)
Fetch the string from the container. If the string is empty return $defaultText.
$stringID
$pageID
$langID
$defaultText
Text to display when the string is empty
This function can not be called statically.
array Translation2::getRawPage (
string $pageID = TRANSLATION2_DEFAULT_PAGEID
, string $langID
= null
)
Fetch the page (aka 'group of strings') from the container, without applying any formatting and without replacing the parameters.
$pageID
$langID
This function can not be called statically.
string Translation2::getStringID (
string $string
, mixed $pageID = TRANSLATION2_DEFAULT_PAGEID
)
Get the stringID of the string passed as parameter
$string
This is NOT the stringID, this is a real string. The method will return its matching stringID.
$pageID
This function can not be called statically.
array Translation2::replaceEmptyStringsWithKeys (
array $strings
)
Replace the array values with their keys when empty, just like gettext would do.
$strings
array of strings (e.g. as returned by getPage())
mixed Translation2::setCharset (
string $charset
)
Set the charset that shall be used when retrieving strings. Currently only used by the MDB2 container.
$charset
charset name to pass to the container (for instance, 'utf8')
This function can not be called statically.
void Translation2::setLang (
string $langID
)
Set the language that shall be used when retrieving strings.
$langID
language code (for instance, 'en' or 'it')
This function can not be called statically.
void Translation2::setPageID (
mixed $pageID
= null
, string $langID
)
Set the page (aka 'group of strings') that shall be used when retrieving strings. If you set it, you don't have to state it in each get() call.
$pageID
This function can not be called statically.
void Translation2::setParams (
params $params
= null
)
Set the replacement for the parameters in the string(s). Parameter delimiters are customizable.
$params
This function can not be called statically.
This package is not documented yet.
Translation2_Admin
Translation2_Admin Inherited Methods
Method Name | Summary |
---|---|
Constructor Translation2::Translation2() | Constructor (deprecated in favour of factory()) |
Factory Translation2::factory() | Return a Translation2 instanciated object |
Translation2::get() | Get translated string |
Translation2::getDecorator() | Return an instance of a decorator |
Translation2::getLang() | get language info |
Translation2::getLangs() | get available languages |
Translation2::getPage() | Same as getRawPage(), but resort to fallback language and replace parameters when needed |
Translation2::getRaw() | Get translated string (as-is) |
Translation2::getRawPage() | Get the array of strings in a page |
Translation2::setCharset() | Set the correct charset in the database |
Translation2::setLang() | Set default lang |
Translation2::setPageID() | Set default page |
Translation2::setParams() | Set parameters for next string |
Translation2::getStringID() | Get the stringID for a given string |
mixed Translation2_Admin::addLang (
array $langData
, array $options
)
If the langsAvail table doesn't exist yet, it is created.
$langData
$options
returns true on success, PEAR_Error on failure
This function can not be called statically.
mixed Translation2_Admin::updateLang (
array $langData
)
Update the language details.
$langData
returns true on success, PEAR_Error on failure
This function can not be called statically.
mixed Translation2_Admin::removeLang (
string $langID
= null
, boolean $force
= false
)
If the strings table holds other languages and $force
==FALSE,
then only the lang column is dropped. If $force
==TRUE, the whole
table is dropped without any check
$langID
Code of the language to remove
$force
returns true on success, PEAR_Error on failure
This function can not be called statically.
mixed Translation2_Admin::add (
string $stringID
, string $pageID
= null
, array $stringArray
)
Add a new translated string (or a set of translated strings) for a given
stringID
. For instance, to add the English, Spanish and Italian
translations for the stringID 'example', use the following code:
<?php
$stringArray = array(
'en' => 'example',
'es' => 'ejemplo',
'it' => 'esempio',
);
$tr->add('example', 'mypage', $stringArray);
?>
$stringID
identificator for the string
$pageID
destination pageID, i.e. the group of strings where this string belongs to.
$stringArray
Associative array with string translations.
returns true on success, PEAR_Error on failure
This function can not be called statically.
mixed Translation2_Admin::remove (
string $stringID
, string $pageID
= null
)
Remove all the translations for the given stringID
+
pageID
pair.
$stringID
$pageID
returns true on success, PEAR_Error on failure
This function can not be called statically.
mixed Translation2_Admin::update (
string $stringID
, string $pageID
, array $stringArray
)
Update the language details.
$stringID
ID of the string to translate
$pageID
ID of the page (or group) the string to translate belongs to
$stringArray
Associative array with string translations:
returns true on success, PEAR_Error on failure
This function can not be called statically.
mixed Translation2_Admin::removePage (
string $pageID
= null
)
Remove all the translations for the given pageID
.
$pageID
page/group ID
returns true on success, PEAR_Error on failure
This function can not be called statically.
void Translation2_Admin::cleanCache (
)
If you use the CacheLiteFunction decorator, you may want to invalidate the cache after a change in the data base.
This function can not be called statically.
array Translation2_Admin::getPageNames (
)
Get a list of all the pageIDs in any table.
returns array of pageIDs
This function can not be called statically.
object Decorator& Translation2_Admin::getAdminDecorator (
string $decorator
, object [optional] 1
)
This method is used to get a decorator instance. A decorator can be seen as a filter, i.e. something that can change or handle the values of the objects/vars that pass through.
$decorator
Name of the decorator
$obj
[optional]
Object to decorate (the default object being $this)
returns object Reference of a Translation2_Admin_Decorator subclass
This function can not be called statically.
Create a subclass of this class for your own "decoration". The base class acts as a proxy to these methods:
get()
getDecorator()
getLang()
getLangs()
getPage()
getRaw()
getRawPage()
getStringID()
replaceEmptyStringsWithKeys()
setCharset()
setContainerOptions() [protected]
setLang()
setOption()
setOptions() [protected]
setPageID()
setParams()
translate()
If you want to "decorate" any of these methods, you have to override them in your custom Decorator.
Translation2_Decorator
Class | Summary |
---|---|
Translation2_Decorator_CacheLiteFunction | Decorator to cache fetched data using Cache_Lite_Function class |
Translation2_Decorator_CacheMemory | Decorator to cache fetched data in memory |
Translation2_Decorator_DefaultText | Decorator to provide a fallback text for empty strings. |
Translation2_Decorator_ErrorText | Decorator to provide an error_text message for empty strings. |
Translation2_Decorator_Iconv | Decorator to switch from/to different encodings. |
Translation2_Decorator_Lang | Decorator to provide a fallback language for empty strings. |
Translation2_Decorator_SpecialChars | Decorator to replace special chars with the matching html entities. |
Translation2_Decorator_UTF8 | Decorator to convert UTF-8 strings to ISO-8859-1 |
This decorator provides a very efficient cache layer. It requires PEAR::Cache_Lite. It supports all the main options supported by Cache_Lite:
lifeTime
[integer]
cacheDir
[string]
fileLocking
[boolean]
caching
[boolean]
If you need to pass an option directly to the Cache_Lite object, you can use setCacheOption().
<?php
$tr = new Translation2($driver, $dbinfo, $params);
$tr =& $tr->getDecorator('CacheLiteFunction');
$tr->setOption('cacheDir', '/var/tmp/');
$tr->setOption('lifeTime', 3600*24*7); //one week
//change a custom Cache_Lite option
$tr->setCacheOption($name, $value);
?>
This decorator provides a memory cache layer. It does NOT persist through requests,
only in the current execution of the script. You can turn off prefetch
if you want small network load (but it will increase the number of queries to the database)
<?php
$tr = new Translation2($driver, $dbinfo, $params);
$tr =& $tr->getDecorator('CacheMemory');
$tr->setOption('prefetch', true); //default value is true
?>
When the fetched string is empty, it replaces it with the 4th parameter
of the
get()
method, i.e. $defaultText
.
If the defaultText
parameter is empty too, then return
"$emptyPostfix.$outputString.$emptyPrefix", the three variables
being class properties you can set to a custom string.
When getPage() is called, all the empty strings in the page are replaced by their stringID value.
<?php
$tr = new Translation2($driver, $dbinfo, $params);
$tr =& $tr->getDecorator('DefaultText');
// %stringID% will be replaced with the stringID
// %pageID_url% will be replaced with the pageID
// %stringID_url% will replaced with a urlencoded stringID
// %url% will be replaced with the targeted url
$tr->outputString = '%stringID%<a href="%url%">(T)</a>'; //default: '%stringID%'
$tr->url = '#'; //same as default
$tr->emptyPrefix = '['; //default: empty string
$tr->emptyPostfix = ']'; //default: empty string
?>
When the fetched string is empty, it replaces it with the contents of the "error_text" column of the langs_avail table
<?php
$tr = new Translation2($driver, $dbinfo, $params);
$tr =& $tr->getDecorator('ErrorText');
?>
This decorator is very useful when you want to provide a fallback language
for empty strings. It is stackable, so you can have more than one default language.
Use setOption() with the fallbackLang
parameter to specify the fallback language of the current decorator.
<?php
$tr = new Translation2($driver, $dbinfo, $params);
//set English as the main language
$tr->setLang('en');
//set Italian as the first fallback language
$tr =& $tr->getDecorator('Lang');
$tr->setOption('fallbackLang', 'it');
//set Spanish as the second fallback language
$tr =& $tr->getDecorator('Lang');
$tr->setOption('fallbackLang', 'es');
?>
Use setOption() with the encoding
parameter to specify the target encoding of the current decorator.
<?php
$tr = new Translation2($driver, $dbinfo, $params);
//set Hungarian as the main language
$tr->setLang('hu');
//encode all the strings using the ISO-8859-2 charset
$tr =& $tr->getDecorator('Iconv');
$tr->setOption('encoding', 'ISO-8859-2');
?>
This decorator replaces special chars with the matching html entities.
Use setOption() with the charset
parameter to specify the target charset of the current decorator (the default
is 'ISO-8859-1'):
<?php
$tr = new Translation2($driver, $dbinfo, $params);
$tr =& $tr->getDecorator('SpecialChars');
$tr->setOption('charset', 'UTF-8');
?>
This decorator calls utf8_decode() on each string
<?php
$tr = new Translation2($driver, $dbinfo, $params);
$tr =& $tr->getDecorator('UTF8');
?>
Name | Value | Line Number |
---|---|---|
TRANSLATION2_EMPTY_PAGEID_KEY | 'array_key_4_empty_pageID' | 34 |
TRANSLATION2_NULL_PAGEID_KEY | 'array_key_4_null_pageID' | 40 |
Name | Value | Line Number |
---|---|---|
TRANSLATION2_DEFAULT_PAGEID | 'translation2_default_pageID' | 38 |
TRANSLATION2_ERROR | -1 | 43 |
TRANSLATION2_ERROR_METHOD_NOT_SUPPORTED | -2 | 44 |
TRANSLATION2_ERROR_CANNOT_CONNECT | -3 | 45 |
TRANSLATION2_ERROR_CANNOT_FIND_FILE | -4 | 46 |
TRANSLATION2_ERROR_DOMAIN_NOT_SET | -5 | 47 |
TRANSLATION2_ERROR_INVALID_PATH | -6 | 48 |
TRANSLATION2_ERROR_CANNOT_CREATE_DIR | -7 | 49 |
TRANSLATION2_ERROR_CANNOT_WRITE_FILE | -8 | 50 |
TRANSLATION2_ERROR_UNKNOWN_LANG | -9 | 51 |
Provides Packages for logging purposes
Implements both an abstraction for various logging mechanisms and the Subject end of a Subject-Observer pattern.
The full online documentation is available at the maintainer's website: http://pear.github.com/Log/.
Provides Packages for creating and working with electronic mails
An interface for sending EMails
Mail supports different types of backends to send email. So two steps are necessary to send an email.
Create a new instance of a specific Mail-Backend with the factory() method.
Send the mail using the send() method.
Mail supports three types of backends:
mail
Sends a mail using PHP's built-in mail() function.
sendmail
Sends a mail using a sendmail program.
smtp
Sends a mail directly connecting to a smtp server.
object &factory (
string $backend
,
array $params = array()
)
Creates a instance of a backend-specific mailer class.
string $backend
- the name of the backend
"mail"
,"smtp"
,
"sendmail"
array $params
-
a array of backend specific parameters.
List of parameter for the backends
mail
If safe mode is disabled, $params
will be passed as the fifth argument to the PHP
mail() function. If
$params
is an array, its
elements will be joined as a space-delimited string.
sendmail
$params["sendmail_path"]
-
The location of the sendmail program on the
filesystem. Default is /usr/bin/sendmail
.
$params["sendmail_args"]
-
Additional parameters to pass to the sendmail. Default is
-i
.
smtp
$params["host"]
- The server to connect.
Default is localhost
.
$params["port"]
- The port to connect.
Default is 25
.
$params["auth"]
- Whether or not to use
SMTP authentication.
Default is FALSE.
$params["username"]
- The username to use
for SMTP authentication.
$params["password"]
- The password to use
for SMTP authentication.
$params["localhost"]
- The value to give
when sending EHLO or HELO.
Default is localhost
$params["timeout"]
- The SMTP connection
timeout. Default is NULL (no timeout).
$params["verp"]
- Whether to use VERP
or not. Default is FALSE.
$params["debug"]
- Whether to enable SMTP
debug mode or not. Default is FALSE.
Mail internally uses
Net_SMTP::setDebug
.
$params["persist"]
- Indicates whether or
not the SMTP connection should persist over multiple calls to the
send() method.
$params["pipelining"]
- Indicates whether or
not the SMTP commands pipelining should be used.
object
- a specific Mail instance
or a PEAR_Error object on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"Unable to find class for driver
xxx "
|
Mailer backend class was not found. |
Check the $backend parameter, if correct
reinstall and/or update your Mail package.
|
This function should be called statically.
mixed send (
mixed $recipients
, array $headers
, string $body
)
Sends a mail. The send() method is provided by the object returned from factory()
mixed $recipients
- an array or a
string with comma separated recipients.
array $headers
-
an associative array of headers. The header name is used as
key and the header value as value. If you want to override
the envelope sender of the email, set the Return-Path header
and that value will be used instead of the value of the From:
header.
string $body
-
the body of the email.
boolean
- TRUE or
a PEAR_Error object on failure.
Mailer driver | Error code | Error message | Reason | Solution |
---|---|---|---|---|
sendmail | NULL | "No from address given." |
The $headers array requires
at least a from entry.
|
Add a From header:
<?php |
sendmail | NULL | "From address specified with dangerous characters." |
The from entry in the $headers array
contains one ore more characters which could be non-RFC compliant.
|
Check the given from address for characters like: spaces or
; or & or
` (backtick).
|
sendmail | NULL |
"sendmail [path to sendmail ]
not executable"
|
The path to sendmail program is not correct. No sendmail executable found there. |
Check the $param['sendmail_path'] entry
in your
Mail::factory() call. If you use
another mailer then sendmail, ie. qmail,
check installation of the mailer. Normally it should includes
a sendmail wrapper.
|
sendmail | NULL |
"sendmail returned error code
code "
|
Sendmail returns a error, which must be handled by use. | See the documention of your mailer program. |
smtp | PEAR_MAIL_SMTP_ERROR_CREATE |
"Failed to create a Net_SMTP object" | Failure in class creation. | Reinstall/update the Net_SMTP package. |
smtp | PEAR_MAIL_SMTP_ERROR_CONNECT |
"Failed to connect to
host:port "
|
Connect to SMTP server failed. |
Check $param['port'] and
$param['host'] entries
in your
Mail::factory() call.
|
smtp | PEAR_MAIL_SMTP_ERROR_AUTH |
"method authentication failure"
|
Authentication failed. |
Check $param['auth'] ,
$param['username'] and
$param['password'] entries
in your
Mail::factory() call.
Ensure to use the correct authentication method
for the SMTP server.
|
smtp | PEAR_MAIL_SMTP_ERROR_FROM |
"No From: address has been provided" |
The $headers array requires
at least a from entry.
|
Add a From header:
<?php |
smtp | PEAR_MAIL_SMTP_ERROR_SENDER |
"Failed to set sender: from "
|
Setting the sender address failed. | Check the RFC-compliances of the sender address and the server connnectivity. |
smtp | PEAR_MAIL_SMTP_ERROR_RECIPIENT |
"Failed to add recipient: recipient
"
|
Sending of recipient address failed. | Check the RFC-compliances of the recipient address and the server connnectivity. |
smtp | PEAR_MAIL_SMTP_ERROR_DATA |
"Failed to send data" | Body of the mail message could not send | Check the RFC-compliances of the message body and the server connnectivity. |
This function can not be called statically.
<?php
include('Mail.php');
$recipients = 'joe@example.com';
$headers['From'] = 'richard@example.com';
$headers['To'] = 'joe@example.com';
$headers['Subject'] = 'Test message';
$body = 'Test message';
$params['sendmail_path'] = '/usr/lib/sendmail';
// Create the mail object using the Mail::factory method
$mail_object =& Mail::factory('sendmail', $params);
$mail_object->send($recipients, $headers, $body);
?>
This class performs email address checking according to the RFC822 specification.
Note that the class only checks for a proper format of the indicated email address. This means it is not guaranteed that the email address itself exists or is owned by the particular user. You may also want to send the user an email, and force them to respond.
array parseAddressList (
string $address = ''
,
string $defaultDomain = 'localhost'
,
boolean $nestGroups
= null
,
boolean $validate
= null
)
Extracts the given addresses into their parts.
string $address
-
the address(es) to validate
string $defaultDomain
-
the default domain to use in case of absence in the
given email address.
boolean $nestGroups
-
whether to return the structure with groups
nested for easier viewing.
boolean $validate
-
whether to validate atoms. Turn this off if you need to run addresses
through before encoding the personal names, for instance.
array
-
a nested array of anonymous objects.
If $nestGroups
set to FALSE, you
can jump over the next paragraph.
Every array entry contains an object per group. This object has two attributes:
groupname
- the name of
the group
addresses
- an array of
all addresses of a group
The addresses
array consists of an
array of anonymous objects for each address. This object
comes with the following attributes:
personal
-
the name of the address owner
comment
-
an array, an entry for each comment per address
mailbox
-
the name of the mailbox, the part before the @
host
-
the name of the server, the part after the @
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | every | The given address string is not RFC822 compliant | The error code contains a description of the error. |
This function can be called statically.
This class checks the string only. It does not check for the existence of an email address.
Extract some addresses
<?php
$address = 'My group: "Richard" <richard@localhost>;, ted@example.com (A comment)';
$addresses = Mail_RFC822::parseAddressList($address, 'phpguru.org', TRUE);
print_r($addresses);
?>
Mail_IMAP provides a simplified backend for working with the c-client (IMAP) extension. It serves as an OO wrapper for commonly used c-client functions. It provides structure and header parsing as well as body retrieval.
Mail_IMAP provides an object-oriented backend for working with the PHP c-client (imap) extension. The c-client extension provides functionality for connecting to IMAP, POP3 or NNTP mailboxes. Mail_IMAP acts as a wrapper for the most used c-client functions providing parsing of multipart messages, intelligent message body retrieval as well as header parsing and retrieval. Mail_IMAP is an ideal solution when a customizable webmail template is necessary. Mail_IMAP may also act as a component in an automated mailing list manager, as it can do all the dirty work of retrieving the subject line, message body, attachments or other message components.
An mbox mail file is contains plain emails concatenated in one big file. Since each mail starts with "From ", and ends with a newline, they can be separated from each other.
This class takes a mbox filename in the constructor, generates an index where the mails start and end when calling open() and returns single mails with get(), using the positions in the index.
With the help of this class, you also can insert(), remove() and update() messages in the mbox file. When calling one of this methods, the class checks if the file has been modified since the index was created - changing the file with the wrong positions in the index would very likely corrupt it. This check is not done when retrieving single messages via get(), as this would slow down the process if you retrieve thousands of mails. You can, however, call hasBeenModified() before using get() to check for modification yourself. If the method returns true, you should close() and re-open() the file.
If something strange happens and you don't know why, activate debugging with setDebug(true). You also can modify the temporary directory in which changed mboxes are stored when adding/removing/modifying by using setTmpDir('/path/')
PEAR
Mail_Mbox
Before you can do anything with the mbox file, you need to create an instance of the Mail_Mbox class and open() it.
<?php
require_once 'Mail/Mbox.php';
$mbox = new Mail_Mbox('/path/to/mbox');
$mbox->open();
//do more here
?>
After opening the file, you can retrieve single messages via get(). The size() method helps you to determine the number of messages:
<?php
//... initialisation
for ($n = 0; $n < $mbox->size(); $n++) {
$message = $mbox->get($n);
//do something with the message text
}
?>
If you're done working with the file, close() it.
<?php
//... other code
$mbox->close();
?>
Also have a look at the Mail_Mbox examples directory in
/path/to/pear/docs/Mail_Mbox/examples/
.
void constructor
Mail_Mbox::Mail_Mbox
(
string
$file
)
After creating a new instance, you should use open() to open an mbox file.
$file
Filename to open.
mixed
Mail_Mbox::close
(
)
Close the Mbox file opened by open().
returns true on success, else PEAR_Error
string
Mail_Mbox::get
(
int
$message
)
Returns the full email message at the given position.
Note: Message number start from 0.
$message
The number of the message to retrieve.
returns Return the message, or PEAR_Error on error.
boolean
Mail_Mbox::getDebug
(
)
Returns true if debugging is enabled.
returns true if debug is enabled.
string
Mail_Mbox::getTmpDir
(
)
Returns the temporary directory.
returns The temporary directory.
boolean
Mail_Mbox::hasBeenModified
(
)
Checks if the mbox file has been modified since opening. If this is true, the file needs to be re-opened.
returns True if it has been modified.
mixed
Mail_Mbox::insert
(
string
$content
,
mixed
$offset
= null
)
Mail_Mbox will insert the message according to the specified offset. (Remember: message 3 is the fourth message). The default is to insert the message AFTER the last message (offset = null).
Note: Mail_Mbox automatically adds \n\n at end of the message.
$content
The content of the new message.
$offset
returns Return true or the PEAR_Error object on failure.
void
Mail_Mbox::open
(
)
Also, this function will process the mbox file and create a cache that tells each message start and end bytes.
mixed
Mail_Mbox::remove
(
int
$message
)
Removes the message with the given id.
Note: messages start with 0.
$message
The number of the message to remove, or array of message ids to remove.
returns Return true or PEAR_Error object on failure.
void
Mail_Mbox::setDebug
(
boolean
$debug
)
Sets the debug flag. If debugging is enabled, you will get more output.
$debug
True if debug is on, otherwise false.
see
Mail_Mbox::$debug
void
Mail_Mbox::setTmpDir
(
string
$tmpdir
)
Sets the temporary directory in which new mbox files are stored.
$tmpdir
The new temporary directory.
see
Mail_Mbox::$tmpdir
int
Mail_Mbox::size
(
)
Returns the number of messages in the mbox file.
returns Number of messages on Mbox (starting on 1, 0 if no message exists).
mixed
Mail_Mbox::update
(
int
$message
,
string
$content
)
Replaces a given message with the text passed to this method.
Mail_Mbox auto adds \n\n at end of the message
messages start with 0.
$message
The number of the Message to update.
$content
The new content of the Message.
returns Return true if all is ok, otherwise PEAR_Error
void
Mail_Mbox::_move
(
string
$ftempname
,
string
$filename
)
Used internally to copy the content of the temp file to the mbox file.
$ftempname
Source file - will be removed
$filename
Output file
void
Mail_Mbox::_process
(
)
Get start bytes and end bytes of each messages.
A Package to enable easy creation of complex multipart emails. If you look for a simple API for creating such emails, then Mail_Mime class will probably suffice. Else you can use Mail_mimePart, which gives you better control about MIME creation.
Mail_mime Mail_mime (
mixed $params = array()
)
Creates a new instance of Mail_Mime
array $params
- An associative array of parameters.
These parameters affect the way the message is built. Use
Mail_Mime::setParam()
to set them later.
$params['eol']
- Type of line end.
Default is ""\r\n"".
$params['delay_file_io']
- Specifies if attachment files
should be read immediately when adding them into message object or when building
the message. Useful for big messages handling using saveMessage* functions.
Default is "false".
$params['head_encoding']
- Type of encoding to
use for the headers of the email. Default is "quoted-printable".
$params['text_encoding']
- Type of encoding to
use for the plain text part of the email. Default is "quoted-printable".
$params['html_encoding']
- Type of encoding for
the HTML part of the email. Default is "quoted-printable".
$params['head_charset']
- The character set
to use for the headers. Default is "iso-8859-1".
$params['text_charset']
- The character set
to use for the plain text part of the email. Default is "iso-8859-1".
$params['html_charset']
- The character set
to use for the HTML part of the email. Default is "iso-8859-1".
Normally, it is not necessary to set parameters. But, if you want to send
the generated MIME message using
Mail
then you have to set eol
to
"\n"
.
For backward compatybility setting end of line string as constructor's first parameter is supported.
If you're working with big attachments, enabling 'delay_file_io' will provent from loading attachments into memory. Until you're not using getMessage* functions don't worry about PHP's memory limit.
boolean addAttachment (
string
$file
, string
$c_type = 'application/octet-stream'
, string
$name = ''
, boolean
$isfile = true
, string
$encoding = 'base64'
, string
$disposition = 'attachment'
, string
$charset = ''
, string
$language = ''
, string
$location = ''
, string
$n_encoding = null
, string
$f_encoding = null
, string
$description = ''
, string
$h_charset = null
)
Adds an attachment to a message.
string $file
- The file name or the data itself
string $c_type
- The content type of the image
or file.
string $name
- The suggested file name for the data. Only
used, if $file
contains data.
boolean $isfile
- Whether $file
is a file name or not.
string $encoding
- Type of transfer encoding to use
for the file data. Defaults is "base64". For text based files
(eg. scripts/html etc.) this could be given as
"quoted-printable".
string $disposition
- The content-disposition of this file
Defaults to attachment. Possible values: attachment, inline.
string $charset
- The character set of attachment's content.
string $language
- The language of the attachment
string $location
- The RFC 2557.4 location of the attachment
string $n_encoding
- Encoding of the attachment's name in Content-Type
By default filenames are encoded using RFC2231 method
Here you can set RFC2047 encoding (quoted-printable or base64) instead.
string $f_encoding
- Encoding of the attachment's filename
in Content-Disposition header.
string $description
- Content-Description header.
string $h_charset
- The character set of the headers e.g. filename
If not specified, $charset will be used
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"File is not readable file_name "
|
The file was not found or the script has not enough rights to access the file. | Check the file name and path. Check user and file permissions. |
NULL |
"Could not open file_name "
|
The file is already opened and exclusivly locked by another application. | In the most cases a program opens the file for writing. addAttachment() does no file locking, so this problem is not caused by competitive callings of this function. |
This function can not be called statically.
boolean addHTMLImage (
string $file
,
string
$c_type = 'application/octet-stream'
,
string $name = ''
,
boolean $isfile
= true
,
string $content_id
= null
)
If sending an HTML message with embedded images, use this function to add the image.
string $file
- The image file name or the image
data itself
string $c_type
- The content type of the image
or file.
string $name
- The filename of the image. Only
used, if $file
contains the image data.
boolean $isfile
- Whether $file
is a filename or not.
string $content_id
- The Content-ID value to use
for the embedded image. A NULL value will generate a suitably unique Content-ID.
When referencing the embedded image with an <img> tag, set
the "src" attribute to be "cid:whatever", where
"whatever" is the Content-ID.
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"File is not readable file_name "
|
The file was not found or the script has not enough rights to access the file. | Check the file name and path. Check user and file permissions. |
NULL |
"Could not open file_name "
|
The file is already opened and exclusivly locked by another application. | In the most cases a programm opens the file for writing. addHTMLImage() does no file locking, so this problem is not caused by competitve callings of this function. |
This function can not be called statically.
string &get (
array $param
= null
, resource $filename
= null
, boolean $skip_head
= false
)
This function should be called once you have added the text/html/images/attachments. It builds the message and returns it. It does not send it. To send what this function returns (in conjunction with the headers() -function) you would need to use the Mail::send()-function
array $param
- An associative array of build parameters.
See constructor parameters list.
resource $filename
- Optional output file where to save the message
instead of returning it.
boolean $skip_head
- True if you want to return/save only the message without headers.
string
- the body of the message
This function can not be called statically.
For versions older than 1.6.0 Mail_Mime::get() has to be called before Mail_Mime::headers().
array &headers (
array $xtra_headers
= null
, boolean $overwrite
= false
, boolean $skip_content
= false
)
Returns an array with the headers needed to prepend to the email (MIME-Version and Content-Type). Please note that the function get() has to be called before calling headers().
array $xtra_headers
- Additional headers,
the format of the argument is $array["header-name"]
=
"header-value"
boolean $overwrite
- Overwrite already existing headers.
When FALSE,
the values already set are kept.
boolean $skip_content
Don't return content headers: Content-Type, Content-Disposition and Content-Transfer-Encoding.
array
-
an associative array with the mime headers
and the additional headers.
The return value can directly passed to
the second parameter of
Mail::send().
This function can not be called statically.
Mail_Mime::headers() has to be called after Mail_Mime::get().
boolean setHTMLBody (
string $data
, boolean $isfile
= false
)
Sets the HTML part of a message
string $data
- The text to set
or, if $isfile
is
TRUE a valid filename. An URL as argument
is not allowed.
boolean $isfile
- If TRUE, the content of
given file $data
is used as message text.
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"File is not readable file_name "
|
The file was not found or the script has not enough rights to access the file. | Check the file name and path. Check user and file permissions. |
NULL |
"Could not open file_name "
|
The file is already opened and exclusivly locked by another application. | In the most cases a programm opens the file for writing. setHTMLBody() does no file locking, so this problem is not caused by competitve callings of this function. |
This function can not be called statically.
boolean setTxtBody (
string $data
, boolean $isfile
= false
)
Sets the plain text part of a message
string $data
- The text to set
or, if $isfile
is
TRUE a valid filename. An URL as argument
is not allowed.
boolean $isfile
- If TRUE, the content of
given file $data
is used as message text.
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"File is not readable file_name "
|
The file was not found or the script has not enough rights to access the file. | Check the file name and path. Check user and file permissions. |
NULL |
"Could not open file_name "
|
The file is already opened and exclusivly locked by another application. | In the most cases a programm opens the file for writing. setTxtBody() does no file locking, so this problem is not caused by competitve callings of this function. |
This function can not be called statically.
string encodeHeader (
string $name
, string $value
, string $charset
, string $encoding
)
Returns encoded header value as for RFC2047.
string $name
- The header name
string $value
- The header body
string $charset
- Character set used in header body
string $encoding
- Encoding ("base64"
or "quoted-printable")
string
- Returns encoded header body (without a name)
This function can not be called statically.
Mail_mimePart Mail_mimePart (
string
$body = ''
, array
$params = array()
)
Create a new Mail_mimePart object.
string $body
-
The body of the mime part if any. Default is an empty string.
array $params
-
An associative array of parameters:
$params["content_type"]
-
The content type for this part ie.
multipart/mixed
$params["encoding"]
-
The encoding to use ie. 7bit
,
8bit
, base64
or
quoted-printable
$params["cid"]
-
content ID to apply
$params["disposition"]
-
Content disposition inline
or attachment
$params["filename"]
-
Optional filename parameter for content disposition
$params["description"]
-
Content description
$params["charset"]
-
Character set to use
$params["name_encoding"]
-
Encoding of the attachment name (Content-Type)
By default filenames are encoded using RFC2231
Here you can set RFC2047 encoding (quoted-printable
or base64) instead
$params["filename_encoding"]
-
Encoding of the attachment filename (Content-Disposition)
$params["headers_charset"]
-
Charset of the headers e.g. filename, description
If not set, "charset" will be used
$params["eol"]
-
End of line sequence. Default: "\r\n"
$params["body_file"]
-
Location of file with part's body (instead of $body)
Default is an empty array.
This function can be called statically.
resource &addSubPart (
string $body
, array $params
)
Adds a sub part to the current MIME part and returns a reference to it
string
-
the body of the sub part
array
-
the parameter for the sub part. See
constructor for the possible values.
resource
-
a reference to the added part
This function can not be called statically.
Add two attachments to a mail
<?php
include 'Mail/mimePart.php';
...
$params['content_type'] = 'multipart/mixed';
$email = new Mail_mimePart('', $params);
// Here we add a text part to the multipart we have
// already. Assume $body contains plain text.
$params['content_type'] = 'text/plain';
$params['encoding'] = '7bit';
$text = $email->addSubPart($body, $params);
// Now add an attachment. Assume $contents is
// the contents of the attachment
$params['content_type'] = 'application/zip';
$params['encoding'] = 'base64';
$params['disposition'] = 'attachment';
$params['dfilename'] = 'example.zip';
$attach =& $email->addSubPart($contents, $params);
// Now build the email. Note that the encode
// function returns an associative array containing two
// elements, body and headers. You will need to add extra
// headers, (eg. Mime-Version) before sending.
$email = $email->encode();
$email['headers']['Mime-Version'] = '1.0';
...
?>
array encode (
string
$boundary = null
)
Encodes and returns the email
string $boundary
-
Optional pre-defined boundary string
array
-
an associative array containing two elements,
body and headers. The headers element is itself
an indexed array.
The key names are
'headers'
-
an array with the mail headers
'body'
-
a string with the mail body
This function can not be called statically.
array encodeToFile (
string
$filename
, string
$boundary = null
, boolean
$skip_head = false
)
Encodes and saves the email into file. File must exist. Data will be appended to the file
string $filename
-
Output file location
string $boundary
-
Optional pre-defined boundary string
boolean $skip_head
-
True if you don't want to save headers
array
-
An associative array containing message headers or PEAR error object
This function can not be called statically.
<?php
include 'Mail.php';
include 'Mail/mime.php' ;
$text = 'Text version of email';
$html = '<html><body>HTML version of email</body></html>';
$file = '/home/richard/example.php';
$crlf = "\n";
$hdrs = array(
'From' => 'you@yourdomain.com',
'Subject' => 'Test mime message'
);
$mime = new Mail_mime(array('eol' => $crlf));
$mime->setTXTBody($text);
$mime->setHTMLBody($html);
$mime->addAttachment($file, 'text/plain');
$body = $mime->get();
$hdrs = $mime->headers($hdrs);
$mail =& Mail::factory('mail');
$mail->send('postmaster@localhost', $hdrs, $body);
?>
The Mail_mimeDecode class provides an API to decode mail/MIME messages.
This class will parse a raw mime email and return the structure. Returned structure is similar to that returned by imap_fetchstructure().
void Mail_mimeDecode (
string
$input
)
Create a new Mail_mimeDecode object.
string $input
-
the input to decode
This function can be called statically.
object decode (
array $args
= null
)
This function performs the decoding and returns a structure containing the message data.
array $args
-
an array with the function arguments
boolean $args['include_bodies']
-
whether to include the bodies in the returned structure.
boolean $args['decode_bodies']
-
whether to decode the returned bodies.
boolean $args['decode_headers']
-
whether to decode the headers
(RFC2047).
string $args['input']
-
if and only if called statically, this should be used to specify
the input to be decoded.
string $args['crlf']
-
if and only if called statically, this should be used to specify
the line ending type.
object
-
array $return->headers
-
an associative array of the headers. The keys of the array
are the header names (lowercased) whilst the values are the
header values (original case). If there are multiple headers
with the same name (eg. Received:
)
then the value is a
numerically indexed array of each of the header values.
If the parameter decode_headers
is specified as TRUE, the headers will be decoded
according to RFC 2047.
string $return->ctype_primary
-
the first part of the content type (ie. before the forward slash).
Eg. if the content type is
multipart/mixed
, ctype_primary would
be "multipart"
.
string $return->ctype_secondary
-
the second part of the content type. Eg. If the content type is
multipart/mixed
, ctype_secondary would
be "mixed"
.
array $return->ctype_parameters
-
if the content type header has any parameters
(eg. boundary="=_hudfhdsalfhds8fy8329hfj
")
then
they will be in this associative array. Keys are the parameter
name (eg. boundary
) whilst the values
are the parameter
values (eg. =_hudfhdsalfhds8fy8329hfj
).
string $return->disposition
-
if the Content-Disposition header is present, its value
will be given here. This is usually either
"inline"
or
"attachment"
.
array $return->d_parameters
-
if any parameters are given with the Content-Disposition header,
they will be given here in an associative array, keys being the
parameter names and values being the parameter values.
"name"
and "filename"
are two common examples here.
array $return->body
-
if the include_bodies
parameter is given
when instanciating the class, (either statically or via a
concrete instance), then this will be present if the part
in question has a body.
MIME parts with content type multipart/*
generally do not not have bodies, instead consisting of
subparts. If the parameter decode_bodies
is specified as TRUE then the body will
be decoded.
array $return->parts
-
if a MIME part consists of subparts, then this array will be
present consisting of objects with the same properties as
described here.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | " Called statically and no input given " |
You called the function statically and forgot
to fill $args['input']
|
Fill $args['input'] with the
content to decode or do not call the function
statically.
|
NULL | every other | See the error message. | The input or parts of the input does not complies to the MIME standard. |
This function can be called statically.
array &uudecode (
string
$input
)
Decodes UU-coded data. 'Unix-to-Unix'-Encoding is used to send binary files (eg. programs, graphics) over 7bit-ASCII-only media, like email.
string $input
-
data to decode
array
-
the decoded data
string $return[]['filename']
-
the name of the UUencoded file.
string $return[]['fileperm']
-
the file permissions of the UUencoded file, if given.
The format is unix-styled, ie. "0666"
or "666"
.
string $return[]['filedata']
-
the decoded content of the UUencoded file.
This function can be called statically.
string getXML (
array $decoded
)
getXML() converts the returned array from decode () into a valid XML document.
string
-
the XML document
This function can be called statically.
Create a XML representation
<?php
...
$output = $obj->decode();
$xml = Mail_mimeDecode::getXML($output);
...
?>
Decode an email
<?php
require_once 'Mail/mimeDecode.php';
...
$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$decoder = new Mail_mimeDecode($input);
$structure = $decoder->decode($params);
?>
This example calls the decode function statically (ie no object, straight function call) and then passes the structure to the getXML() function.
<?php
...
$params['include_bodies'] = true;
$params['decode_bodies'] = false;
$params['decode_headers'] = true;
$params['input'] = $input;
$params['crlf'] = "\r\n";
$structure = Mail_mimeDecode::decode($params);
$xml = Mail_mimeDecode::getXML($structure);
?>
This class allows you to queue mails for later delivery.
Pear
Mail_Queue
We are using the db-container for the example and a mysql database. You need to create some tables in the mysql-database to store the messages:
mysql.sql
CREATE TABLE mail_queue ( id bigint(20) NOT NULL default '0', create_time datetime NOT NULL default '0000-00-00 00:00:00', time_to_send datetime NOT NULL default '0000-00-00 00:00:00', sent_time datetime default NULL, id_user bigint(20) NOT NULL default '0', ip varchar(20) NOT NULL default 'unknown', sender varchar(50) NOT NULL default '', recipient text NOT NULL, headers text NOT NULL, body longtext NOT NULL, try_sent tinyint(4) NOT NULL default '0', delete_after_send tinyint(1) NOT NULL default '1', PRIMARY KEY (id), KEY id (id), KEY time_to_send (time_to_send), KEY id_user (id_user) );
First you need to define some options. As you need them two times (once for adding messages, once for sending the messages) its always good to add them to a config-file. Let's call it config.php
config.php
<?php
require_once "Mail/Queue.php";
// options for storing the messages
// type is the container used, currently there are 'creole', 'db', 'mdb' and 'mdb2' available
$db_options['type'] = 'mdb2';
// the others are the options for the used container
// here are some for db
$db_options['dsn'] = 'mysql://user:password@host/database';
$db_options['mail_table'] = 'mail_queue';
// here are the options for sending the messages themselves
// these are the options needed for the Mail-Class, especially used for Mail::factory()
$mail_options['driver'] = 'smtp';
$mail_options['host'] = 'your_server_smtp.com';
$mail_options['port'] = 25;
$mail_options['localhost'] = 'localhost'; //optional Mail_smtp parameter
$mail_options['auth'] = false;
$mail_options['username'] = '';
$mail_options['password'] = '';
?>
So we are done configuring it, now let's use it. First we need to construct a mail-message and add it to the queue:
add_message.php
<?php
include './config.php';
/* we use the db_options and mail_options here */
$mail_queue =& new Mail_Queue($db_options, $mail_options);
$from = 'user@server.com';
$to = "user2@server.com";
$message = 'Hi! This is test message!! :)';
$hdrs = array( 'From' => $from,
'To' => $to,
'Subject' => "test message body" );
/* we use Mail_mime() to construct a valid mail */
$mime =& new Mail_mime();
$mime->setTXTBody($message);
$body = $mime->get();
// the 2nd parameter allows the header to be overwritten
// @see http://pear.php.net/bugs/18256
$hdrs = $mime->headers($hdrs, true);
/* Put message to queue */
$mail_queue->put($from, $to, $hdrs, $body);
?>
NB: the first time you call put(), PEAR::DB and PEAR::MDB2 will create a new table to keep the sequence number, so make sure the db user you are using has "CREATE TABLE" privileges. Or you can create the table separately, calling the createSequence() method of PEAR::DB or PEAR::MDB2 (you should also be aware that the two DBAL will create a table with a different field name: "id" for DB, "sequence" for MDB2).
Ok, now we've used the simple way to add a message ... there are more advanced options, please check docs of the put-function for these. Now we need to send the messages. This is most often done by using a cron-job which regularly runs a script to send the messages. Here is a simple script to achieve this:
send_messages.php
<?php
include './config.php';
/* How many mails could we send each time the script is called */
$max_amount_mails = 50;
/* we use the db_options and mail_options from the config again */
$mail_queue =& new Mail_Queue($db_options, $mail_options);
/* really sending the messages */
$mail_queue->sendMailsInQueue($max_amount_mails);
?>
We are done. Now run the last script regularly and add your mails to the queue as needed.
Since Mail_Queue v.1.1, the preload() method doesn't preload ALL the mails in memory, but just a few of them each time. When the buffer is empty, it is filled again automatically. You can set the size of the buffer via the new setBufferSize() method.
You can also send the stored emails one by one. Here is a simple script to achieve this:
send_messages_one_by_one.php
<?php
// set the internal buffer size according your
// memory resources (the number indicates how
// many emails can stay in the buffer at any
// given time)
$mail_queue->setBufferSize(20);
//set the queue size (i.e. the number of mails to send)
$limit = 50;
$mail_queue->container->setOption($limit);
// loop through the stored emails and send them
while ($mail = $mail_queue->get()) {
$result = $mail_queue->sendMail($mail);
}
?>
The sendMailsInQueue method, since version 1.2.3, has callback support. This may be utilised for report generation and postprocessing.
The callback function is called before the relevant entry is deleted from the mail_queue table in the database so if necessary you could add extra fields to it for inserting into your log/report table. The function should accept only one parameter - an associative array of values; 'id', 'queued_as' and 'greeting'.
You'll need to use recent releases of the PEAR::Mail (version 1.2.0b3 or higher) and PEAR::Net_SMTP (version 1.3.3 or higher) packages to be able to retrieve the esmtp id and greeting details if you need them for your reports. Also, if you want to decode the body of the email and store that for your report you'll need to install the PEAR::Mail_mimeDecode package.
Provide the callback function like so:
<?php
$returned = $mail_queue->sendMailsInQueue(
MAILQUEUE_ALL,
MAILQUEUE_START,
MAILQUEUE_TRY,
'mailqueue_callback');
function mailqueue_callback($args) {
$row = get_mail_queue_row($args['id']);
$headers = unserialize($row['headers']);
$subject = $headers['Subject'];
$body = unserialize($row['body']);
$mail_headers = '';
foreach($headers as $key=>$value) {
$mail_headers .= "$key:$value\n";
}
$mail = $mail_headers . "\n" . $body;
$decoder = new Mail_mimeDecode($mail);
$decoded = $decoder->decode(array(
'include_bodies' => TRUE,
'decode_bodies' => TRUE,
'decode_headers' => TRUE,
));
$body = $decoded->body;
$esmtp_id = $args['queued_as'];
if (isset($args['greeting'])) {
$greeting = $args['greeting'];
$greets = explode(' ', $greeting);
$server = $greets[0];
} else {
$server = 'localhost';
}
insert_to_log(compact('server', 'esmtp_id', 'subject', 'body'));
}
?>
mixed constructor Mail_Queue::Mail_Queue (
array $container_options
, array $mail_options
)
Creates a new mail queue object to store mails in, fetch mails from and send mails with.
$container_options
Array of container (mail storage) options. See tutorial for details.
$mail_options
Array of mailer options. See tutorial for details.
<?php
require_once 'Mail/Queue.php';
$container_options = array(
'type' => 'db',
'database' => 'dbname',
'phptype' => 'mysql',
'username' => 'root',
'password' => '',
'mail_table' => 'mail_queue',
);
//optionally, a 'dns' string can be provided instead of db parameters.
//look at DB::connect() method or at DB or MDB docs for details.
//you could also use mdb container instead db
$mail_options = array(
'driver' => 'smtp',
'host' => 'your_smtp_server.com',
'port' => 25,
'auth' => false,
'username' => '',
'password' => '',
);
$mail_queue =& new Mail_Queue($container_options, $mail_options);
* *****************************************************************
* // TO ADD AN EMAIL TO THE QUEUE
* *****************************************************************
$from = 'user@server.com';
$from_name = 'admin';
$recipient = 'recipient@other_server.com';
$recipient_name = 'recipient';
$message = 'Test message';
$from_params = empty($from_name) ? '<'.$from.'>' : '"'.$from_name.'" <'.$from.'>';
$recipient_params = empty($recipient_name) ? '<'.$recipient.'>' : '"'.$recipient_name.'" <'.$recipient.'>';
$hdrs = array(
'From' => $from_params,
'To' => $recipient_params,
'Subject' => 'test message body',
);
$mime =& new Mail_mime();
$mime->setTXTBody($message);
$body = $mime->get();
$hdrs = $mime->headers($hdrs);
// Put message to queue
$mail_queue->put($from, $recipient, $hdrs, $body);
// Also you could put this msg in more advanced mode
$seconds_to_send = 3600;
$delete_after_send = false;
$id_user = 7;
$mail_queue->put( $from, $recipient, $hdrs, $body, $seconds_to_send, $delete_after_send, $id_user );
// TO SEND EMAILS IN THE QUEUE
// How many mails could we send each time
$max_ammount_mails = 50;
$mail_queue =& new Mail_Queue($container_options, $mail_options);
$mail_queue->sendMailsInQueue($max_ammount_mails);
?>
returnsTrue on success else pear error class.
throws no exceptions thrown
This function can not be called statically.
boolean Mail_Queue::deleteMail (
integer $id
)
Deletes a mail from the queue by identifier.
$id
Mail identifier.
throws no exceptions thrown
This function can not be called statically.
void Mail_Queue::factorySendMail (
)
Creates the necessary Mail object.
throws no exceptions thrown
This function can not be called statically.
object Mail_Queue_Container::get (
)
Get next mail from queue. The emails are preloaded into an internal memory buffer to speed up the process. Since Mail_Queue v.1.1, the preload() method doesn't preload ALL the mails in memory, but just a few of them each time. When the buffer is empty, it is filled again automatically. You can set the size of the buffer via the setBufferSize() method.
returnsMail_Queue_Body object
throws no exceptions thrown
This function can not be called statically.
int Mail_Queue::put (
string $from
, string $to
, array $hdrs
, array $body
, integer $sec_to_send = 0
, bool $delete_after_send
= true
, integer $id_user = MAILQUEUE_SYSTEM
)
Injects a new mail into the mail queue.
$from
Sender e-mail address.
$to
Recipient e-mail address.
$hdrs
Array of mail headers as returned by Mail_Mime::headers().
$body
Mail body array as returned by Mail_Mime::get().
$sec_to_send
Optional - Seconds to pass before delivery is attempted.
$delete_after_send
Optional - Whether or not to delete the mail after delivery.
$id_user
Optional - user ID. Stored in queue and readable from mail body object with getIdUser() at a later point.
returns ID of the record where this mail has been put or Mail_Queue_Error on error
throws no exceptions thrown
This function can not be called statically.
mixed Mail_Queue::sendMail (
object MailBody $mail
)
Sends mail object returned by Mail_Queue::get().
$mail
Mail object.
returns True on success else pear error class
throws no exceptions thrown
This function can not be called statically.
bool Mail_Queue::sendMailById (
integer $id
)
Sends a single mail from the queue specified by $id. Note: The queue entry will not be deleted automatically after delivery!
$id
Mail identifier
returns True on success else false
throws no exceptions thrown
This function can not be called statically.
mixed Mail_Queue::sendMailsInQueue (
integer $limit = MAILQUEUE_ALL
, integer $offset = MAILQUEUE_START
, integer $try = MAILQUEUE_TRY
, string or array $callback = null
)
Sends mails remaining in queue.
$limit
Optional - maximum number of mails to send.
$offset
Optional - skip $offset mails and start sending from there.
$try
Optional - count of tries before mail returns to queue for later delivery.
$callback
Optional - details of callback function to be used after mail has been sent.
returnsTrue on success else Mail_Queue_Error object.
throws no exceptions thrown
This function can not be called statically.
Provides Packages for mathematical purposes
Originally this class was part of NumPHP (Numeric PHP package)
Math_Complex
object Math_Complex
constructor
Math_Complex::Math_Complex
(
float
$real
,
float
$im
)
This package is not documented yet.
$real
Real part of the number
$im
Imaginary part of the number
No exceptions thrown.
This function can not be called statically.
float
Math_Complex::abs
(
)
This package is not documented yet.
No exceptions thrown.
This function can not be called statically.
float
Math_Complex::abs2
(
)
This package is not documented yet.
No exceptions thrown.
This function can not be called statically.
mixed
Math_Complex::angle
(
)
This package is not documented yet.
returns A float on success, a PEAR_Error otherwise
No exceptions thrown.
This function can not be called statically.
float|PEAR_Error
Math_Complex::arg
(
)
This package is not documented yet.
returns A floating point number on success, a PEAR_Error otherwise
No exceptions thrown.
This function can not be called statically.
float
Math_Complex::getIm
(
)
This package is not documented yet.
No exceptions thrown.
This function can not be called statically.
float
Math_Complex::getReal
(
)
This package is not documented yet.
No exceptions thrown.
This function can not be called statically.
float
Math_Complex::norm
(
)
This package is not documented yet.
No exceptions thrown.
This function can not be called statically.
string
Math_Complex::toString
(
)
This package is not documented yet.
No exceptions thrown.
This function can not be called statically.
Originally this class was part of NumPHP (Numeric PHP package)
Math_ComplexOp
Math_Complex
|PEAR_Error
&
Math_ComplexOp::acos
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::acosh
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::acot
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::acoth
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::acsc
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::acsch
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::add
(
Math_Complex
&$c1
,
Math_Complex
&$c2
)
This package is not documented yet.
&$c1
&$c2
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
boolean|PEAR_Error&
Math_ComplexOp::areEqual
(
Math_Complex
&$c1
,
Math_Complex
&$c2
)
This package is not documented yet.
&$c1
&$c2
returns True if $c1 == $c2, False if $c1 != $c2, PEAR_Error object on error
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::asec
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::asech
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::asin
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::asinAlt
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::asinh
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
&
Math_ComplexOp::asinReal
(
float
$r
)
This package is not documented yet.
$r
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::atan
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::atanh
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::conjugate
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::cos
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::cosh
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::cot
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::coth
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
&
Math_ComplexOp::createFromPolar
(
float
$r
,
float
$theta
)
This package is not documented yet.
$r
$theta
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::csc
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::csch
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::div
(
Math_Complex
&$c1
,
Math_Complex
&$c2
)
This package is not documented yet.
&$c1
&$c2
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::exp
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::inverse
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
boolean
Math_ComplexOp::isComplex
(
mixed
&$c1
)
This package is not documented yet.
&$c1
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::log
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::log10
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::logBase
(
Math_Complex
&$c1
,
Math_Complex
&$c2
)
This package is not documented yet.
&$c1
&$c2
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::mult
(
Math_Complex
&$c1
,
Math_Complex
&$c2
)
This package is not documented yet.
&$c1
&$c2
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::multIm
(
Math_Complex
$c1
,
float
$im
)
This package is not documented yet.
$c1
$im
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::multReal
(
Math_Complex
&$c1
,
float
$real
)
This package is not documented yet.
&$c1
$real
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::negative
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::pow
(
Math_Complex
&$c1
,
Math_Complex
&$c2
)
This package is not documented yet.
&$c1
&$c2
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::powReal
(
Math_Complex
$c1
,
float
$real
)
This package is not documented yet.
$c1
$real
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::sec
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::sech
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::sin
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::sinh
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::sqrt
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
&
Math_ComplexOp::sqrtReal
(
float
$realnum
)
This package is not documented yet.
$realnum
A float
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::sub
(
Math_Complex
&$c1
,
Math_Complex
&$c2
)
This package is not documented yet.
&$c1
&$c2
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::tan
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
Math_Complex
|PEAR_Error
&
Math_ComplexOp::tanh
(
Math_Complex
&$c1
)
This package is not documented yet.
&$c1
returns A valid Math_Complex number on success, PEAR_Error otherwise
No exceptions thrown.
This function can be called statically.
mixed Math_TrigOp::acoth (
float $x
)
This package is not documented yet.
$x
return A floating point on success, PEAR_Error object otherwise.
throws no exceptions thrown
This function can not be called statically.
mixed Math_TrigOp::acsch (
float $x
)
This package is not documented yet.
$x
return A floating point on success, PEAR_Error object otherwise
throws no exceptions thrown
This function can not be called statically.
mixed Math_TrigOp::asech (
float $x
)
This package is not documented yet.
$x
return A floating point on success, PEAR_Error object otherwise
throws no exceptions thrown
This function can not be called statically.
mixed Math_TrigOp::cot (
float $x
)
This package is not documented yet.
$x
return A floating point on success, PEAR_Error object otherwise
throws no exceptions thrown
This function can not be called statically.
mixed Math_TrigOp::coth (
float $x
)
This package is not documented yet.
$x
return A floating point on success, PEAR_Error object otherwise
throws no exceptions thrown
This function can not be called statically.
mixed Math_TrigOp::csc (
float $x
)
This package is not documented yet.
$x
return A floating point on success, PEAR_Error object otherwise
throws no exceptions thrown
This function can not be called statically.
mixed Math_TrigOp::csch (
float $x
)
This package is not documented yet.
$x
return A floating point on success, PEAR_Error object otherwise
throws no exceptions thrown
This function can not be called statically.
mixed Math_TrigOp::sec (
float $x
)
This package is not documented yet.
$x
return A floating point on success, PEAR_Error object otherwise
throws no exceptions thrown
This function can not be called statically.
mixed Math_TrigOp::sech (
float $x
)
This package is not documented yet.
$x
return A floating point on success, PEAR_Error object otherwise
throws no exceptions thrown
This function can not be called statically.
Static class implementing supplementary trigonometric functions
Class Trees for Math_TrigOp
Math_TrigOp
Provides Packages for network-related working
Package to check the correct syntax of an IPv4 address.
boolean Net_CheckIP::check_ip (
string $ip
)
This function can validate if a given string has a valid IPv4 syntax.
string $ip
- the IP address to check
boolean
- TRUE, if syntax is valid
This function can be called statically.
Using check_ip()
<?php
require_once "Net_CheckIP/CheckIP.php";
if (Net_CheckIP::check_ip("your_ip_goes_here")) {
// Syntax of the IP is ok
}
?>
A package to check the correct syntax of an IPv4 address, to determine whether the IP is from a private/reserved or the zeroconf pool, and the IP's class network (A, B and C are currently supported).
boolean Net_CheckIP2::isValid (
string $ip
)
This function can validate if a given string has a valid IPv4 syntax.
string $ip
- the IP address to check
boolean
- TRUE, if syntax is valid
This function can be called statically.
Using isValid()
<?php
require_once 'Net_CheckIP2/CheckIP.php';
if (Net_CheckIP::isValid("your_ip_goes_here")) {
// Syntax of the IP is ok
}
?>
Net_CheckIP2 is meant to replace the Net_CheckIP package with a couple small changes:
Old
<?php
require_once 'Net/CheckIP.php';
$ip = '127.0.0.1';
if (Net_CheckIP::check_ip($ip) {
echo 'This is a valid IPv4 address!';
}
?>
New
<?php
require_once 'Net/CheckIP2.php';
$ip = '127.0.0.1';
if (Net_CheckIP2::isValid($ip) {
echo 'This is a valid IPv4 address!';
}
?>
Net_CheckIP2 has a getClass method which helps the developer to determine if the IP is within an class A, B or C network.
In case getClass is unable to determine the class, or the IP is invalid, false is returned.
getClass
<?php
require_once 'Net/CheckIP.php';
$ip = '127.0.0.1';
$classNetwork = Net_CheckIP::getClass($ip);
if ($classNetwork === false) {
die('Unable to determine the class. The IP might be invalid also.');
}
switch ($classNetwork) {
case Net_CheckIP2::CLASS_A:
echo 'Class A network.';
break;
case Net_CheckIP2::CLASS_B:
echo 'Class B network.';
break;
case Net_CheckIP2::CLASS_C:
echo 'Class C network.';
break;
}
?>
Along with isValid()(), Net_CheckIP2 offers a couple methods to determine the IP's whereabouts. Currently implemented are methods to check if an IP is from a reserved IP space or the zeroconf pool.
Checks if the IP address is reserved (according to RFC1918).
isReserved()
<?php
require_once 'Net/CheckIP.php';
$ip = '127.0.0.1';
var_dump(Net_CheckIP::isReserved($ip));
?>
Zeroconf - automatically created usable IPs without manual intervention or management/configuration servers. This includes IPs from 169.254.0.0 to 169.254.255.255. (RFC3330)
isZeroConf()
<?php
require_once 'Net/CheckIP.php';
$ip = '127.0.0.1';
var_dump(Net_CheckIP::isZeroconf($ip));
?>
Provides methods for parsing and manipulating DNS packets as returned from DNS servers and provides communication methods for sending and receiving DNS packets.
The Net_DNS class provides DNS packet manipulation methods and the ability to send and receive packets to/from nameservers. Any type of DNS packet can be created, including RFC 2136 style DNS UPDATE packets.
The current list of supported RR types are:
This class includes sub-classses that provide methods for creating, parsing, sending, and receving DNS nameserver packets.
This class is only a container class and generally should never be constructed. It contains constants defined in RFC 1035 with methods for converting between the integer and the name values of such constants.
For information on sending a basic query to a nameserver for a specific record, see Net_DNS_Resolver::query().
The resolver class a high level interface for communicating with DNS servers. It uses the UNIX system stub resolver configuration files (if available) to configure the resolver. The resolver can also be configured manually by overriding the system configuration. This is done by setting the appropriate values in the Net_DNS_Resolver object. See Net_DNS_Resolver::query() for more information on the resolver configuration.
Net_DNS_Packet Net_DNS_Resolver::query (
string $hostname
, string $type = 'A'
, string $class = 'IN'
)
hostname
- The name to lookup (eg. www.php.net)
type
- The record type to query
class
- The zone class to query
Constructs a DNS query packet and forwards the query to a nameserver configured in the system stub resolver (ie. /etc/resolv.conf). When a response is received from the DNS server, a fully populated Net_DNS_Packet object is returned that represents the response.
The resolver object contains many properties that control that behaviour of the resolver. Some of these settings are automatically read from the system resolver configuration if available. On Linux/UNIX based systems, this includes /etc/resolv.conf as well as various environment variables. This configuration is done at the time of object instantiation and can be overridden by setting the appropriate object properties.
This function will only return a Net_DNS_Packet if the ANSWER section contains resource records. Specifically, if the ANCOUNT variable in the DNS packet header is 0, query() will return 0 (note: 0, not FALSE). If you are expecting a packet without resource records in the ANSWER section, use Net_DNS_Resolver::rawQuery(). This is useful when doing manual recursion.
For a description of the returned RR data object, see Net_DNS_RR.
Resolver Configuration Object Properties:
array $nameservers
An array of nameserver IP addresses that should be queried.
int $port
The port on which nameservers should be queried. The default is 53.
array $domain
The domain in which the resolver client host resides.
array $searchlist
An array of strings containingg domains to apply to unqualified hosts passed to the resolver.
int $retry
The number of seconds between retransmission of unaswered queries
int $retrans
The number of times unanswered requests should be retried
int $recurse
Sets the value of the RD (recursion desired) bit in the header. If the RD bit is set to 0, the server will not perform recursion on the request.
int $usevc
Whether or not to use TCP (Virtual Circuits) instead of UDP If set to 0, UDP will be used unless TCP is required. TCP is required for questions or responses greater than 512 bytes.
int $debug
If set to TRUE (non-zero), debugging code will be displayed as the resolver makes the request.
Environment Variables:
RES_NAMESERVERS
Space separated list of nameserver IP addresses to query
RES_SEARCHLIST
Space separated list of domain names to add to unqualified search requests.
LOCALDOMAIN
The name of the domain
RES_OPTIONS
A space separated list of options formatted as:
optionname:value
If the value is ommited, the value defaults to 1 (true). [optionname] corresponds to an object property.
Using Net_DNS_Resolver::query()
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$response = $resolver->query('example.com');
if ($response) {
foreach ($response->answer as $rr) {
$rr->display();
}
}
?>
Output:
example.com. 129808 IN A 192.0.34.166
The following example shows a DNS query for an MX record. Note that the IP address for the mail exchanger listed within the zone is returned with the response in the additional section. The second exchanger (that is not inside this zone) is not listed. To receive this address, you must perform another query specifically for the A record using the returned hostname.
Using Net_DNS_Resolver::query() to look up an MX record
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$response = $resolver->query('php.net', 'MX');
if ($response) {
foreach ($response->answer as $rr) {
$rr->display();
}
if (count($response->additional)) {
foreach ($response->additional as $rr) {
$rr->display();
}
}
}
?>
Output:
php.net. 86121 IN MX 15 smtp.osuosl.org.
php.net. 86121 IN MX 5 osu1.php.net.
osu1.php.net. 86121 IN A 140.211.166.39
The next example shows a more complex query with debugging information enabled. Note that the usevc option is set to TRUE. This forces the resolver to use TCP instead of UDP. This can be seen in the debug output on the send_tcp() line.
Using Net_DNS_Resolver::query() with specific nameservers and options
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$resolver->debug = 1; // Turn on debugging output to show the query
$resolver->usevc = 1; // Force the use of TCP instead of UDP
$resolver->nameservers = array( // Set the IP addresses
'198.41.0.4', // of the nameservers
'192.228.79.201' // to query.
);
$response = $resolver->query('example.com');
if (! $response) {
echo "\n";
echo "ANCOUNT is 0, therefore the query() 'failed'\n";
echo "See Net_DNS_Resolver::rawQuery() to receive this packet\n";
}
?>
Output:
;; query(example.com, A, IN)
;; send_tcp(198.41.0.4:53)
;; sending 29 bytes
;; received 517 bytes
;; HEADER SECTION
;; id = 58298
;; qr = 1 opcode = QUERY aa = 0 tc = 0 rd = 1
;; ra = 0 rcode = NOERROR
;; qdcount = 1 ancount = 0 nscount = 13 arcount = 15
;; QUESTION SECTION (1 record)
;;
;example.com. IN A
;; ANSWER SECTION (0 records)
;; AUTHORITY SECTION (13 records)
com. 172800 IN NS A.GTLD-SERVERS.NET.
com. 172800 IN NS G.GTLD-SERVERS.NET.
com. 172800 IN NS H.GTLD-SERVERS.NET.
com. 172800 IN NS C.GTLD-SERVERS.NET.
com. 172800 IN NS I.GTLD-SERVERS.NET.
com. 172800 IN NS B.GTLD-SERVERS.NET.
com. 172800 IN NS D.GTLD-SERVERS.NET.
com. 172800 IN NS L.GTLD-SERVERS.NET.
com. 172800 IN NS F.GTLD-SERVERS.NET.
com. 172800 IN NS J.GTLD-SERVERS.NET.
com. 172800 IN NS K.GTLD-SERVERS.NET.
com. 172800 IN NS E.GTLD-SERVERS.NET.
com. 172800 IN NS M.GTLD-SERVERS.NET.
;; ADDITIONAL SECTION (15 records)
A.GTLD-SERVERS.NET. 172800 IN AAAA 2001:503:a83e::2:30
A.GTLD-SERVERS.NET. 172800 IN A 192.5.6.30
G.GTLD-SERVERS.NET. 172800 IN A 192.42.93.30
H.GTLD-SERVERS.NET. 172800 IN A 192.54.112.30
C.GTLD-SERVERS.NET. 172800 IN A 192.26.92.30
I.GTLD-SERVERS.NET. 172800 IN A 192.43.172.30
B.GTLD-SERVERS.NET. 172800 IN AAAA 2001:503:231d::2:30
B.GTLD-SERVERS.NET. 172800 IN A 192.33.14.30
D.GTLD-SERVERS.NET. 172800 IN A 192.31.80.30
L.GTLD-SERVERS.NET. 172800 IN A 192.41.162.30
F.GTLD-SERVERS.NET. 172800 IN A 192.35.51.30
J.GTLD-SERVERS.NET. 172800 IN A 192.48.79.30
K.GTLD-SERVERS.NET. 172800 IN A 192.52.178.30
E.GTLD-SERVERS.NET. 172800 IN A 192.12.94.30
M.GTLD-SERVERS.NET. 172800 IN A 192.55.83.30
ANCOUNT is 0, therefore the query() 'failed'
See Net_DNS_Resolver::rawQuery() to receive this packet
This function can not be called statically.
Net_DNS_Packet Net_DNS_Resolver::rawQuery (
string $hostname
, string $type = 'A'
, string $class = 'IN'
)
hostname
- The name to lookup (eg. www.php.net)
type
- The record type to query
class
- The zone class to query
The Net_DNS_Resolver::rawQuery() function performs a DNS query similar to the Net_DNS_Resolver::query() function; however, rawQuery() will return any response from the nameserver. This is useful when the response packet may or may not contain any resource records in the "ANSWER" section.
rawQuery() uses the same resolver configuration used by Net_DNS_Resolver::query().
For a description of the returned RR data object, see Net_DNS_RR.
Using Net_DNS_Resolver::rawQuery()
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$response = $resolver->rawQuery('example.com');
if ($response) {
if (count($response->answer)) {
foreach ($response->answer as $rr) {
$rr->display();
}
}
}
?>
Output:
example.com. 129808 IN A 192.0.34.166
This function can not be called statically.
Net_DNS_Packet Net_DNS_Resolver::axfr (
string $dname
, string $class = 'IN'
, boolean $old = FALSE
)
dname
- The domain name (zone name) to transfer
class
- The zone class to transfer
old
- Only used for backwards compatibility with previous version of Net_DNS
axfr() attempts a zone transfer from the nameservers specified in the Net_DNS_Resolver->nameservers array. Net_DNS_Resolver::axfr() uses the same resolver configuration as the Net_DNS_Resolver::query() method.
Most public nameservers will not allow a zone transfer by default. A zone transfer will provide a full list of DNS resource records inside of a zone file. A zone transfer will always use TCP instead of UDP queries.
For a description of the returned RR data object, see Net_DNS_RR.
Failed Net_DNS_Resolver::axfr() query
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$resolver->debug = 1;
$response = $resolver->axfr('example.com');
print_r($response);
if (count($response) == 0) {
echo "\n";
echo "AXFR Failed\n";
}
?>
Output:
;; axfr_start(example.com, IN)
;; query(example.com, AXFR, IN)
;; axfr_start(192.168.0.254:53)
;; sending 29 bytes
;; read_tcp: expecting 2 bytes
;; read_tcp: received 2 bytes
;; read_tcp: expecting 29 bytes
;; read_tcp: received 29 bytes
;; received 29bytes
;; HEADER SECTION
;; id = 29190
;; qr = 1 opcode = QUERY aa = 0 tc = 0 rd = 1
;; ra = 1 rcode = NOTAUTH
;; qdcount = 1 ancount = 0 nscount = 0 arcount = 0
;; QUESTION SECTION (1 record)
;;
;example.com. IN AXFR
;; ANSWER SECTION (0 records)
;; AUTHORITY SECTION (0 records)
;; ADDITIONAL SECTION (0 records)
Array
(
)
AXFR Failed
In the following example, debugging has been turned on and the nameserver has been configured to allow zone transfers. The most important item of note in this example is the SOA record at the beginning and end of the returned records. When a name server sends a zone transfer, the first record sent is the SOA record. The zone transfer is considered complete when the name server sends the SOA record again. This behaviour can be seen in the debug output. The resulting array returned by axfr() does not return the final SOA record.
Succesful Net_DNS_Resolver::axfr() query
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
$resolver->debug = 1;
$response = $resolver->axfr('my.example.com');
echo "\n\nThe following resource records were returned from the nameserver:\n";
if (count($response)) {
foreach ($response as $rr) {
$rr->display();
}
}
?>
Output:
;; axfr_start(my.example.com, IN)
;; query(my.example.com, AXFR, IN)
;; axfr_start(192.168.0.254:53)
;; sending 32 bytes
;; read_tcp: expecting 2 bytes
;; read_tcp: received 2 bytes
;; read_tcp: expecting 262 bytes
;; read_tcp: received 262 bytes
;; received 262bytes
;; HEADER SECTION
;; id = 21220
;; qr = 1 opcode = QUERY aa = 1 tc = 0 rd = 0
;; ra = 1 rcode = NOERROR
;; qdcount = 1 ancount = 10 nscount = 0 arcount = 0
;; QUESTION SECTION (1 record)
;;
;my.example.com. IN AXFR
;; ANSWER SECTION (10 records)
my.example.com. 300 IN SOA ns1.my.example.com. hostmaster.my.example.com. 103 3600 1800 2592000 300
my.example.com. 300 IN NS ns1.my.example.com.
my.example.com. 300 IN MX 10 mx1.my.example.com.
my.example.com. 300 IN MX 20 mx2.my.example.com.
my.example.com. 300 IN A 192.168.0.1
mx1.my.example.com. 300 IN A 192.168.0.2
mx2.my.example.com. 300 IN A 192.168.0.3
server.my.example.com. 300 IN CNAME www.my.example.com.
www.my.example.com. 300 IN A 192.168.0.1
my.example.com. 300 IN SOA ns1.my.example.com. hostmaster.my.example.com. 103 3600 1800 2592000 300
;; AUTHORITY SECTION (0 records)
;; ADDITIONAL SECTION (0 records)
The following resource records were returned from the nameserver:
my.example.com. 300 IN SOA ns1.my.example.com. hostmaster.my.example.com. 103 3600 1800 2592000 300
my.example.com. 300 IN NS ns1.my.example.com.
my.example.com. 300 IN MX 10 mx1.my.example.com.
my.example.com. 300 IN MX 20 mx2.my.example.com.
my.example.com. 300 IN A 192.168.0.1
mx1.my.example.com. 300 IN A 192.168.0.2
mx2.my.example.com. 300 IN A 192.168.0.3
server.my.example.com. 300 IN CNAME www.my.example.com.
www.my.example.com. 300 IN A 192.168.0.1
This function can not be called statically.
The Net_DNS_Packet class provides methods for creating a DNS packet suitable for sending to a nameserver. It also provides the methods of parsing a response from a nameserver into a Net_DNS_Packet object.
A standard DNS packet is made up of five main pieces:
Header
Question Section
Answer Section
Authority Section
Additional Section
Net_DNS defines the DNS packet header as a Net_DNS_Header object. Net_DNS_Question defines the question section of a packet object. The remaining three sections should be arrays of Net_DNS_RR objects.
The Net_DNS_RR class provides methods of parsing resource records returned by a nameserver, creation of resource record objects to be sent to a nameserver, and the ability to access each component of an RR as an object property.
Each resource record object MUST contain the following properties:
name
- The DNS name of the RR
type
- The RR type
class
- The RR class (normally IN)
ttl
- The RR time to live
rdlength
- The amount of data (in bytes) of the rdata section
rdata
- The data (uncompressed) of the right hand side of the RR
If type
is a supported RR type,
it will automatically be decompressed and/or decoded
into its appropriate property values. The values inside
of an RR object will vary based on the type of RR.
Supported RR types:
A
string address
- IPv4 style address
AAAA
string address
- IPv6 style address
CNAME
string cname
- The canonical name of the queried host
HINFO
string cpu
- The host CPU type
string os
- Type host operating system
MX
integer preference
- The MX preference (lower takes priority
string exchange
- The name of the mail exchange host
NAPTR
unknown order
- unknown
unknown preference
- unknown
unknown flags
- unknown
unknown services
- unknown
unknown regex
- unknown
unknown replacement
- unknown
NS
string nsdname
- The name of the NS record nameserver
PTR
string ptrdname
- The name for the queried IP address
SOA
string mname
- Master nameserver hostname as
specified in the SOA record - not neccessarily accurate
string rname
- Email address of the person
responsible for the zone - not neccessarily accurate
string serial
- The serial number (version
number) of the retrieved zone
string refresh
- The length of time before
the zone should be refresed
string retry
- The length of time between
retries for slave servers to refresh the zone
string expire
- The length of time before
slave servers should consider the data invalid without refreshing
the zone data.
string minimum
- The default TTL for RRs
inside of the zone that are not otherwise specified.
SRV
unknown preference
- unknown
unknown weight
- unknown
unknown port
- unknown
unknown target
- unknown
TSIG
integer time_signed
- The time the signature
has was created
integer fudge
- The time offset that is
acceptable between the client and the server
integer mac_size
- The size of the following
signature data
string/binary mac
- The data containing the
has sent by the client/server
integer original_id
- The ID sent to or
received from the server identifying this query.
integer error
- The value of the error
calculating or verifying the signature.
integer other_len
- The amount of data (in
bytes) additional to the signature
string other_data
- Additional data required
by the nameserver for the signature.
string key
- The key shared by the client and
the server to validate authorization.
TXT
string text
- The text defined in the record
By constructing a specially formatted DNS packet and sending it to a nameserver, a dynamic DNS update can be performed very easily. RFC 2136 defines some variations of a DNS packet that are used by Net_DNS to request an update. The nameserver must first be configured to accept DNS updates, a key must be established between the client and server, and most importantly, the PHP mhash extension must be included for processing the MD5 digest of the key.
The format of the DNS packet is very specific and requires in-depth knowledge of how DNS updates work. Net_DNS currently does not support any abstraction of these specially formatted packets. Packets must currently be crafted and sent manually. The full formatting of update packets can be found in RFC 2136.
For a complete listing of all of the fields that are available for DNS updates, please review RFC2136 (http://www.ietf.org/rfc/rfc2136.txt). The following examples show the simplest forms of a DNS update.
Sending a dynamic DNS update packet to a nameserver
<?php
require_once 'Net/DNS.php';
$resolver = new Net_DNS_Resolver();
// We should only send the request to the master server
// accepting DNS updates for the zone.
$resolver->nameservers = array('192.168.0.254');
// We must manually construct the DNS packet for a DNS update
// First we must instantiate the packet object
$packet = new Net_DNS_Packet();
// Create the header for the update packet. Most of the defaults are
// acceptable, but we must set the header OPCODE to "UPDATE"
$packet->header = new Net_DNS_Header();
$packet->header->id = $resolver->nextid();
$packet->header->qr = 0;
$packet->header->opcode = "UPDATE";
// As specified in RFC2136, the question section becomes the "ZONE" section.
// This specifies the zone for which we are requesting a change. This
// reflects the zone configuration as specified in the nameserver
// configuration.
$packet->question[0] = new Net_DNS_Question('example.com', "SOA", "IN");
// The "ANSWER" section of the packet becomes the "PREREQUISITE" section of
// the packet. Section 2.4 of RFC2136 defines the possible values for this
// section. As show below, without any prerequisites the nameserver will
// attempt to perform the update unconditionally.
$packet->answer = array();
// The AUTHORITY section becomes the "UPDATE" section of the DNS packet. The
// UPDATE section is a collection of resource record updates that should be
// modified in one form or another. RRs can be deleted or added based on the
// values passed in the RR object. These values are specified in RFC2136 but
// are summarized here:
//
// Adding an RR
// A complete RR object with a non-zero TTL is considered an addition.
//
// Deleting an RR
// A complete RR object with a zero TTL is considered an deletion. An RR that
// matches (exactly) with all values except for the TTL will be removed.
//
// Deleting an RRset
// A complete RR object with a zero TTL and a type of ANY is considered a
// deletion of all RRs with the specified name and type. An RR that matches
// (exactly) with all values except for the TTL and the TYPE will be removed.
//
// Deleting all RRsets for a name
// A complete RR object with a zero TTL, a type of ANY, and a class of ANY is
// considered a deletion of all RRs with the specified name. Any RR that
// matches the name section of the query will be removed.
//
// The following specification will delete the RR that has a name of
// "example.com", class of "IN", type of "A", and an address of
// "192.0.34.166".
$rrDelete =& Net_DNS_RR::factory("example.com. 0 IN A 192.0.34.166");
//
// The following specification will add an RR that has a name of example.com,
// a TTL of 1 hour, a class of "IN", type of "A", and an address of
// "192.0.34.155"). Note that the only difference between this RR and the
// previous RR is the value of the TTL.
$rrAdd =& Net_DNS_RR::factory("example.com. 3600 IN A 192.0.34.166");
//
// The RR modifications are added to the authority (UPDATE) section of the DNS
// packet.
$packet->authority[0] = $rrDelete;
$packet->authority[1] = $rrAdd;
//
// The signature must be present in any packet sent to a nameserver that
// requires authentication. The TSIG RR is added to the additional section of
// the DNS packet.
$tsig =& Net_DNS_RR::factory("keyname.as.specified.in.server. TSIG ThisIsMyKey");
$packet->additional = array($tsig);
// Net_DNS does not automatically calculate the number of records stored in
// each section. This calculation must be done manually.
$packet->header->qdcount = count($packet->question);
$packet->header->ancount = count($packet->answer);
$packet->header->nscount = count($packet->authority);
$packet->header->arcount = count($packet->additional);
//
// After creating your packet, you must send it to the name server for
// processing. DNS updates must use the send_tcp() method:
$response = $resolver->send_tcp($packet, $packet->data());
//
// The response from the server will vary. If the update was successfuly, the
// server will have a response code of "NOERROR". Any other error types will
// be reported in the response packet's header "rcode" variable.
if ($response->header->rcode != "NOERROR") {
return($response->header->rcode);
}
?>
Net_DNSBL checks if a given Host or URL is listed on an DNS-based Blackhole List (DNSBL, Real-time Blackhole List or RBL) or Spam URI Realtime Blocklist (SURBL)
PEAR::Net_DNSBL provides an easy way to check if a given Host or URL is listed on an DNS-based Blackhole List (DNSBL, Real-time Blackhole List or RBL) or Spam URI Realtime Blocklist (SURBL).
Please not that this currently only works with IPv4 IPs.
Let's start with a simple example to check if the remote host is listed in two blacklists:
<?php
require_once 'Net/DNSBL.php';
$dnsbl = new Net_DNSBL();
$remoteIP = $_SERVER['REMOTE_ADDR'];
$remoteIP = '81.70.69.193';
$dnsbl->setBlacklists(array('sbl-xbl.spamhaus.org', 'bl.spamcop.net'));
if ($dnsbl->isListed($remoteIP)) {
// do something
}
?>
This example shows how to check if an URL is listed in a SURBL:
<?php
require_once 'Net/DNSBL/SURBL.php';
$surbl = new Net_DNSBL_SURBL();
if ($surbl->isListed('http://test.multi.surbl.org/')) {
// do something
}
?>
An example how to extract URLS from a text can be found on http://www.phpfreaks.com/quickcode/Extract_All_URLs_on_a_Page/15.php and be used this way:
<?php
foreach ($extracted_urls as $surbl_check_url) {
if ($surbl->isListed($surbl_check_url)) {
// do something
break;
}
}
?>
Let's get some details, if our host is listed:
<?php
require_once 'Net/DNSBL.php';
$dnsbl = new Net_DNSBL();
$dnsbl->setBlacklists(array('sbl-xbl.spamhaus.org', 'bl.spamcop.net'));
if ($dnsbl->isListed($_SERVER['REMOTE_ADDR'])) {
var_dump($dnsbl->getDetails($_SERVER['REMOTE_ADDR']));
var_dump($dnsbl->getTxt($_SERVER['REMOTE_ADDR']));
var_dump($dnsbl->getListingBl($_SERVER['REMOTE_ADDR']));
var_dump($dnsbl->getListingRecord($_SERVER['REMOTE_ADDR']));
}
?>
Finger is a service providing information about a user of a server. The finger-protocol is deactivated on the most Internet servers due to security reasons. So it makes especially sense for intranets and local host administration.
The output of the finger command depends on the system and less standarized, because the output should be easy to read for humans and not for machines.
string
Net_Finger::query
(
string $server
,
string $query
)
Execute a finger query on a server
string $server
- The name of the server or the IP-adress
string $query
- The finger object to look up
string - the data from the finger request.
The returned PEAR_Error object in case of an error is unspecific, so you can ignore the error code and/or message. The reason for a failure could be a failed connection to the server or the server did not run a finger service.
You will not get a PEAR_Error, if the query fails due to a not existing finger object. This can be only done by checking the data returned by query().
This function can be called statically.
Using query()
<?php
$server = "localhost" ; // or IP adress to use
$query = "ralph" ; // Username to look up
$data = Net_Finger::query( $server, $query) ;
echo $data ;
?>
Net_FTP provides comfortable communication with FTP-Servers. It mainly provides an OO wrapper to PHP's integrated FTP functions, adding some missing features like recursive up- and downloading of complete folders, as well as deleting. The options for generating directory listings were also extended to feature well structured listing of directories and/or files.
Makes Net_FTP::ls() return a structures array of files (no directories) in a directory.
Makes Net_FTP::ls() return a structures array of directories (no files) in a directory.
Makes Net_FTP::ls() return a structures array of directories and files in a directory.
Makes Net_FTP::ls() return an unstructred array as returned by the PHP function ftp_raw_list().
object Net_FTP::Net_FTP (
string $host = null
,
int $port = null
)
Create a new object for communication with FTP servers.
string $host = null
- The host to connect to (
either an IP address or a domain name).
int $port = null
- The port to connect to on the
server.
object
- the new Net_FTP object.
This function can not be called statically.
Using Net_FTP()
<?php
require_once 'Net/FTP.php';
$test = new Net_FTP('ftp.mydomain.com', 21);
?>
mixed Net_FTP::connect (
string $host = null
,
int $port = null
)
Create a new object for communication with FTP servers.
string $host = null
- The host to connect to (
either an IP address or a domain name). This parameter can be left out, if it has been set
in the constructor or manually!
int $port = null
- The port to connect to on the
server. This parameter can be left out, if it has been set in the constructor or manually!
mixed
- true on success, otherwise PEAR::Error.
The returned PEAR_Error object in case of an error is unspecific. You can ignore the errornumber and errormessage, because only "Connection failed" will be returned if the connection fails.
This function can not be called statically.
Using connect()
<?php
$test->connect('192.168.0.1', 21);
?>
void Net_FTP::disconnect (
)
Disconnect from the FTP server you're connected to.
void
This function can not be called statically.
Using disconnect()
<?php
$test->disconnect();
?>
mixed Net_FTP::login (
string $username = null
,
int $password = null
)
Does the login on the FTP server you are connected to. for that you first have to connect to a FTP server before logging in. Username and Password can either be set by the parameters or manually before (using the set-methods).
string $username = null
- The username
to be used for login to. This parameter can be left out, if it has been set manually!
int $password = null
- The password to use for
login. This parameter can be left out, if it has been set manually!
mixed
- true on success, otherwise PEAR::Error.
The returned PEAR_Error object in case of an error is unspecific. You can ignore the errornumber and errormessage, because only "Login failed" will be returned if the login fails.
This function can not be called statically.
Using login()
<?php
$test->login('myuser', 'mypass');
?>
mixed Net_FTP::cd (
string $directory
)
Changes the directory on the FTP server you are logged in. The parameter can either be an absolute path (e.g. '/home/mydir') or a relative one (e.g. 'mydir').
string $directory
- The directory you want to change to. This can
either be an absolute path (e.g. '/home/mydir') or a relative one (e.g. 'mydir').
mixed
- true on success, otherwise PEAR::Error.
The returned PEAR_Error object in case of an error is unspecific. You can ignore the errornumber and errormessage, because only "Directory change failed" will be returned if the operation fails.
This function can not be called statically.
Using cd()
<?php
$test->cd('../mydir');
?>
mixed Net_FTP::pwd (
)
Returns the directory currently selected on the server.
mixed
- path (absolute) as string on success, otherwise PEAR::Error.
The returned PEAR_Error object in case of an error is unspecific. You can ignore the errornumber and errormessage, because only "Could not determine the actual path" will be returned if the operation fails.
This function can not be called statically.
Using pwd()
<?php
echo $test->pwd();
?>
mixed Net_FTP::mkdir (
string $directory
,
bool $recursive = false
)
Creates a new directory on the FTP server. You can give this function either a relative or an absolute path as the first parameter. The second (optional) parameter lets you create directories recursively (meaning you can create './my/new/dir' even if '/my' doesn't exist. All 3 directories would be created ).
string $directory
- The directory you want to create.
This can either be an absolute path
(e.g. '/home/mydir')
or a relative one
(e.g. 'mydir').
mixed
- true on success, otherwise PEAR::Error.
The returned PEAR_Error object in case of an error is specific. There is only one errornumber which may occur. The message given by this error will contain the directory where the error occurred in "Creation of '$dir' failed". operation fails.
This function can not be called statically.
Using mkdir()
<?php
$test->mkdir('../mydir');
?>
mixed Net_FTP::execute (
string $command
)
Executes a given command on the server (the SITE EXEC command is added by the method).
string $command
- The command to be executed (without SITE EXEC).
mixed
- true on success, otherwise PEAR::Error.
The returned PEAR_Error object in case of an error is specific. There is only one errornumber which may occur. The message given by this error will contain the directory where the error occurred in "Execution of command '$command' failed". operation fails.
This function can not be called statically.
Using execute()
<?php
$test->execute($myCommand);
?>
mixed Net_FTP::mdtm (
string $file
,
string $format = null
)
gives you the last modification-date of a file either as a unix timestamp or in a formated date.
string $file
- The file to check.
string $format
- A date()-function styled format-string.
mixed
- the last modification date on success, otherwise PEAR::Error.
Several errors may be returned by mdtm. The errornumber is unspecific (until now) and will not tell you anything about the errormessage. Possible errors are:
Error message | Description | Solution |
---|---|---|
Filename '$file' seems to be a directory. | The filename you gave the method does not reference a regular-file but a directory. | Specify a correct filenamepath (eg. /my/file/path/foo.html, ../foo.html). |
Could not get last-modification-date of '$file'. | The last-modification date could not be determined by PHP. Reasons for this may be that your FTP-server does not support the used command or that you gave the function a non existent file as reference. |
|
Date-format failed on timestamp '$res'. | The given format-string was not well formated. | Check the documentation of the PHP function date(). |
This function can not be called statically.
Using mdtm()
<?php
var_dump($test->mdtm('/foo/bar'));
// returns the last modification time in german timeformat
var_dump($test->mdtm('/foo/bar', 'd.m.Y, H:i'));
?>
mixed Net_FTP::size (
string $file
)
gives you the size of a file bytes.
string $file
- The file to check.
mixed
- the size of the given file on success, otherwise PEAR::Error.
The returned PEAR_Error object in case of an error is unspecific. You can ignore the errornumber and errormessage, because only "Connection failed" will be returned if the connection fails.
This function can not be called statically.
Using size()
<?php
var_dump($test->size('/foo/bar'));
?>
mixed Net_FTP::ls (
string $dir = null
,
string $mode
= NET_FTP_DIRS_FILES
)
this function gives you a listing of either the files / directories / both or an unformated array (like the PHP function ftp_rawlist()).
string $dir = null
- The directory to list. You can
either use a relative or an absolute path. This optional parameter will be set to the
current path.
int $mode =
- A constant representing the nodes list
(directories, files, both or a ram directory listing).
This parameter is determined by the constants
(see: Constants).
This parameter is optional and will be set for
listing directories and files structured in an array.
mixed
- a directory listing in the form you determine on success,
otherwise PEAR::Error.
Several errors may be returned by ls. The errornumber is unspecific (until now) and will not tell you anything about the errormessage. Possible errors are:
Error message | Description | Solution |
---|---|---|
Raw directory-list in wrong format. | The format given by the server on PHP function ftp_rawlist()was wrong. Check if the directory you wanted to be listed is correct and you have access to listing it. | Specify a correct directory path (eg. /my/file/path/, ../) and check (maybe change) the rights on it. |
Could not get last-modification-date of '$file'. | The last-modification date could not be determined by PHP. Reasons for this might be that your FTP-server does not support the used command or that you gave the function a non existent file as reference. |
|
Date-format failed on timestamp '$res'. | The given format-string was not well formated. | Check the documentation of the PHP function date(). |
This function can not be called statically.
Using ls()
<?php
var_dump($test->ls('/foo/bar'));
?>
mixed Net_FTP::rm (
string $path = null
,
string $recursive = false
)
This method deletes a file or a directory.
string $path = null
- The file or directory to delete. In case you set
the second parameter to true, directories will be deleted even if not empty. All files and
directories inside will be deleted.
$recursive = false
-
This parameter determines, whether a directory is deleted only if it's empty (standard) or if
all included files and directories will be deleted with it ($recursive = true).
mixed
- true on success, otherwise PEAR::Error.
Several errors may be returned by rm. The errornumber is unspecific (until now) and will not tell you anything about the errormessage. Possible errors are:
Error message | Description | Solution |
---|---|---|
Could not delete file '$file'. | The file named $file (complete path) could not be deleted. | Either the file does not exist or you do not have the permission to delete it. So check if the file exists and you have permissions on it. |
Directory name '$dir' is invalid, has to end with '/' | You entered a directory for deletion ($recursive = true) without ending '/'. | Correct your parameter. |
Could not delete directory '$dir'. | The directory $dir could not be deleted. | Maybe the directory is not empty ($recursive = false) or you do not have the permission to delete the directory. |
This function can not be called statically.
Using rm()
<?php
var_dump($test->rm('/foo/bar/', true));
?>
mixed Net_FTP::get (
string $remote_file
,
string $local_file
,
bool $overwrite = false
,
int $mode = null
)
This downloads a file from the FTP server to the computer your script runs on.
string $remote_file
- The file you'd like to download. This could either
be an absolute or relative path to a file (not a directory! see:
Net_FTP::getRecursive()).
string $local_file
-
The destination you'd like to download the file to (including filename, not directory!). You
can specify this with either an absolute path or a path relative to the scripts directory.
(Beware: The script directory is determined by the called script, if you use includes!)
bool $overwrite = false
-
Whether to overwrite the local file if it exists, or not. if not set the file will not be
overwritten.
int $mode = null
-
This has to be one of the constants FTP_ASCII or
FTP_BINARY. if not specified, the class will
try to determine the mode from the file extension (from extensions.ini) or
fall back to the standard transfer mode (attribute).
mixed
- true on success, otherwise PEAR::Error.
Several errors may be returned by get. The errornumber is unspecific (until now) and will not tell you anything about the errormessage. Possible errors are:
Error message | Description | Solution |
---|---|---|
Local file '$local_file' exists and may not be overwriten. | The local file you specified exists and you did not specify to overwrite it. | Set parameter $overwrite = true. |
Local file '$file' is not writeable. Can not overwrite. | You specified to overwrite the localfile. This did not work. | Maybe you don't have the permission to overwrite the file. Check the filepermissions. |
File '$remote_file' could not be downloaded to '$local_file'. | The download of the remote file failed. | This may have several reasons: Maybe the remote file does not exist or the local directory you wanted to download to does not exist or is not writeable. |
This function can not be called statically.
Using get()
<?php
var_dump($test->get('foo/bar.zip', '/tmp/downloaded.zip', true, FTP_BINARY));
?>
mixed Net_FTP::put (
string $local_file
,
string $remote_file
,
bool $overwrite = false
,
int $mode = null
)
This uploads a file to the FTP server from the computer your script runs on.
string $local_file
- The source file you'd like to upload.
You can specify this with either an absolute path
or a path relative to the scripts directory.
(Beware: The script directory is determined by the called script,
if you use includes!)
string $remote_file
- The path (including filename) you'd like to upload to.
This could either be an absolute or relative path to a file
(not a directory! see:
Net_FTP::putRecursive()).
bool $overwrite = false
- Whether to overwrite the remote file, if it exists or not.
If not set the file will not be overwritten.
int $mode = null
- This has to be one of the constants
FTP_ASCII or
FTP_BINARY.
If not specified,
the class will try to determine the mode
from the file extension
(from extensions.ini)
or fall back to the standard transfer mode (attribute).
mixed
- true on success, otherwise PEAR::Error.
Several errors may be returned by put. The errornumber is unspecific (until now) and will not tell you anything about the errormessage. Possible errors are:
Error message | Description | Solution |
---|---|---|
Local file '$local_file' does not exist. | The local file you specified does not exist. | Correct the local file path. |
Remote file '$remote_file' exists and may not be overwriten. | The specified remote file exists but may not be overwritten. | Maybe you don't have the permission to overwrite the file. Check the filepermissions. |
File '$local_file' could not be uploaded to '$remote_file'. | The upload of the local file failed. | This may have several reasons: Maybe the local file does not exist or the remote directory you wanted to upload to does not exist or is not writeable. |
This function can not be called statically.
Using put()
<?php
var_dump($test->put('/tmp/downloaded.zip', 'foo/bar.zip', true, FTP_BINARY));
?>
mixed Net_FTP::getRecursive (
string $remote_path
,
string $local_path
,
bool $overwrite = false
,
int $mode = null
)
This downloads a whole directory from the FTP server to the computer your script runs on.
string $remote_path
- The directory you'd like to download.
This could either be an absolute or relative path to a directory (path has to end with '/').
string $local_path
-
The destination you'd like to download the directory to. You can specify this with
either an absolute path or a path relative to the scripts directory.
(Beware: The script directory is determined by the called script, if you use includes!)
bool $overwrite = false
-
Whether to overwrite the local files if they exist, or not. if not set the directory will not be
overwritten.
int $mode = null
-
This has to be one of the constants FTP_ASCII or
FTP_BINARY. if not specified, the class will
try to determine the mode from the file extensions (from extensions.ini) or
fall back to the standard transfer mode (attribute).
mixed
- true on success, otherwise PEAR::Error.
Several errors may be returned by getRecursive. The errornumber is unspecific (until now) and will not tell you anything about the errormessage. Possible errors are:
Error message | Description | Solution |
---|---|---|
Given remote-path '$remote_path' seems not to be a directory. | The path you specified on the FTP sever seems not to be a valid directory node. | Maybe your path does not end with '/' or the directory does not exist. |
Given local-path '$local_path' seems not to be a directory. | The path you specified on the local host seems not to be a valid directory node. | Maybe your path does not end with '/' or the directory does not exist. |
Could not create dir '$local_path'. | The given directory could not be created. | Check your permissions on the source-directory. |
Could not create dir '$local_path'. | The given directory could not be created. | Check your permissions on the source-directory. |
This function can not be called statically.
Using getRecursive()
<?php
var_dump($test->getRecursive('foo/', '/tmp/foo/', true));
?>
mixed Net_FTP::putrecursive (
string $local_path
,
string $remote_path
,
bool $overwrite = false
,
int $mode = null
)
This uploads a whole directory to the FTP server from the computer your script runs on.
string $local_path
- The source directory you'd like to upload.
You can specify this with either an absolute path
or a path relative to the scripts directory.
(Beware: The script directory is determined by the called script,
if you use includes!)
string $remote_path
- The path you'd like to upload to.
This could either be an absolute or relative path to a directory.
bool $overwrite = false
- Whether to overwrite the remote directory if it exists, or not.
If not set the directory will not be overwritten.
int $mode = null
-
This has to be one of the constants FTP_ASCII or
FTP_BINARY.
If not specified, the class will try to determine the mode
from the file extensions (from extensions.ini)
or fall back to the standard transfer mode (attribute).
mixed
- true on success, otherwise PEAR::Error.
Several errors may be returned by putRecursive. The errornumber is unspecific (until now) and will not tell you anything about the errormessage. Possible errors are:
Error message | Description | Solution |
---|---|---|
Given local-path '$local_path' seems not to be a directory. | The local path you have specified does not seem to be a directory. | Correct the local directory path. (Does it end with '/'?) |
Given remote-path '$remote_path' seems not to be a directory. | The remote path you have specified does not seem to be a directory. | Correct the local directory path. (Does it end with '/'?) |
This function can not be called statically.
Using putRecursive()
<?php
var_dump($test->putRecursive('/tmp/foo/', 'foo/', true));
?>
integer Net_FTP::checkfileextension (
string $filename
)
This method checks a given filename for its proper transfermode (using extensions.ini). If the file extension can not be found, the class falls back to the standard transfer mode (attribute).
string $filename
-
The filename to check extension for.
int
- either FTP_ASCII or FTP_BINARY.
No errors. Always a filetransfermode should be returned.
This function can not be called statically.
Using checkFileExtension()
<?php
var_dump($test->checkfileextension('foo/bar.zip'));
?>
An object for querying game servers and returning normalised output
Net_GameServerQuery provides an common API to query game servers.
Package for polling Netgeo service retrieving the geographical location of IPs or addresses.
<?php
require_once('Net/Geo.php');
// new Net_Geo object
$net_geo = new Net_Geo();
// fetch the client's IP
$ip = $_SERVER['REMOTE_ADDR'];
// fetch information array from net_geo
$results = $net_geo->getRecord($ip);
// output
echo "Single IP results:<br />";
echo "<pre>";
print_r($results);
echo "</pre>";
// example array of multiple IPs
$arr_ips = array
(
gethostbyname("www.google.com"),
gethostbyname("www.heise.de"),
gethostbyname("www.college.ch")
);
// fetch information array from net_geo
$results = $net_geo->getRecord($arr_ips);
// output
echo "Multiple IP results:<br />";
echo "<pre>";
print_r($results);
echo "</pre>";
?>
bool Net_Geo() (
string $applicationName = ''
, string $alternateServerUrl = ''
)
Create a new object for IP-based geographical information retrieval.
string $applicationName
- Optional application name to use in UserAgent when polling Netgeo
string $alternateServerUrl
- URL to Netgeo service script, will be set to "http://netgeo.caida.org/perl/netgeo.cgi" unless changed
boolean
- Will always return true
This function can not be called statically.
array getRecord() (
mixed $target
)
Get geographical information about a list of targets or a single target. Error messages or "OK" are written to element STATUS of the returned array.
mixed $target
- A single IP or an array of IPs.
array
- Informative array about geographical location of target(s)
Error code | Error message | Reason | Solution |
---|---|---|---|
string "OK" | "No error occured." | Target was resolved properly. | NULL |
const INPUT_ERROR |
"User input is unusable target "
|
The target given cannot be used. | Check target parameter: A string with a single domain or IP, or a one-dimensional array with domain or IP in every element |
const NETGEO_HTTP_ERROR | "Netgeo unreachable" | A connection to the Netgeo service could not be established. | Check http://netgeo.caida.org/perl/netgeo.cgi (the default server URL used). If the script moved, use the parameter $alternateServerUrl in Net_Geo(). |
This function can not be called statically.
Library to perform geo-location lookups of IP addresses.
This package is built on top of MaxMind's GeoIP databases to accurately determine the geographic location of an IP address. MaxMind offers both free and non-free database. Please refer to their website for specific instructures on obtaining both database types.
object getInstance() (
string $filename [, int $flags]
)
This method is an implementation of the so-called singleton pattern and is the preferred way to create instances of Net_GeoIP.
Creating an instance of Net_GeoIP
<?php
require_once "Net/GeoIP.php";
$geoip = Net_GeoIP::getInstance("/path/to/geoipdb.dat", Net_GeoIP::SHARED_MEMORY);
?>
If you want to use multiple databases in one application, you will need to create an instance of Net_GeoIP for each database. Using the singleton getInstance() method will make sure that at any given point exactly one object for each database exists, which saves on overhead of setting up database segments.
string $filename
- Name of (and path
to) the database file
int $flags
- Flags that control the
class behaviour. This parameter can be one of the following
class constants:
Net_GeoIp::SHARED_MEMORY - use SHMOP to share a database among multiple PHP instances.
Only one Net_GeoIP instance can use shared memory at a time.
If you are using Net_GeoIP::SHARED_MEMORY (shmop) you can only use Net_GeoIP::SHARED_MEMORY for one (1) instance (i.e. for one database). Any subsequent attempts to instantiate using SHARED_MEMORY will read the same shared memory block already initialized, and therefore will cause problems since the expected database format won't match the database in the shared memory block.
Note that there is no easy way to flag "nice errors" to prevent attempts to create new instances using Net_GeoIP::SHARED_MEMORY flag and it is also not posible (in a safe way) to allow new instances to overwrite the shared memory block.
In short, is you are using multiple databases, use the Net_GeoIP::SHARED_MEMORY flag with care.
Net_GeoIp::MEMORY_CACHE - store the full contents of the database in memory for current script.
This is useful if you access the database several times in a script.
Net_GeoIp::STANDARD - standard no-cache version. This is also the default value if this parameter is ommitted.
string lookupCountryName() (
string $addr
)
This method returns the full country name for the given IP address. It works with both the free and the non-free databases.
Looking up the country name
<?php
require_once "Net/GeoIP.php";
$geoip = Net_GeoIP::getInstance("/path/to/geoipdb.dat");
try {
echo $geoip->lookupCountryName($_SERVER['REMOTE_ADDR']);
} catch (Exception $e) {
// Handle exception
}
?>
string $addr
- IP address
Lookups on HostnamesNote that this PHP API does NOT support lookups on hostnames. This is so that the public API can be kept simple and so that the lookup functions don't need to try name lookups if IP lookup fails (which would be the only way to keep the API simple and support name-based lookups).
If you do not know the IP address, you can convert an name to IP very simply using PHP native functions or other libraries:
<?php
$geoip->lookupCountryName(gethostbyname("example.com"));
?>Or, if you don't know whether an address is a name or IP address, use application-level logic:
<?php
if (ip2long($ip_or_name) === false) {
$ip = gethostbyname($ip_or_name);
} else {
$ip = $ip_or_name;
}
$country = $geoip->lookupCountryName($ip);
?>
This method sthrow an exception if the IP address is invalid or if the database type is incorrect.
string lookupCountryCode() (
string $addr
)
This method returns the 2-letter country code for the given IP address. It works with both the free and the non-free databases.
Looking up the country code
<?php
require_once "Net/GeoIP.php";
$geoip = Net_GeoIP::getInstance("/path/to/geoipdb.dat");
try {
echo $geoip->lookupCountryCode($_SERVER['REMOTE_ADDR']);
} catch (Exception $e) {
// Handle exception
}
?>
This method throws an exception if the IP address is invalid or if the database type is incorrect.
array lookupRegion() (
string $addr
)
This method returns an array containing the country code and the region for the specified IP address. It works only with a non-free Region database.
Looking up the region
<?php
require_once "Net/GeoIP.php";
$geoip = Net_GeoIP::getInstance("/path/to/geoipdb.dat");
try {
list($country_code, $region) = $geoip->lookupRegion($_SERVER['REMOTE_ADDR']);
} catch (Exception $e) {
// Handle exception
}
?>
This method throws an exception if the IP address is invalid.
object lookupLocation() (
string $addr
)
This method returns an instance of Net_GeoIP_Location for the specified IP address. It works only with a non-free City database.
Looking up the location record
<?php
require_once "Net/GeoIP.php";
$geoip = Net_GeoIP::getInstance("/path/to/geoipdb.dat");
try {
$location = $geoip->lookupLocation($_SERVER['REMOTE_ADDR']);
var_dump($location);
printf("City: %s, %s\nLatitude: %s, Longitude: %s\n",
$location->city, $location->region,
$location->latitude, $location->longitude);
} catch (Exception $e) {
// Handle exception
}
?>
This method throws an exception if the IP address is invalid.
string lookupOrg() (
string $addr
)
This method returns the name of the organization or of the ISP which has registered the IP address range that contains the specified IP address. It works only with a non-free Organization/ISP database.
Looking up organization name
<?php
require_once "Net/GeoIP.php";
$geoip = Net_GeoIP::getInstance("/path/to/geoipdb.dat");
try {
echo $geoip->lookupOrg($_SERVER['REMOTE_ADDR']);
} catch (Exception $e) {
// Handle exception
}
?>
This method throws an exception if the IP address is invalid or the database type is wrong.
Provides several methods for working with IP addresses and netmasks.
mixed atoh (
string $addr
)
Converts a dot-quad formatted IP address into a hexadecimal string - for example "192.168.1.0" to "c0a80100". Returns FALSE if the string given is no valid IP adress.
This function can be called statically.
mixed htoa (
string $addr
)
Converts hexadecimal string to a dot-quad formatted IP address - for example "c0a80100" to "192.168.1.0". Returns FALSE if the hexadecimal string is no valid IP adress.
This function can be called statically.
float ip2double (
string $ip_addr
)
Converts an IP address to a PHP double - for example "192.168.1.0" to "3232235776". A double is, in contrast to ip2long's integer, not signed and may be more comfortable when presented to the user. It can still be reconverted with PHP's long2ip().
This function can be called statically.
bool validateIP (
string $ip_addr
)
Validates the syntax of an IP address in dot-quad format. Returns true if address is valid, false if not.
This function can be called statically.
bool validateNetmask (
string $netmask
)
Validates the syntax of a dot-quad formatted netmask. Returns true if netmask is valid, false if not.
This function can be called statically.
bool parseAddress (
string $address
)
Parses a Classless Inter-Domain Routing address (e.g. 192.168.1.0/24) and stores information in the object variables network, ip, netmask, broadcast, long and bitmask.
How to get netmask and broadcast from CIDR address
<?php
require('Net/IPv4.php');
$cidr = '192.168.0.50/16';
$net = Net_IPv4::parseAddress($cidr);
echo $net->network; // 192.168.0.0
echo $net->ip; // 192.168.0.50
echo $net->broadcast; // 192.168.255.255
echo $net->bitmask; // 16
echo $net->long; // 3232235520 (long/double version of 192.168.0.50)
echo $net->netmask; // 255.255.0.0
?>
boolean
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
mixed calculate (
)
Fully populates the object properties based on the IP address and netmask/bitmask properties. Once these two fields are populated, calculate() will perform calculations to determine the network and broadcast address of the network.
Calculating broadcast and network address
<?php
require 'Net/IPv4.php';
// create IPv4 object
$ip_calc = new Net_IPv4();
// set variables
$ip_calc->ip = "192.168.1.10";
$ip_calc->netmask = "255.255.255.0";
/* alternative method with numerical values:
$ip_calc->long = 3232235786;
$ip_calc->bitmask = 24;
*/
// calculate
$error = $ip_calc->calculate();
if (!is_object($error))
{
// if returned true, output
echo "Your network address: ".$ip_calc->network."<br />";
echo "Your broadcast address: ".$ip_calc->broadcast."<br />";
}
else
{
// otherwise handle error
echo "An error occured: ".$error->getMessage();
}
?>
boolean
- Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
bool ipInNetwork (
string $ip
, string $cidr_block
)
Determines whether or not the supplied IP address is within the supplied network.
Checking if a IP adress is contained in a network
<?php
require 'Net/IPv4.php';
$ip = '10.11.12.13';
$net1 = '10.0.0.1/8';
$net2 = '127.0.0.1/8';
echo Net_IPv4::ipInNetwork($ip, $net1) // bool(true)
echo Net_IPv4::ipInNetwork($ip, $net2) // bool(false)
?>
This function can be called statically.
Provides function to work with the 'Internet Protocol v6'
boolean Net_IPv6::checkIPv6 (
string $ip
)
Checks an IP for IPv6 compatibility.
string $ip
- the IP address to check
boolean
- TRUE, if syntax is valid
This function can be called statically.
string Net_IPv6::compress (
string $ip
, boolean $force = false
)
Compresses an IPv6 address. RFC 2373 allows you to compress zeros in an address to '::'. This function expects an valid IPv6 address and compresses successive zeros to '::'
FF01:0:0:0:0:0:0:101 -> FF01::101 0:0:0:0:0:0:0:1 -> ::1
By default, the method does not touch an already compressed address and returns the address as is. This happens also in case of an address that is compressed but can be compressed further, for example an address like FF01::0:1 will be returned as is.
Since version 1.2.1, you can change this behavoir to enforce compression by setting
$force
to true.
string $ip
- the IP address to compress
boolean $force
- if true, an already compressed
IP address will be compressed again
string
- the compressed IP
is an IPv6 address
This function can be called statically.
string Net_IPv6::uncompress (
string $ip
, boolean $leadingZeros = false
)
Uncompresses an IPv6 address. RFC 2373 allows you to compress zeros in an address to '::'. This function expects an valid IPv6 address and expands the '::' to the required zeros.
FF01::101 -> FF01:0:0:0:0:0:0:101 ::1 -> 0:0:0:0:0:0:0:1
Since version 1.2.1, the method can return the address in a nice format where each part of the
address has a fixed length of 4 characters. To enable this, you must set
$leadingZeros
to true.
string $ip
- the IP address to uncompress
boolean $leadingZeros
- if true, the uncompressed address
has a fixed length
string
- the uncompressed IP
is an IPv6 address
This function can be called statically.
int Net_IPv6::getAddressType (
string $ip
)
RFC 1883, Section 2.3 describes several types of addresses in the IPv6 addresse space. This methods tries to find the type of address for the given IP.
Several address types are markers for reserved spaces and as consequence a subject to change.
string $ip
- the IP address in Hex format, compressed IPs are allowed
int
- the addresstype
The type can be one of this constants:
NET_IPV6_MULTICAST
NET_IPV6_UNICAST_PROVIDER
NET_IPV6_LOCAL_LINK
NET_IPV6_LOCAL_SITE
NET_IPV6_UNKNOWN_TYPE
NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC
NET_IPV6_RESERVED_IPX
NET_IPV6_RESERVED
NET_IPV6_RESERVED_NSAP
NET_IPV6_UNASSIGNED
This function can be called statically.
Method available since: Release 1.1.0
string Net_IPv6::getNetmask (
string $ip
, int $bits = null
)
Calculates the network prefix based on the netmask bits.
string $ip
- the IP address in Hex format, compressed IPs are allowed
int $bits
- if the number of netmask bits is not part of the IP, you must provide the mumber of bits
string
- the network prefix
This function can be called statically.
Method available since: Release 1.1.0
string Net_IPv6::getNetmaskSpec (
string $ip
)
Returns a possible existing netmask specification at an IP addresse.
string $ip
- the IP address in Hex format, compressed IPs are allowed
string
- the netmask specification.
This function can be called statically.
Method available since: Release 1.1.0
mixed Net_IPv6::getPrefixLength (
string $ip
)
Tests for a prefix length specification in the address and returns the prefix length, if exists.
string $ip
- a valid ipv6 address
mixed
- the prefix as String or false, if prefix was not found.
This function can be called statically.
Method available since: Release 1.1.0
This function is deprecated. That means that future versions of this package may not support it anymore.
Deprecated in release 1.2.1
boolean Net_IPv6::isInNetmask (
string $ip
, string $netmask
, int $bits = null
)
Checks if an (compressed) IP is in a specific address space.
If the IP does not contain the number of netmask bits (for example: F8000::FFFF/16),
then you have to use the $bits
parameter.
string $ip
- the IP address in Hex format, compressed IPs are allowed
string $netmask
- the netmask (for example: F800::
)
int $bits
- if the number of netmask bits is not part of the IP, you must provide the mumber of bits
boolean
- TRUE, if the IP is in the address space.
This function can be called statically.
Method available since: Release 1.1.0
string Net_IPv6::removeNetmaskSpec (
string $ip
)
Removes a possible existing netmask length specification in an IP addresse.
string $ip
- the IP address in Hex format, compressed IPs are allowed
string
- the IP without netmask length specification.
This function can be called statically.
Method available since: Release 1.1.0
string Net_IPv6::removePrefixLength (
string $ip
)
Tests for a prefix length specification in the address and removes the prefix length, if exists.
string $ip
- the IP address in Hex format, compressed IPs are allowed
string
- the address without a prefix length.
This function can be called statically.
Method available since: Release 1.1.0
This function is deprecated. That means that future versions of this package may not support it anymore.
Deprecated in release 1.2.1
array Net_IPv6::splitV64 (
string $ip
)
Splits an IPv6 address into the IPv6 and a possible IPv4-formated part. RFC 2373 allows you to note the last two parts of an IPv6 address in the IPv4 address format:
0:0:0:0:0:0:13.1.68.3 0:0:0:0:0:FFFF:129.144.52.38
string $ip
- the IP address to split
array
- key [0]
contains the IPv6 part, key [1]
the IPv4
formated part
This function can be called statically.
Net_LDAP allows you to query and manipulate the data stored in directory servers using PHP in an object-oriented way.
Detailed usage examples are available at: /your/pear/path/docs/Net_LDAP/examples/
Please note, that as of 2009-05-28 Net_LDAP2, which is superseding Net_LDAP, has become stable. Please use Net_LDAP2 instead.
Net_LDAP is a clone of Perls Net::LDAP package. PEAR Net_LDAP for PHP does, besides some own features, provide most of Perl Net::LDAP methods. Net_LDAP allows you to query and manipulate the data stored in directory servers using PHP in an object-oriented way. A directory server is a database server providing a hierarchical database and is usually queried using the LDAP protocol.
The following table gives you a short overview which classes are available in Net_LDAP and what they can be used for. Their relations will be explained too.
Class name | Description |
---|---|
Net_LDAP | This is the main class. It enables you to connect and bind to a LDAP-server and to run ldap-querys like searching and manipulating entries. Most common, you will run a search using a LDAP-Filter and will get a Net_LDAP_Search-object. You may also fetch an entry directly, which gives you a Net_LDAP_Entry-object. |
Net_LDAP_Search | Objects of this class are returned from search querys. You can use this object to retrieve informations about a search result, like how much entries you have found for the provided filter. You can retrieve the found entries as Net_LDAP_Entry-objects in various forms: sorted, consecutively starting from the end or beginning or unsorted at once. |
Net_LDAP_Entry | Objects of this kind are either produced by casting fresh entries manually, by retrieving the result of a LDAP-search or by fetching an entry directly. It gives you the possibility to read and/or manipulate the attributes of an entry which describes the characteristic of the specific object. |
Net_LDAP_Util | The utility class contains only static methods, so you should not need to make an instance of it. It features some helpful methods, some of them are used internally by Net_LDAP but may be used externally from you as well. The most methods deal with escaping issues, since LDAP has some metacharacters with special meaning so that they usually need to be properly escaped. |
Net_LDAP_Filter | You are free to give LDAP-Filters on your own to the Net_LDAP->search() method, however this has some drawbacks (including escaping issues). For this reason, you can use the Net_LDAP_Filter class to easily build and combine your filters. LDAP filters are extensively explained at the chapter LDAP filters. |
Net_LDAP_Error | This is a error class. Most methods of Net_LDAP will return a object of this class if something went wrong. You can use this object to identify errors and to get detailed knowledge on what went wrong. See Errorhandling for more information. |
Net_LDAP_LDIF | LDIF files are human readable, plain text files containing directory data and/or change commands, much like an SQL file. Unlike SQL files, it is data centric, not action centric. Net_LDAP_LDIF enables you to convert between Net_LDAP_Entry-objects and LDIF files. Please note, that Net_LDAP_LDIF has a little different error handling explained later. |
Nearly all of Net_LDAPs methods return a Net_LDAP_Error object if something went wrong. You always should check for errors after you performed an action to be sure that your application doesn't do things you don't want it to do.
Handling errors is an easy task, you just have to test the return value as shown below. If an error occured, you can halt the script for example. In other cases, you may just log the error, but what exactly happens depends on your specific situation, of course.
You can use the getMessage() method of the error object to retrieve the error message explaining the problem and getCode() to get the error code which is usually the LDAP-Error code (see Table below) and may be used for automated reaction on errors.
Dealing with errors
<?php
// Perform an arbitrary action:
$result = $ldap->search($searchbase, $filter, $options);
// Check, if an error occured and do something.
// Here we use die() to show the message of the error.
if (PEAR::isError($result)) {
die($result->getMessage());
}
?>
Error code | Description |
---|---|
0x00 | LDAP_SUCCESS |
0x01 | LDAP_OPERATIONS_ERROR |
0x02 | LDAP_PROTOCOL_ERROR |
0x03 | LDAP_TIMELIMIT_EXCEEDED |
0x04 | LDAP_SIZELIMIT_EXCEEDED |
0x05 | LDAP_COMPARE_FALSE |
0x06 | LDAP_COMPARE_TRUE |
0x07 | LDAP_AUTH_METHOD_NOT_SUPPORTED |
0x08 | LDAP_STRONG_AUTH_REQUIRED |
0x09 | LDAP_PARTIAL_RESULTS |
0x0a | LDAP_REFERRAL |
0x0b | LDAP_ADMINLIMIT_EXCEEDED |
0x0c | LDAP_UNAVAILABLE_CRITICAL_EXTENSION |
0x0d | LDAP_CONFIDENTIALITY_REQUIRED |
0x0e | LDAP_SASL_BIND_INPROGRESS |
0x10 | LDAP_NO_SUCH_ATTRIBUTE |
0x11 | LDAP_UNDEFINED_TYPE |
0x12 | LDAP_INAPPROPRIATE_MATCHING |
0x13 | LDAP_CONSTRAINT_VIOLATION |
0x14 | LDAP_TYPE_OR_VALUE_EXISTS |
0x15 | LDAP_INVALID_SYNTAX |
0x20 | LDAP_NO_SUCH_OBJECT |
0x21 | LDAP_ALIAS_PROBLEM |
0x22 | LDAP_INVALID_DN_SYNTAX |
0x23 | LDAP_IS_LEAF |
0x24 | LDAP_ALIAS_DEREF_PROBLEM |
0x30 | LDAP_INAPPROPRIATE_AUTH |
0x31 | LDAP_INVALID_CREDENTIALS |
0x32 | LDAP_INSUFFICIENT_ACCESS |
0x33 | LDAP_BUSY |
0x34 | LDAP_UNAVAILABLE |
0x35 | LDAP_UNWILLING_TO_PERFORM |
0x36 | LDAP_LOOP_DETECT |
0x3C | LDAP_SORT_CONTROL_MISSING |
0x3D | LDAP_INDEX_RANGE_ERROR |
0x40 | LDAP_NAMING_VIOLATION |
0x41 | LDAP_OBJECT_CLASS_VIOLATION |
0x42 | LDAP_NOT_ALLOWED_ON_NONLEAF |
0x43 | LDAP_NOT_ALLOWED_ON_RDN |
0x44 | LDAP_ALREADY_EXISTS |
0x45 | LDAP_NO_OBJECT_CLASS_MODS |
0x46 | LDAP_RESULTS_TOO_LARGE |
0x47 | LDAP_AFFECTS_MULTIPLE_DSAS |
0x50 | LDAP_OTHER |
0x51 | LDAP_SERVER_DOWN |
0x52 | LDAP_LOCAL_ERROR |
0x53 | LDAP_ENCODING_ERROR |
0x54 | LDAP_DECODING_ERROR |
0x55 | LDAP_TIMEOUT |
0x56 | LDAP_AUTH_UNKNOWN |
0x57 | LDAP_FILTER_ERROR |
0x58 | LDAP_USER_CANCELLED |
0x59 | LDAP_PARAM_ERROR |
0x5a | LDAP_NO_MEMORY |
0x5b | LDAP_CONNECT_ERROR |
0x5c | LDAP_NOT_SUPPORTED |
0x5d | LDAP_CONTROL_NOT_FOUND |
0x5e | LDAP_NO_RESULTS_RETURNED |
0x5f | LDAP_MORE_RESULTS_TO_RETURN |
0x60 | LDAP_CLIENT_LOOP |
0x61 | LDAP_REFERRAL_LIMIT_EXCEEDED |
1000 | Unknown Net_LDAP Error |
To connect to an LDAP server, you should use Net_LDAP's static connect() method. It takes one parameter, an array full of configuration options, and either returns a Net_LDAP object if connecting works, or a Net_LDAP_Error object in case of a failure.
The following table lists all configuration options. If the default value for an option fits your needs, you don't need add it to your configuration array.
Name | Description | Default |
---|---|---|
host |
LDAP server name to connect to. You can provide several hosts in an array in which case the hosts are tried from left to right. | localhost |
port |
Port on the server | 389 |
version |
LDAP version | 3 |
starttls |
TLS is started after connecting | false |
binddn |
The distinguished name to bind as (username) | (none) |
bindpw |
Password for the binddn |
(none) |
basedn |
LDAP base name (root directory) | (none) |
options |
Array of additional ldap options as key-value pairs | array() |
filter |
Default search filter (string or preferably Net_LDAP_Filter object). See LDAP filters | (objectClass=*) |
scope |
Default search scope, see Search | sub |
Connecting to an LDAP server
<?php
// Inclusion of the Net_LDAP package:
require_once 'Net/LDAP.php';
// The configuration array:
$config = array (
'binddn' => 'cn=admin,ou=users,dc=example,dc=org',
'bindpw' => 'password',
'basedn' => 'dc=example,dc=org',
'host' => 'ldap.example.org'
);
// Connecting using the configuration:
$ldap = Net_LDAP::connect($config);
// Testing for connection error
if (PEAR::isError($ldap)) {
die('Could not connect to LDAP-server: '.$ldap->getMessage());
}
?>
It may be possible that restricted characters (",", "+", """, "\", "<", ">", ";", "#", "=", space or a hexpair) are used in attributes or values inside the DN. You should have a look to the APIdoc of Net_LDAP2_Util::escape_dn_value(), Net_LDAP2_Util::unescape_dn_value(), Net_LDAP2_Util::ldap_explode_dn() and Net_LDAP2_Util::canonical_dn(). These functions can be used to safely handle DNs.
After connecting to the server, you can use Net_LDAP's search() method to search the directory. The method takes three parameters:
$base
is the base search DN. If kept
null
, the default base DN configured when connecting
is used.
$filter
is the query filter that determines which
results are returned. It is either a string (experts use only) or better a Net_LDAP_Filter-object.
Net_LDAP_Filter automatically deals with LDAP-Filter escaping issues.
LDAP filters are extensively explained at the chapter LDAP filters.
$params
is an array of configuration options for
the current query.
Name | Description | Default |
---|---|---|
scope |
The scope used for searching:
|
sub |
sizelimit |
Number of entries returned at maximum | 0 (no limit) |
timelimit |
Seconds to spent for searching | 0 (no limit) |
attrsonly |
If true , only attribute names are returned |
false |
attributes |
Array of attribute names, which the entry should contain. It is good practice to limit this to just the ones you need. | array() (all attributes) |
The search() method will return either a Net_LDAP_Search object or a Net_LDAP_Error. You can use the Net_LDAP_Search-object to trigger further actions like counting how many entries where found or to retrieve the found entries.
Making a search query
<?php
// Building a very basic filter
// we want to find all Entries whose surnames start with "Joe":
$filter = Net_LDAP_Filter::create('sn', 'begins', 'Joe');
// We define a custom searchbase here. If you pass NULL, the basedn provided
// in the Net_LDAP configuration will be used. This is often not what you want.
$searchbase = 'ou=addressbook,dc=example,dc=org';
// Some options:
// We search all subtrees beneath 'ou=addressbook,dc=example,dc=org'
// and we select the attribute 'sn'. It is a good practice to limit the
// requested attributes to only those you actually want to use later.
// However, note that it is faster to select unneeded attributes than
// refetching an entry later to just get those attributes.
$options = array(
'scope' => 'sub',
'attributes' => array('sn')
);
// Perform the search!
$search = $ldap->search($searchbase, $filter, $options);
// Test for search errors:
if (PEAR::isError($search)) {
die($search->getMessage() . "\n");
}
// Say how many entries we have found:
echo "Found " . $search->count() . " entries!";
?>
You can retrieve directory entries in several ways, either directly or from a performed search request. If you want to fetch an entry directly, you need to know its absolute distinguished name (DN). To directly fetch an known entry from the directory server, you use Net_LDAP's getEntry() method. It takes two parameters: The DN of the entry and the attributes you want to read from the entry. It returns a Net_LDAP_Entry object if fetching worked, or a Net_LDAP_Error object in case of a failure.
You may also check if the entry exists in the server before you fetch it. This can be achieved by Net_LDAP's dnExists() which takes the DN to test and returns either true or false.
Fetching an entry directly
<?php
// Defining the DN we want to fetch;
// we want to select the given- and the surname
$dn = 'cn=admin,o=example,dc=org';
$entry = $ldap->getEntry($dn, array('gn', 'sn'));
// Error checking is important!
if (Net_LDAP::isError($entry)) {
die('Could not fetch entry: '.$entry->getMessage());
}
?>
The second way to retrieve entries is from a searchresult. As described in chapter "Search", you access the entries of a search result through the Net_LDAP_Search-object resulting from Net_LDAP's search() method. Each of the following methods return a Net_LDAP_Error-object if something goes wrong, so remember to test for errors! You have several ways to read the entries:
Method of Net_LDAP_Search | Description |
---|---|
entries() | This returns the entries at once unsorted. |
as_struct() | This returns all entries as multidimensional array instead of Net_LDAP_Entry-objects. The array keys of the first dimension are the DNs and the value is an array containing all attributes. The array keys of the second level are the attributes names; the value of the second level is an array containing all the attributes values. Note, that even if there are no or just one value, an array is present. |
sorted() | Use this if you want to get the entries at once but sorted. You can sort by several attributes which can contain multiple values.
You can of course sort ascending (default) or descending - just pass the PHP constant SORT_ASC or SORT_DESC as second parameter. |
sorted_as_struct() | Like as_struct(), this returns the entries as multidimensional array, but in this case sorted. For parameters, see sorted() |
shiftEntry() | This returns one entry from the beginning of the search result.
Since this returns FALSE if all entries are fetched, shiftEntry() is perfectly
appropriate to get used inside a while-loop.
Take care not to mix shiftEntry() and popEntry()! |
popEntry() | Exactly the same as shiftEntry, but returns the entry from the end of the searchresult. Again, be sure to not mix shiftEntry() and popEntry()! |
To directly fetch an known entry from the directory server, you use Net_LDAP's getEntry() method. It takes two parameters: The DN of the entry and the attributes you want to read from the entry. It returns a Net_LDAP_Entry object if fetching worked, or a Net_LDAP_Error object in case of a failure.
You may also check if the entry exists in the server before you fetch it. This can be achieved by Net_LDAP's dnExists() which takes the DN to test and returns either true or false.
Fetching all entries from searchresult
<?php
// return all entries:
$entry = $search->entries();
?>
Fetching all entries from searchresult: sorted
<?php
// return sorted by first 'sn' then 'gn', but descending:
$entry = $search->sorted(array('sn', 'gn'), SORT_DESC);
?>
Fetching entries one by one inside a while loop
<?php
// return entries one by one:
while ( $entry = $search->shiftEntry() ) {
// do something, like printing the DN of the entry;
// in a real case, dont forget to test for errors!
echo "ENTRY: " . $entry->dn();
}
?>
Reading attribute values depends on the selection of those attributes at search time. You can only access attributes that where selected! You can read attribute values using either Net_LDAP_Entry's getValues() or getValue() method. getValue() will return an array where the keys are the attributes names. If you use getValues() you may pass an option:
'single'
: only the first value is returned as string
'all'
: all values including the value count are returned in an array
'default'
: in all other cases an attribute value with a single value is
returned as string, if it has multiple values it is returned
as an array (without value count)
Reading attributes
<?php
// read Surename, singlevalued
$surename = $entry->getValue('sn', 'single');
// read mail adress which may be multivalued
$mail = $entry->getValue('mail', 'all');
?>
If you want to read the distinguished name of an Entry (DN), you must use a different method: dn()
Reading an entries DN
<?php
$dn = $entry->dn();
?>
PEAR::Net_LDAP has the unique feature to apply a regular expression match directly against attributes, so you do not need to manually fetch all values and run the regex against them. Instead, you can use Net_LDAP_Entry's preg_match() function. The behavior of this function is the same as PHPs preg_match(), but the $matches array is slightly different. It features one dimension more, since it may match for several attribute values if the attribute is multivalued. If you pass $matches, be sure to do it via REFERENCE, because otherwise $matches remains empty. preg_match() returns true or false, depending on match.
Performing preg_match on attribute values
<?php
// Look, if the user has an emailadress for 'example', if so,
// we want to display the tld:
// (be sure to pass $matches as reference!)
$matches = array();
if ( $entry->preg_match('mail', '/example\.(.+)/', &$matches) ) {
// print every TLD found for 'example':
foreach ($matches as $match) {
echo $match[1];
}
}
?>
It is important to know how attribute changing works. Modifications to an entry
through the Net_LDAP_Entry-object are local only.
After you have made all changes and want to transfer them to the directory server, you must
call update() of the Net_LDAP_Entry object.
This will return either TRUE
or an Net_LDAP_Error.
Another good information is, that you must select attributes at search time
if you want to add/change/delete attribute values. Otherwise Net_LDAP will most likely fail
silently giving you the wrong assumtion that everything was okay - Net_LDAP needs knowledge
of the attributes it should work with!
Modification of attributes is also possible through Net_LDAP's modify() method. This method will call the methods described here on the Net_LDAP_Entry object given, and directly calls an update() after that, thus performing the changes directly on the server. The parameter is an complex array describing the changes to be performed. It is considered for more advanced users, because it is more compact, so please refer to the latest API documentation for more information.
Adding attrbiute values to an entry is an easy task. You just need to call add()! The parameter is an array whose keys are the attribute names and values the attributes values. If only one attribute value should be added, the second level may be a string. If the attribute doesn't exist so far, it will be added, if it exists, the attributes values will be added.
Adding attributes
<?php
// Adding several attributes:
$result = $entry->add(
array(
'sn' => 'Doe',
'gn' => array('John'),
'mail' => array('john@doe.org', 'j.doe@example.org')
)
);
?>
Changing values is with the replace() method as easy as adding values. However, you have to be a little more careful.
The expected parameter is an array describing the new absolute state of the named
attributes. This means, if you specify a NULL
value for an attribute,
this attribute will get deleted!
You may specify single values as string too.
The keys of the array are expected to be the attributes names.
Changing attributes
<?php
// Changing several attributes:
// 'sn' is changed to "Smith", 'gn' gets deleted and mail will
// be changed to te two new adresses
$result = $entry->replace(
array(
'sn' => 'Smith',
'gn' => null,
'mail' => array('smith@example.org', 'smith@example.de')
)
);
?>
Using the delete() method you are able to delete specific attributes values as well
as delete a whole attribute.
You need to specify the attribute names as array keys, the array values are the values you want to delete.
If you want to delete whole attributes, specify them as single level array.
Special care must be taken not to delete the whole entry which will be the case if the parameter array is
omitted or set to NULL
!
Also, don't mix syntax modes. If you want to delete whole attributes you can't delete specific values from another attribute
in the same function call.
Deleting attributes
<?php
// Delete the whole entry:
$result = $entry->delete();
// Delete the whole telephone number attribute:
$result = $entry->delete('telephoneNumber');
// Delete one specific mail attributes value:
$result = $entry->delete( array('mail' => 'j.doe@example.org') );
// Delete mail and telephone attributes as a whole:
$result = $entry->delete( array('mail', 'telephoneNumber') );
// Delete two specific mail adresses:
$result = $entry->delete( array('mail' => array('smith@example.org', 'smith@example.de')) );
?>
Object classes describe the attribute set of an entry with this objectclass set. The entry stores the objectclass in a special attribute named "objectClass", and of course you may alter that attribute like any other attribute.
However, special care must be taken if changing this attribute since most directory servers impose rules on the other attributes the object class define. For example, it is usually not possible to delete an objectclass if some of the attributes the class describes are still in use by the entry. This should be not much of a problem with optional attributes, but sometimes objectclasses have mandatory attributes set. Also structural objectclasses can only be added when creating new entrys. Because of the internal architecture of Net_LDAP it is currently not possible to resolve those cases.
To add or remove objectclasses with mandatory attributes or new structural object classes, you need to delete the old entry from the directory server and add the new one with the new objectclass and attributes as fresh entry.
Changing complex objectclasses
<?php
// Let's assume that the objectclass myClass enforce the attribute "fooattr"
// Take care that you have all attributes requested, otherwise the new
// entry will not have all attributes set!
$entry->add(array(
'objectClass' => 'myClass',
'fooatrr' => 'foo',
'someotherattr' => array('bar', 'baz')
));
// Calling $entry->update() now will not succeed under some circumstances!
// We construct a fresh entry object which is in fact a copy of the already
// existing entry with all changes already applied (the local copy).
// It is important, that at fetching time of $entry all attributes where selected!
// Only the selected attributes will get copied.
$changed_entry = Net_LDAP_Entry::createFresh($entry->dn(), $entry->getValues());
// Now delete the old entry and add the new one:
$ldap->delete($entry);
$ldap->add($changed_entry);
?>
Adding new entries is performed in two ways. First, you need to establish a fresh Net_LDAP_Entry object. After that, you can add that entry like you would add already-existent entries using Net_LDAP's add() method.
Adding a fresh entry
<?php
// Build a new fresh entry:
$dn = 'cn=new-admin,o=example,dc=org';
$attributes = array(
'cn' => 'new-admin',
'mail' => array('new-admin@exaple.org', 'n.admin@example.de'),
'telephoneNumber' => '1234567890'
);
$entry = Net_LDAP_Entry::createFresh($dn, $attributes);
// Add the entry to the directory:
$ldap->add($entry);
?>
Renaming and/or moving an entry is an operation on the DN of an entry. Moving an entry means, to rename a DN in such a way, that the entry becomes a new base-DN. You can rename or move an entry, if you call the dn() method of the entry you want to relocate. Alternatively, you may call Net_LDAP's move() method that also ca handle only DNs. Remember that you must call the entires update() method to carry out the move/rename. Net_LDAP's move() will move the entry immediately. If you use an entryobject togehter with Net_LDAP's move(), you are able to perform cross directory moves.
Moving an entry using Net_LDAP_Entry
<?php
// Defining the DN we want to fetch;
$dn = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin,o=new-example,dc=org';
$entry = $ldap->getEntry($dn);
$entry->dn($newdn);
?>
Moving an entry using Net_LDAP and Net_LDAP_Entry
<?php
// Defining the DN we want to fetch;
$dn = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin,o=new-example,dc=org';
$entry = $ldap->getEntry($dn);
$ldap->move($entry, $newdn);
?>
Renaming an entry using Net_LDAP and DNs
<?php
// Defining the DN we want to fetch;
$dn = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin2,o=new-example,dc=org';
$ldap->move($dn, $newdn);
?>
Performing a cross directory move
<?php
// $ldap_src is the source ldap and $ldap_tgt the target
$dn = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin,o=new-example,dc=org';
$entry = $ldap_src->getEntry($dn);
$ldap_src->move($entry, $newdn, $ldap_tgt);
?>
Deleting entries is performed using Net_LDAP's delete() method.
Just pass the Net_LDAP_Entry object or the DN of the entry you want to delete.
In the case that the DN contains subentrys, you need to pass TRUE
as second parameter
which will make delete() delete recursive.
A second way exist: You may simply call delete() from the Net_LDAP_Entry you want to delete. Don't forget that you must call update() to carry out the delete in this case.
Deleting an entry
<?php
$dn = 'cn=new-admin,o=example,dc=org';
$ldap->delete($dn);
?>
Deleting an entry using a Net_LDAP_Entry object
<?php
$entry->delete();
$entry->update();
?>
LDAP filters are defined in RFC 2254 and can be compared to the WHERE clause in SQL select statements - they filter the data returned from some search request - in this case the entries returned from the directory server. With Net_LDAP, you may use plain strings as filters, or preferably, the Net_LDAP_Filter class which mostly releases you of the burden to escape yourself and to remember all the various special characters needed for constructing and combining filters.
Where and how to use filters is described in chapter Search.
Although you should preferably use the Net_LDAP_Filter class to construct your LDAP filters, some theory may be interesting and helpful in understanding how to construct LDAP filters and what they are capable of.
Basic LDAP filters are composed of an "[attribute][operator][value]" pair enclosed by round brackets. There are several comparison operators available: "=" (equal), ">" (greater), "<" (less), ">=" (greater or equal), "<=" (less or equal) and "=~" (phonetical similar). The exact match behavior is defined by the attribute syntax of the attribute to which the filter should apply.
Some basic string filters
<?php
// Filter entries whose first name is 'Benedikt':
$filter = '(givenName=Benedikt)';
// Filter entries which have an employeenumber higher or equal than 1424:
$filter = '(employeeNumber>=1424)';
// Filter entries whose first name sounds similar to "Stephane"
// This should also find "Stephen" and "Stefan" (depending on implementation)
$filter = '(givenName=~Stephane)';
?>
The value part of the basic filter construct could also include a special character: "*". The star acts as placeholder for none, one or several characters at that position. "V*lue" would therefore match against "Value", "Vlue", "VaaAaAalue" and so on. There are some special named combinations using the star, but they work exactly the same way:
Name | Filter | Description |
---|---|---|
present | (attr=*) | Also refered to as "any". Finds any entry containing any (unless empty) value for the named attribute. |
begins | (attr=value*) | value starts with some fixed string |
ends | (attr=*value) | value ends with some fixed string |
contains | (attr=*value*) | value contains some fixed string |
Basic filters can be combined using the three logical operators "&" (and), "|" (or) and "!" (not). Note, that the smallest filter component, the basic filter enclosed in round brackets, remains isolated: instead of just adding another "[attribute][operator][value]" pair into the brackets, a new bracket level is introduced that contains all filter components that should be combined. Note also, that the logical operator does stand in front of all filter components, not between them as common in programming languages.
Combining string filters
<?php
// Search all 'Benedikt's with phone number 1234567890
$filter = '(&(givenName=Benedikt)(telephoneNumber=1234567890))';
// Search the same, but exclude person "Benedikt Foobar"
// Note that the "not" is a logical operator and thus needs its own
// surrounding bracket. This explains nicely, that each bracket level
// is evaluated independently from surrounding brackets.
$filter = '(&(givenName=Benedikt)(telephoneNumber=1234567890)(!(sureName=Foobar)))';
?>
As you will read below, there are some special characters inside the LDAP filter definition and thus must be escaped. These are mainly the special characters used directly by the filter syntax like braces and the logical operators. Despite those, there are some other cases which need special threatment. Nearly all of this cases are hidden through the Net_LDAP_Filter class so you should only consider using string filters if you need to or you know what you are doing. If you need a filter string, you may also use Net_LDAP_Filters toString() function after building the filter.
The filter class has two different usage models: one for constructing basic filters and another to combine them logically. This has to do with the syntax of LDAP filters you may read below.
For creating basic filter components, you need to use the create() factory method.
There, you combine three items: an attribute to filter for, a matching rule for comparison and a value
that is beeing compared with the servers entries.
The given value is automatically escaped, so you need take care if you want to use the star placeholder.
In this case, you need to pass FALSE
as fourth parameter to create()
which causes value
to be threaten as-is. This of course also means, that you need to
escape the parts of the value that may contain restricted characters yourself using
Net_LDAP_Util::escape_filter_value(). To learn what characters
are restricted, refer to RFC 2254 or the documentation of
Net_LDAP_Util::escape_filter_value(); otherwise its safe to always escape.
The matching rules partly follow the basic filter matching rules described above, but are enhanced to make your life easier:
Rule | Description |
---|---|
equals | One of attribute s values is exactly value . Please note that case sensitiviness depends on the matching rule defined in the attributes schema syntax. |
begins | One of attribute s values must begin with value |
ends | One of attribute s values must end with value |
contains | One of attribute s values must contain value |
present | any | The attribute can contain any value but must be existent |
greater | The attribute s value is greater than value |
less | The attribute s value is less than value |
greaterOrEqual | The attribute s value is greater or equal than value |
lessOrEqual | The attribute s value is less or equal than value |
approx | One of attribute s values sounds similar to value . The matching behavior depends on the server implementation. |
Creating LDAP filters
<?php
// Filter entries whose first name is 'Benedikt':
$filter = Net_LDAP_Filter::create('givenName', 'equals', 'Benedikt');
// Filter entries whose first name starts with 'Steph':
$filter = Net_LDAP_Filter::create('givenName', 'begins', 'Steph');
// Filter entries containing 'Lone*'; matching the star character.
// The automatic escaping of $value will conveniently escape the star for us.
$filter = Net_LDAP_Filter::create('givenName', 'contains', 'Lone*');
// Filter entries containing 'Foo[something]Bar'; not matching the star character.
// For this to work, we need to disable automatic escaping of $value by passing
// false as fourth parameter. This however implies, that we take care of
// proper escaping, which is showed in the example.
$escaped_values = Net_LDAP_Util::escape_filter_value(array('Foo', 'Bar'));
$foo =& $escaped_values[0];
$bar =& $escaped_values[1];
$filter = Net_LDAP_Filter::create('givenName', 'contains', "$foo*$bar", false);
// Filter entries whose first name sounds similar to "Stephane"
// This should also find "Stephen" and "Stefan" (depending on implementation)
$filter = '(givenName=~Stephane)';
?>
Although the filters can be used stand alone, they can be combined to match sophisticated
search requiremets. This is done by using the combine() to combine
several present Net_LDAP_Filter objects using a logical operator.
The execption is the not
operator since it only allows one filter
object to be negated.
Rule | Description |
---|---|
and | All filter components must evaluate to true for the combined filter to be true |
or | At least one filter component must evaluate to true for the combined filter to be true |
not | The result of the filter component is inversed (true becomes false and vice versa).
Note that this operator only accepts one filter object. |
Combining LDAP filters
<?php
// Create some test filters
$filter_benedikt = Net_LDAP_Filter::create('givenName', 'equals', 'Benedikt');
$filter_steph = Net_LDAP_Filter::create('givenName', 'begins', 'Steph');
$filter_foobar = Net_LDAP_Filter::create('sureName', 'equals', 'Foobar');
$filter_height = Net_LDAP_Filter::create('personHeight', 'greater', '175');
// Negate 'foobar' filter.
// This filters every entry whose sure name is not 'Foobar'
$filter_not_foobar = Net_LDAP_Filter::combine('not', $filter_foobar);
// Build a 'and' combination to be able to search for people whose
// first names start with 'Steph' and who are are taller than 175
// except those whose surname is 'Foobar'
$filter_stephs_tall = Net_LDAP_Filter::combine('and',
array($filter_steph, $filter_height, $filter_not_foobar));
// In any case, add every person whose first name is
// 'Benedikt' to the search result.
$filter_add_benedikt = Net_LDAP_Filter::combine('or', array($filter_benedikt, $filter_stephs_tall));
?>
You may need some advanced functionality if you have to deal with string representation of filters.
Method | Description |
---|---|
parse() | Takes an filter string and parses it into a Net_LDAP_Filter object. It also verifies, that the filter syntax is correct. |
printMe() | In PERLs interface, this method is called "print" but due to language constraints, we cannot use that name. Prints the string representation of this filter object to standard output or to an optional filehandle passed as parameter. |
toString() | Returns the string representation of the filter object. |
LDIF support was added to Net_LDAP in release 1.1.0a1.
LDIF files are in detail described at RFC 2849. Shortly, they contain directory data in an plain text, human readable kind, much like a SQL file does. However, unlike SQL files LDIF files are mostly data based, not action based. There are two different LDIF file contents, which can be mixed freely - content and change files. The first and most often used one is the LDIF content file:
Example LDIF content file
# # This is a content LDIF file. # It contains one single entry featuring several # attributes and one comment (this one). # attr1, attr4 and cn are single valued, the others are # multivalued. objectclass is a special case, LDAP servers # will interpret this operational attribute to define the # classes the object will belong to and thus, which attributes # it may contain. the OCL-attribute is usually multivalued. # version: 1 dn: cn=test1,ou=example,dc=cno objectclass: someobjectclass attr1: 12345 attr2: 1234 attr2: baz attr3: foo attr3: bar attr4: brrrzztt cn: test1
LDIF files could describe not only the data an entry contains, but also various changes to the entry itself. If such an LDIF file would then be given to a LDAP server, he would interpret those changes instead just importing the data. Note in the example below, that even though LDIF content and LDIF change files could be mixed freely, this is not true for individual entries: a specific entry may be either describing content or changes, but not both.
Example LDIF change file
# # This is a content+change LDIF file. # It does contain the (shortened) entry from the example above to show # that LDIF files can contain multiple entry modes. # The second entry is a change entry. In this case, some # operations will be done on the entries attributes. # version: 1 dn: cn=test1,ou=example,dc=cno objectclass: someobjectclass attr1: 12345 cn: test1 # Delete attr1, replace values of attr2 and add new attribute attr42 # The attribute "changetype" is special: it says, what to do with # this entries dataset. It could also be "delete" or "add" to delete # a whole entry or to add a completely fresh one. "modrdn" will # move the entry to a new location once the LDIF file is imported. dn: cn=test2,ou=example,dc=cno changetype: modify delete: attr1 - replace: attr2 attr2: 123456_newtest - add: attr42 attr42: the answer
Before we can start using Net_LDAP_LDIF we must say some short words about how error handling works. Net_LDAP_LDIF was designed to have mostly the same API as the original PERL Net::LDAP::LDIF has. Because of this, the methods of Net_LDAP_LDIF do not return a Net_LDAP_Error object. You must use the error() method that will return a Net_LDAP_Error object in case of failure or true in case everything was ok. In LDIF reading mode, you can additionally use error_lines() to get knowledge about where in the input file the error occured.
Regardless if you want to read or write a LDIF file, you always have to use the constructor of Net_LDAP_LDIF to initialize your access to the LDIF file. You need to pass at least one parameter to Net_LDAP_LDIF(): the path of the file that should be read or written. You may pass the open mode as second parameter. The possible file open modes are "r" (read), "w" (write, clears the file first) and "a" (append to the end). In case you omit the open mode, read mode is assumed. The third optional parameter is an associative array containing one or several of the following options:
Name | Description | Default |
---|---|---|
encode |
Some DN values in LDIF cannot be written verbatim and have to be encoded in some way. Possible values are: "none", "canonical" and "base64" (RFC default) | base64 |
onerror |
What should be done on errors? "undef" will let error handling in your hands, in this case you use error() and error_lines() to process errors manually. "die" aborts the script printing the error - this is sometimes useful for CLI scripts. "warn" just prints out the error but continues like "undef" would. | undef |
change |
Turning this to "1" (true) will tell Net_LDAP_LDIF to write change sets instead of content files. | false |
lowercase |
Set this to true to convert attribute names to lowercase when writing. | 0 |
sort |
If true, sort attribute names when writing entries according to the rule: objectclass first then all other attributes alphabetically sorted by attribute name | 0 |
version |
Set the LDIF version to write to the resulting LDIF file. According to RFC 2849 currently the only legal value for this option is 1 currently. | 1 |
wrap |
Number of columns where output line wrapping shall occur. Setting it to 40 or lower inhibits wrapping. Useful for better human readability of the resulting file. | 78 |
For advanced users: instead of passing a file path, you also may pass an already initialized file handle. In this case, the mode parameter will be ignored. You may use this, if you want to mix LDIF content and LDIF change mode by using two Net_LDAP_LDIF instances to write to the same filehandle, but it could be very useful in other cases too. To initialize the second instance of Net_LDAP_LDIF, you can use handle() to get the filehandle from the first instance.
One of the two modes how Net_LDAP_LDIF can be used is to read a LDIF file and parse its contents into an array of Net_LDAP_Entry objects. This is done using the read_entry()-method which will return the next entry. If you want to fetch all entries, you use the eof() to detect the end of the input file:
Parsing a LDIF file into Net_LDAP_Entry objects
<?php
// open some LDIF file for reading
$ldif = new Net_LDAP_LDIF('somefile.ldif', 'r');
if ($ldif->error()) {
$error_o = $ldif->error(); // get Net_LDAP_Error object on error
die('ERROR: '.$error_o->getMessage());
}
// parse the entries of the LDIF file into objects
do {
$entry = $ldif->read_entry();
if ($ldif->error()) {
// in case of error, print error.
// here we use the shorthand parameter, so error()
// returns a string instead of a Net_LDAP_Object
die('ERROR AT INPUT LINE '.$ldif->error_lines().': '.$ldif->error(true));
} else {
// No error: do something with the entry
// Here we just print the entries DN
echo 'sucessfully parsed '.$entry->dn();
}
} while (!$ldif->eof());
// We should call done() once we are finished
$ldif->done();
?>
Writing an LDIF file is very easy too. Just pass the entries you want to have written to the write_entry()-method. Beware, that if you have opened the file in "w" write mode this will clear any previous data of that file. Use "a" (append) if you just want add data.
Writing entries
<?php
// Assume we have some valid Net_LDAP_Entry objects inside $entries
// $entries = array( ... );
// open some file for writing
$ldif = new Net_LDAP_LDIF('somewritefile.ldif', 'w');
if ($ldif->error()) die('ERROR: '.$error_o->getMessage());
// write the data and check for error
// you could pass one single Net_LDAP_Entry object or
// several objects inside an array
$ldif->write_entry($entries);
if ($ldif->error()) die('WRITE ERROR: '.$error_o->getMessage());
?>
The process of writing changes is exactly the same like writing entry contents. However there are two differences: Firstly you need to pass the "changes" option and secondly, the entries you want to write need changes. Entries not containing changes will silently be ignored since there is nothing to write.
Writing entry changes
<?php
// cast some test data and three entries
$testattrs = array(
'attr1' => '1234',
'attr2' => 'foo',
'attr3' => array('bar', 'baz')
);
$entries = array(
Net_LDAP_Entry::createFresh('cn=foo,dc=example,dc=cno',
array_merge(array('cn' => 'foo'), $testattrs)),
Net_LDAP_Entry::createFresh('cn=bar,dc=example,dc=cno',
array_merge(array('cn' => 'bar'), $testattrs)),
Net_LDAP_Entry::createFresh('cn=baz,dc=example,dc=cno',
array_merge(array('cn' => 'baz'), $testattrs))
);
// make some changes to the first and the last entry
$entries[0]->add(array('someattr' => 'added'));
$entries[0]->replace(array('attr1' => 'replaced'));
$entries[2]->delete(array('attr2'));
$entries[2]->delete(array('attr3' => 'bar'));
// open some file for writing, but in change mode
$ldif = new Net_LDAP_LDIF('somewritefile.ldif', 'w', array('change' => true));
if ($ldif->error()) die('ERROR: '.$error_o->getMessage());
// write the data and check for error
// you could pass one single Net_LDAP_Entry object or
// several objects inside an array
$ldif->write_entry($entries);
if ($ldif->error()) die('WRITE ERROR: '.$error_o->getMessage());
// Now, only two entries are contained in the LDIF file,
// cn=foo,dc=example,dc=cno and cn=baz,dc=example,dc=cno.
// cn=bar,dc=example,dc=cno had no changes and was skipped.
?>
Resulting LDIF change file
version: 1 dn: cn=foo,dc=example,dc=cno changetype: modify add: someattr someattr: added - replace: attr1 attr1: replaced - dn: cn=baz,dc=example,dc=cno changetype: modify delete: attr2 - delete: attr3 attr3: bar -
Sometimes you are interested in the lines inside the LDIF file. For those cases you can use the current_lines() and next_lines() methods. They work in the current context, which may be confusing: current_lines() will always return the lines that have built up the current Net_LDAP_Entry object when called current_entry() after read_entry() has been called. next_lines() will always return the lines, that will build up the next entry from the current point of view, meaning "relative to the entry that was just been read". However, you can override this by activating the "force" parameter of next_lines() which allows you to loop over all entries. current_entry() behaves exactly like current_lines().
If you think, that the lines you have read would be better in form of an Net_LDAP_Entry object, use the parseLines() method to parse those lines into an entry. This is a good way if you need just a few specific entries of a large LDIF file.
Reading LDIF lines
<?php
// open some LDIF file for reading
// (error checking code is ommitted in this example for
// better readability - in production, test for errors!)
$ldif = new Net_LDAP_LDIF('somefile.ldif', 'r');
// since nothing has been read until now, this will
// return an empty array
$empty_array = $ldif->current_lines();
// so let's read the first entries data
$first_entry_lines = $ldif->next_lines();
// if we call it again, we will not read ahead to the
// second entry - we again read the first one!
$first_entry_lines_again = $ldif->next_lines();
// If we call current_lines() now, we haven't read ahead
// like we learned from the last statement.
$empty_array_again = $ldif->current_lines();
// If we want to shift, we must use
// the read_entry() method, which will read ahead.
$first_entry = $ldif->read_entry();
// Now, current_lines() returns the lines of the
// first entry and next_lines() the lines of the second:
$first_entry_lines = $ldif->current_lines();
$second_entry_lines = $ldif->next_lines();
// There is another way to shift the lines which is faster if
// you are just interested in the LDIFs content - you
// need to pass the "force" parameter to next_lines():
$third_entry_lines = $ldif->next_lines(true);
$fourth_entry_lines = $ldif->next_lines(true);
$fifth_entry_lines = $ldif->next_lines(true);
// If you want to convert the lines to an Net_LDAP_Entry,
// you may do so anytime by using parseLines()
$fourth_entry = $ldif->parseLines($fourth_entry_lines);
// Since we shifted manually only the lines,
// current_lines() will return the lines that built up the
// last (e.g. the first entry) Net_LDAP_Entry object:
$first_entry_lines = $ldif->current_lines();
// If we decide to read the next entry, we can do that:
$sixth_entry = $ldif->read_entry();
// current_lines() is shifted now:
$sixth_entry_lines = $ldif->current_lines();
?>
Net_LDAP2 allows you to query and manipulate the data stored in directory servers using PHP in an object-oriented way.
Detailed usage examples are available at: /your/pear/path/docs/Net_LDAP2/examples/
Net_LDAP2 is a clone of Perls Net::LDAP package. PEAR Net_LDAP2 for PHP does, besides some own features, provide most of Perl Net::LDAP methods. Net_LDAP2 allows you to query and manipulate the data stored in directory servers using PHP in an object-oriented way. A directory server is a database server providing a hierarchical database and is usually queried using the LDAP protocol.
Net_LDAP2 is intendet as replacement of Net_LDAP.
The following table gives you a short overview which classes are available in Net_LDAP2 and what they can be used for. Their relations will be explained too.
Class name | Description |
---|---|
Net_LDAP2 | This is the main class. It enables you to connect and bind to a LDAP-server and to run ldap-querys like searching and manipulating entries. Most common, you will run a search using a LDAP-Filter and will get a Net_LDAP2_Search-object. You may also fetch an entry directly, which gives you a Net_LDAP2_Entry-object. |
Net_LDAP2_Search | Objects of this class are returned from search querys. You can use this object to retrieve informations about a search result, like how much entries you have found for the provided filter. You can retrieve the found entries as Net_LDAP2_Entry-objects in various forms: sorted, consecutively starting from the end or beginning or unsorted at once. |
Net_LDAP2_Entry | Objects of this kind are either produced by casting fresh entries manually, by retrieving the result of a LDAP-search or by fetching an entry directly. It gives you the possibility to read and/or manipulate the attributes of an entry which describes the characteristic of the specific object. |
Net_LDAP2_Util | The utility class contains only static methods, so you should not need to make an instance of it. It features some helpful methods, some of them are used internally by Net_LDAP2 but may be used externally from you as well. The most methods deal with escaping issues, since LDAP has some metacharacters with special meaning so that they usually need to be properly escaped. |
Net_LDAP2_Filter | You are free to give LDAP-Filters on your own to the Net_LDAP2->search() method, however this has some drawbacks (including escaping issues). For this reason, you can use the Net_LDAP2_Filter class to easily build and combine your filters. LDAP filters are extensively explained at the chapter LDAP filters. |
Net_LDAP2_Error | This is a error class. Most methods of Net_LDAP2 will return a object of this class if something went wrong. You can use this object to identify errors and to get detailed knowledge on what went wrong. See Errorhandling for more information. |
Net_LDAP2_LDIF | LDIF files are human readable, plain text files containing directory data and/or change commands, much like an SQL file. Unlike SQL files, it is data centric, not action centric. Net_LDAP2_LDIF enables you to convert between Net_LDAP2_Entry-objects and LDIF files. Please note, that Net_LDAP2_LDIF has a little different error handling explained later. |
Net_LDAP2_SimpleFileSchemaCache | Net_LDAP2 features a schema caching facility. This class implements a simple file based cache that allows Net_LDAP2 to store the schema data that get fetched from LDAP inside a file to accelerate schema access. Caching the schema can gain some performance, especially with slow servers or connections. You may use this cache class for example to store the schema object in a linux tmpfs which will result in caching the schema in the computers memory, enabling nearly instant access. This cache also features a cache ageing mechanism. Please see Schema caching for more information. |
Nearly all of Net_LDAPs methods return a Net_LDAP2_Error object if something went wrong. You always should check for errors after you performed an action to be sure that your application doesn't do things you don't want it to do.
Handling errors is an easy task, you just have to test the return value as shown below. If an error occured, you can halt the script for example. In other cases, you may just log the error, but what exactly happens depends on your specific situation, of course.
You can use the getMessage() method of the error object to retrieve the error message explaining the problem and getCode() to get the error code which is usually the LDAP-Error code (see Table below) and may be used for automated reaction on errors.
Dealing with errors
<?php
// Perform an arbitrary action:
$result = $ldap->search($searchbase, $filter, $options);
// Check, if an error occured and do something.
// Here we use die() to show the message of the error.
if (Net_LDAP2::isError($result)) {
die($result->getMessage());
}
?>
Error code | Description |
---|---|
0x00 | LDAP_SUCCESS |
0x01 | LDAP_OPERATIONS_ERROR |
0x02 | LDAP_PROTOCOL_ERROR |
0x03 | LDAP_TIMELIMIT_EXCEEDED |
0x04 | LDAP_SIZELIMIT_EXCEEDED |
0x05 | LDAP_COMPARE_FALSE |
0x06 | LDAP_COMPARE_TRUE |
0x07 | LDAP_AUTH_METHOD_NOT_SUPPORTED |
0x08 | LDAP_STRONG_AUTH_REQUIRED |
0x09 | LDAP_PARTIAL_RESULTS |
0x0a | LDAP_REFERRAL |
0x0b | LDAP_ADMINLIMIT_EXCEEDED |
0x0c | LDAP_UNAVAILABLE_CRITICAL_EXTENSION |
0x0d | LDAP_CONFIDENTIALITY_REQUIRED |
0x0e | LDAP_SASL_BIND_INPROGRESS |
0x10 | LDAP_NO_SUCH_ATTRIBUTE |
0x11 | LDAP_UNDEFINED_TYPE |
0x12 | LDAP_INAPPROPRIATE_MATCHING |
0x13 | LDAP_CONSTRAINT_VIOLATION |
0x14 | LDAP_TYPE_OR_VALUE_EXISTS |
0x15 | LDAP_INVALID_SYNTAX |
0x20 | LDAP_NO_SUCH_OBJECT |
0x21 | LDAP_ALIAS_PROBLEM |
0x22 | LDAP_INVALID_DN_SYNTAX |
0x23 | LDAP_IS_LEAF |
0x24 | LDAP_ALIAS_DEREF_PROBLEM |
0x30 | LDAP_INAPPROPRIATE_AUTH |
0x31 | LDAP_INVALID_CREDENTIALS |
0x32 | LDAP_INSUFFICIENT_ACCESS |
0x33 | LDAP_BUSY |
0x34 | LDAP_UNAVAILABLE |
0x35 | LDAP_UNWILLING_TO_PERFORM |
0x36 | LDAP_LOOP_DETECT |
0x3C | LDAP_SORT_CONTROL_MISSING |
0x3D | LDAP_INDEX_RANGE_ERROR |
0x40 | LDAP_NAMING_VIOLATION |
0x41 | LDAP_OBJECT_CLASS_VIOLATION |
0x42 | LDAP_NOT_ALLOWED_ON_NONLEAF |
0x43 | LDAP_NOT_ALLOWED_ON_RDN |
0x44 | LDAP_ALREADY_EXISTS |
0x45 | LDAP_NO_OBJECT_CLASS_MODS |
0x46 | LDAP_RESULTS_TOO_LARGE |
0x47 | LDAP_AFFECTS_MULTIPLE_DSAS |
0x50 | LDAP_OTHER |
0x51 | LDAP_SERVER_DOWN |
0x52 | LDAP_LOCAL_ERROR |
0x53 | LDAP_ENCODING_ERROR |
0x54 | LDAP_DECODING_ERROR |
0x55 | LDAP_TIMEOUT |
0x56 | LDAP_AUTH_UNKNOWN |
0x57 | LDAP_FILTER_ERROR |
0x58 | LDAP_USER_CANCELLED |
0x59 | LDAP_PARAM_ERROR |
0x5a | LDAP_NO_MEMORY |
0x5b | LDAP_CONNECT_ERROR |
0x5c | LDAP_NOT_SUPPORTED |
0x5d | LDAP_CONTROL_NOT_FOUND |
0x5e | LDAP_NO_RESULTS_RETURNED |
0x5f | LDAP_MORE_RESULTS_TO_RETURN |
0x60 | LDAP_CLIENT_LOOP |
0x61 | LDAP_REFERRAL_LIMIT_EXCEEDED |
1000 | Unknown Net_LDAP2 Error |
To connect to an LDAP server, you should use Net_LDAP2's static connect() method. It takes one parameter, an array full of configuration options, and either returns a Net_LDAP2 object if connecting works, or a Net_LDAP2_Error object in case of a failure.
The following table lists all configuration options. If the default value for an option fits your needs, you don't need add it to your configuration array.
Name | Description | Default |
---|---|---|
host |
LDAP server name to connect to. You can provide several hosts in an array in which case the hosts are tried from left to right. | localhost |
port |
Port on the server | 389 |
version |
LDAP version | 3 |
starttls |
TLS is started after connecting | false |
binddn |
The distinguished name to bind as (username). If you don't supply this, an anonymous bind will be established. | (none) |
bindpw |
Password for the binddn . If the credentials are wrong, the bind will fail server-side and an
anonymous bind will be established instead. An empty bindpw string requests an unauthenticated bind. This can cause
security problems in your application, if you rely on a bind to make security decisions (see
RFC-4513, section 6.3.1). |
(none) |
basedn |
LDAP base name (root directory) | (none) |
options |
Array of additional ldap options as key-value pairs | array() |
filter |
Default search filter (string or preferably Net_LDAP2_Filter object). See LDAP filters | (objectClass=*) |
scope |
Default search scope, see Search | sub |
Connecting to an LDAP server
<?php
// Inclusion of the Net_LDAP2 package:
require_once 'Net/LDAP2.php';
// The configuration array:
$config = array (
'binddn' => 'cn=admin,ou=users,dc=example,dc=org',
'bindpw' => 'password',
'basedn' => 'dc=example,dc=org',
'host' => 'ldap.example.org'
);
// Connecting using the configuration:
$ldap = Net_LDAP2::connect($config);
// Testing for connection error
if (PEAR::isError($ldap)) {
die('Could not connect to LDAP-server: '.$ldap->getMessage());
}
?>
It may be possible that restricted characters (",", "+", """, "\", "<", ">", ";", "#", "=", space or a hexpair) are used in attributes or values inside the DN. You should have a look to the APIdoc of Net_LDAP2_Util::escape_dn_value(), Net_LDAP2_Util::unescape_dn_value(), Net_LDAP2_Util::ldap_explode_dn() and Net_LDAP2_Util::canonical_dn(). These functions can be used to safely handle DNs.
After connecting to the server, you can use Net_LDAP2's search() method to search the directory. The method takes three parameters:
$base
is the base search DN. If kept
null
, the default base DN configured when connecting
is used.
$filter
is the query filter that determines which
results are returned. It is either a string (experts use only) or better a Net_LDAP2_Filter-object.
Net_LDAP2_Filter automatically deals with LDAP-Filter escaping issues.
LDAP filters are extensively explained at the chapter LDAP filters.
$params
is an array of configuration options for
the current query.
Name | Description | Default |
---|---|---|
scope |
The scope used for searching:
|
sub |
sizelimit |
Number of entries returned at maximum | 0 (no limit) |
timelimit |
Seconds to spent for searching | 0 (no limit) |
attrsonly |
If true , only attribute names are returned |
false |
attributes |
Array of attribute names, which the entry should contain. It is good practice to limit this to just the ones you need. | array() (all attributes) |
The search() method will return either a Net_LDAP2_Search object or a Net_LDAP2_Error. You can use the Net_LDAP2_Search-object to trigger further actions like counting how many entries where found or to retrieve the found entries.
Making a search query
<?php
// Building a very basic filter
// we want to find all Entries whose surnames start with "Joe":
$filter = Net_LDAP2_Filter::create('sn', 'begins', 'Joe');
// We define a custom searchbase here. If you pass NULL, the basedn provided
// in the Net_LDAP2 configuration will be used. This is often not what you want.
$searchbase = 'ou=addressbook,dc=example,dc=org';
// Some options:
// We search all subtrees beneath 'ou=addressbook,dc=example,dc=org'
// and we select the attribute 'sn'. It is a good practice to limit the
// requested attributes to only those you actually want to use later.
// However, note that it is faster to select unneeded attributes than
// refetching an entry later to just get those attributes.
$options = array(
'scope' => 'sub',
'attributes' => array('sn')
);
// Perform the search!
$search = $ldap->search($searchbase, $filter, $options);
// Test for search errors:
if (PEAR::isError($search)) {
die($search->getMessage() . "\n");
}
// Say how many entries we have found:
echo "Found " . $search->count() . " entries!";
?>
You can retrieve directory entries in several ways, either directly or from a performed search request. If you want to fetch an entry directly, you need to know its absolute distinguished name (DN). To directly fetch an known entry from the directory server, you use Net_LDAP2's getEntry() method. It takes two parameters: The DN of the entry and the attributes you want to read from the entry. It returns a Net_LDAP2_Entry object if fetching worked, or a Net_LDAP2_Error object in case of a failure.
You may also check if the entry exists in the server before you fetch it. This can be achieved by Net_LDAP2's dnExists() which takes the DN to test and returns either true or false.
Fetching an entry directly
<?php
// Defining the DN we want to fetch;
// we want to select the given- and the surname
$dn = 'cn=admin,o=example,dc=org';
$entry = $ldap->getEntry($dn, array('gn', 'sn'));
// Error checking is important!
if (Net_LDAP2::isError($entry)) {
die('Could not fetch entry: '.$entry->getMessage());
}
?>
The second way to retrieve entries is from a searchresult. As described in chapter "Search", you access the entries of a search result through the Net_LDAP2_Search-object resulting from Net_LDAP2's search() method. Each of the following methods return a Net_LDAP2_Error-object if something goes wrong, so remember to test for errors! You have several ways to read the entries:
Method of Net_LDAP2_Search | Description |
---|---|
entries() | This returns the entries at once unsorted. |
as_struct() | This returns all entries as multidimensional array instead of Net_LDAP2_Entry-objects. The array keys of the first dimension are the DNs and the value is an array containing all attributes. The array keys of the second level are the attributes names; the value of the second level is an array containing all the attributes values. Note, that even if there are no or just one value, an array is present. |
sorted() | Use this if you want to get the entries at once but sorted. You can sort by several attributes which can contain multiple values.
You can of course sort ascending (default) or descending - just pass the PHP constant SORT_ASC or SORT_DESC as second parameter. |
sorted_as_struct() | Like as_struct(), this returns the entries as multidimensional array, but in this case sorted. For parameters, see sorted() |
shiftEntry() | This returns one entry from the beginning of the search result.
Since this returns FALSE if all entries are fetched, shiftEntry() is perfectly
appropriate to get used inside a while-loop.
Take care not to mix shiftEntry() and popEntry()! |
popEntry() | Exactly the same as shiftEntry, but returns the entry from the end of the searchresult. Again, be sure to not mix shiftEntry() and popEntry()! |
To directly fetch an known entry from the directory server, you use Net_LDAP2's getEntry() method. It takes two parameters: The DN of the entry and the attributes you want to read from the entry. It returns a Net_LDAP2_Entry object if fetching worked, or a Net_LDAP2_Error object in case of a failure.
You may also check if the entry exists in the server before you fetch it. This can be achieved by Net_LDAP2's dnExists() which takes the DN to test and returns either true or false.
Fetching all entries from searchresult
<?php
// return all entries:
$entry = $search->entries();
?>
Fetching all entries from searchresult: sorted
<?php
// return sorted by first 'sn' then 'gn', but descending:
$entry = $search->sorted(array('sn', 'gn'), SORT_DESC);
?>
Fetching entries one by one inside a while loop
<?php
// return entries one by one:
while ( $entry = $search->shiftEntry() ) {
// do something, like printing the DN of the entry;
// in a real case, dont forget to test for errors!
echo "ENTRY: " . $entry->dn();
}
?>
Since Net_LDAP2 you are able to use PHPs Standard Library (SPL) to iterate over search results. This is done easily by just using the Net_LDAP2_Search search result object inside an foreach loop similar to an array. You may optionally retrieve the DN of each entry by the same mechanism you use to retrieve the key of an associative array.
Fetching entries via foreach()
<?php
foreach ($search as $dn => $entry) {
// do something:
$sn = $entry->getValue('sn', 'single');
echo "Fetched DN: $dn; Surname: $sn";
}
?>
Reading attribute values depends on the selection of those attributes at search time. You can only access attributes that where selected! You can read attribute values using either Net_LDAP2_Entry's getValues() or getValue() method. getValue() will return an array where the keys are the attributes names. If you use getValues() you may pass an option:
'single'
: only the first value is returned as string
'all'
: all values including the value count are returned in an array
'default'
: in all other cases an attribute value with a single value is
returned as string, if it has multiple values it is returned
as an array (without value count)
Also note that if you try to fetch an attribute, that is not set at the entry, an empty string will be returned.
Reading attributes
<?php
// read Surename, singlevalued
$surename = $entry->getValue('sn', 'single');
// read mail adress which may be multivalued
$mail = $entry->getValue('mail', 'all');
?>
If you want to read the distinguished name of an Entry (DN), you must use a different method: dn()
Reading an entries DN
<?php
$dn = $entry->dn();
?>
PEAR::Net_LDAP2 has the unique feature to apply a regular expression match directly against attributes, so you do not need to manually fetch all values and run the regex against them. Instead, you can use Net_LDAP2_Entry's preg_match() function. The behavior of this function is the same as PHPs preg_match(), but the $matches array is slightly different. It features one dimension more, since it may match for several attribute values if the attribute is multivalued. If you pass $matches, be sure to do it via REFERENCE, because otherwise $matches remains empty. preg_match() returns true or false, depending on match.
Performing preg_match on attribute values
<?php
// Look, if the user has an emailadress for 'example', if so,
// we want to display the tld:
// (be sure to pass $matches as reference!)
$matches = array();
if ( $entry->preg_match('mail', '/example\.(.+)/', &$matches) ) {
// print every TLD found for 'example':
foreach ($matches as $match) {
echo $match[1];
}
}
?>
It is important to know how attribute changing works. Modifications to an entry
through the Net_LDAP2_Entry-object are local only.
After you have made all changes and want to transfer them to the directory server, you must
call update() of the Net_LDAP2_Entry object.
This will return either TRUE
or an Net_LDAP2_Error.
Another good information is, that you must select attributes at search time
if you want to add/change/delete attribute values. Otherwise Net_LDAP2 will most likely fail
silently giving you the wrong assumtion that everything was okay - Net_LDAP2 needs knowledge
of the attributes it should work with!
Modification of attributes is also possible through Net_LDAP2's modify() method. This method will call the methods described here on the Net_LDAP2_Entry object given, and directly calls an update() after that, thus performing the changes directly on the server. The parameter is an complex array describing the changes to be performed. It is considered for more advanced users, because it is more compact, so please refer to the latest API documentation for more information.
Adding attrbiute values to an entry is an easy task. You just need to call add()! The parameter is an array whose keys are the attribute names and values the attributes values. If only one attribute value should be added, the second level may be a string. If the attribute doesn't exist so far, it will be added, if it exists, the attributes values will be added.
Adding attributes
<?php
// Adding several attributes:
$result = $entry->add(
array(
'sn' => 'Doe',
'gn' => array('John'),
'mail' => array('john@doe.org', 'j.doe@example.org')
)
);
?>
Changing values is with the replace() method as easy as adding values. However, you have to be a little more careful.
The expected parameter is an array describing the new absolute state of the named
attributes. This means, if you specify a NULL
value for an attribute,
this attribute will get deleted!
You may specify single values as string too.
The keys of the array are expected to be the attributes names.
Changing attributes
<?php
// Changing several attributes:
// 'sn' is changed to "Smith", 'gn' gets deleted and mail will
// be changed to te two new adresses
$result = $entry->replace(
array(
'sn' => 'Smith',
'gn' => null,
'mail' => array('smith@example.org', 'smith@example.de')
)
);
?>
Using the delete() method you are able to delete specific attributes values as well
as delete a whole attribute.
You need to specify the attribute names as array keys, the array values are the values you want to delete.
If you want to delete whole attributes, specify them as single level array.
Special care must be taken not to delete the whole entry which will be the case if the parameter array is
omitted or set to NULL
!
Also, don't mix syntax modes. If you want to delete whole attributes you can't delete specific values from another attribute
in the same function call.
Deleting attributes
<?php
// Delete the whole entry:
$result = $entry->delete();
// Delete the whole telephone number attribute:
$result = $entry->delete('telephoneNumber');
// Delete one specific mail attributes value:
$result = $entry->delete( array('mail' => 'j.doe@example.org') );
// Delete mail and telephone attributes as a whole:
$result = $entry->delete( array('mail', 'telephoneNumber') );
// Delete two specific mail adresses:
$result = $entry->delete( array('mail' => array('smith@example.org', 'smith@example.de')) );
?>
Object classes describe the attribute set of an entry with this objectclass set. The entry stores the objectclass in a special attribute named "objectClass", and of course you may alter that attribute like any other attribute.
However, special care must be taken if changing this attribute since most directory servers impose rules on the other attributes the object class define. For example, it is usually not possible to delete an objectclass if some of the attributes the class describes are still in use by the entry. This should be not much of a problem with optional attributes, but sometimes objectclasses have mandatory attributes set. Also structural objectclasses can only be added when creating new entrys. Because of the internal architecture of Net_LDAP2 it is currently not possible to resolve those cases.
To add or remove objectclasses with mandatory attributes or new structural object classes, you need to delete the old entry from the directory server and add the new one with the new objectclass and attributes as fresh entry.
Changing complex objectclasses
<?php
// Let's assume that the objectclass myClass enforce the attribute "fooattr"
// Take care that you have all attributes requested, otherwise the new
// entry will not have all attributes set!
$entry->add(array(
'objectClass' => 'myClass',
'fooatrr' => 'foo',
'someotherattr' => array('bar', 'baz')
));
// Calling $entry->update() now will not succeed under some circumstances!
// We construct a fresh entry object which is in fact a copy of the already
// existing entry with all changes already applied (the local copy).
// It is important, that at fetching time of $entry all attributes where selected!
// Only the selected attributes will get copied.
$changed_entry = Net_LDAP2_Entry::createFresh($entry->dn(), $entry->getValues());
// Now delete the old entry and add the new one:
$ldap->delete($entry);
$ldap->add($changed_entry);
?>
When operating on an LDAP connection, you might want to retrieve informations regarding the directory servers schema. Often this is the case to verify that your program only querys attributes that are valid for an entry or to ensure that you only try to write such attributes to the server.
To get that inforamtion, you can use the Net_LDAP2_Schema which is retrieved via the Net_LDAP2 object. It allows you to perform various querys, not only on attributes and object classes, but also on DIT content rules, for example. For often needed functionality, shorthand methods are implemented since version 2.0.10 like attributeExists(), objectClassExists(), getAssignedOCLs() and checkAttribute().
Performing basic schema checks
<?php
// Fetch the schema object for the connected directory server.
$schema = $ldap->schema();
// this may have failed since not every server allows us
// to fetch the schema without permission. Also technical
// problems may prevent us from this.
if ( Net_LDAP2::isError($schema) ) {
die('SCHEMA ERROR: '.$schema->getMessage()."\n");
}
// lets see, if an attribute is defined in the schema:
if ( $schema->attributeExists('myCoolAttribute') ) {
echo "Attribute 'myCoolAttribute' is defined in the schema!";
}
// lets see, if an object class is defined in the schema:
if ( $schema->attributeExists('myCoolOCL') ) {
echo "Object class 'myCoolOCL' is defined in the schema!";
}
// Check, if the attribute is defined in objectClasses.
// This is especially useful if you want to know if
// attributes are valid for a given set of object classes.
if ( $schema->checkAttribute('myCoolAttribute', array('person', 'myCoolOCL')) ) {
echo "Attribute 'myCoolAttribute' is defined for the given OCLs!";
}
?>
Adding new entries is performed in two ways. First, you need to establish a fresh Net_LDAP2_Entry object. After that, you can add that entry like you would add already-existent entries using Net_LDAP2's add() method.
Adding a fresh entry
<?php
// Build a new fresh entry:
$dn = 'cn=new-admin,o=example,dc=org';
$attributes = array(
'cn' => 'new-admin',
'mail' => array('new-admin@exaple.org', 'n.admin@example.de'),
'telephoneNumber' => '1234567890'
);
$entry = Net_LDAP2_Entry::createFresh($dn, $attributes);
// Add the entry to the directory:
$ldap->add($entry);
?>
Renaming and/or moving an entry is an operation on the DN of an entry. Moving an entry means, to rename a DN in such a way, that the entry becomes a new base-DN. You can rename or move an entry, if you call the dn() method of the entry you want to relocate. Alternatively, you may call Net_LDAP2's move() method that also ca handle only DNs. Remember that you must call the entires update() method to carry out the move/rename. Net_LDAP2's move() will move the entry immediately. If you use an entryobject togehter with Net_LDAP2's move(), you are able to perform cross directory moves.
Moving an entry using Net_LDAP2_Entry
<?php
// Defining the DN we want to fetch;
$dn = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin,o=new-example,dc=org';
$entry = $ldap->getEntry($dn);
$entry->dn($newdn);
?>
Moving an entry using Net_LDAP2 and Net_LDAP2_Entry
<?php
// Defining the DN we want to fetch;
$dn = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin,o=new-example,dc=org';
$entry = $ldap->getEntry($dn);
$ldap->move($entry, $newdn);
?>
Renaming an entry using Net_LDAP2 and DNs
<?php
// Defining the DN we want to fetch;
$dn = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin2,o=new-example,dc=org';
$ldap->move($dn, $newdn);
?>
Performing a cross directory move
<?php
// $ldap_src is the source ldap and $ldap_tgt the target
$dn = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin,o=new-example,dc=org';
$entry = $ldap_src->getEntry($dn);
$ldap_src->move($entry, $newdn, $ldap_tgt);
?>
Deleting entries is performed using Net_LDAP2's delete() method.
Just pass the Net_LDAP2_Entry object or the DN of the entry you want to delete.
In the case that the DN contains subentrys, you need to pass TRUE
as second parameter
which will make delete() delete recursive.
A second way exist: You may simply call delete() from the Net_LDAP2_Entry you want to delete. Don't forget that you must call update() to carry out the delete in this case.
Deleting an entry
<?php
$dn = 'cn=new-admin,o=example,dc=org';
$ldap->delete($dn);
?>
Deleting an entry using a Net_LDAP2_Entry object
<?php
$entry->delete();
$entry->update();
?>
LDAP filters usually serve as parameter to a LDAP Search request.
LDAP filters are defined in RFC 2254 and can be compared to the WHERE clause in SQL select statements - they filter the data returned from some search request - in this case the entries returned from the directory server. With Net_LDAP2, you may use plain strings as filters, or preferably, the Net_LDAP2_Filter class which mostly releases you of the burden to escape yourself and to remember all the various special characters needed for constructing and combining filters.
Although you should preferably use the Net_LDAP2_Filter class to construct your LDAP filters, some theory may be interesting and helpful in understanding how to construct LDAP filters and what they are capable of.
Basic LDAP filters are composed of an "[attribute][operator][value]" pair enclosed by round brackets. There are several comparison operators available: "=" (equal), ">" (greater), "<" (less), ">=" (greater or equal), "<=" (less or equal) and "=~" (phonetical similar). The exact match behavior is defined by the attribute syntax of the attribute to which the filter should apply.
Some basic string filters
<?php
// Filter entries whose first name is 'Benedikt':
$filter = '(givenName=Benedikt)';
// Filter entries which have an employeenumber higher or equal than 1424:
$filter = '(employeeNumber>=1424)';
// Filter entries whose first name sounds similar to "Stephane"
// This should also find "Stephen" and "Stefan" (depending on implementation)
$filter = '(givenName~=Stephane)';
?>
The value part of the basic filter construct could also include a special character: "*". The star acts as placeholder for none, one or several characters at that position. "V*lue" would therefore match against "Value", "Vlue", "VaaAaAalue" and so on. There are some special named combinations using the star, but they work exactly the same way:
Name | Filter | Description |
---|---|---|
present | (attr=*) | Also refered to as "any". Finds any entry containing any (unless empty) value for the named attribute. |
begins | (attr=value*) | value starts with some fixed string |
ends | (attr=*value) | value ends with some fixed string |
contains | (attr=*value*) | value contains some fixed string |
Basic filters can be combined using the three logical operators "&" (and), "|" (or) and "!" (not). Note, that the smallest filter component, the basic filter enclosed in round brackets, remains isolated: instead of just adding another "[attribute][operator][value]" pair into the brackets, a new bracket level is introduced that contains all filter components that should be combined. Note also, that the logical operator does stand in front of all filter components, not between them as common in programming languages.
Combining string filters
<?php
// Search all 'Benedikt's with phone number 1234567890
$filter = '(&(givenName=Benedikt)(telephoneNumber=1234567890))';
// Search the same, but exclude person "Benedikt Foobar"
// Note that the "not" is a logical operator and thus needs its own
// surrounding bracket. This explains nicely, that each bracket level
// is evaluated independently from surrounding brackets.
$filter = '(&(givenName=Benedikt)(telephoneNumber=1234567890)(!(sureName=Foobar)))';
?>
As you will read below, there are some special characters inside the LDAP filter definition and thus must be escaped. These are mainly the special characters used directly by the filter syntax like braces and the logical operators. Despite those, there are some other cases which need special threatment. Nearly all of this cases are hidden through the Net_LDAP2_Filter class so you should only consider using string filters if you need to or you know what you are doing. If you need a filter string, you may also use Net_LDAP2_Filters asString() function after building the filter.
The filter class has two different usage models: one for constructing basic filters and another to combine them logically. This has to do with the syntax of LDAP filters you may read below.
For creating basic filter components, you need to use the create() factory method.
There, you combine three items: an attribute to filter for, a matching rule for comparison and a value
that is beeing compared with the servers entries.
The given value is automatically escaped, so you need take care if you want to use the star placeholder.
In this case, you need to pass FALSE
as fourth parameter to create()
which causes value
to be threaten as-is. This of course also means, that you need to
escape the parts of the value that may contain restricted characters yourself using
Net_LDAP2_Util::escape_filter_value(). To learn what characters
are restricted, refer to RFC 2254 or the documentation of
Net_LDAP2_Util::escape_filter_value(); otherwise its safe to always escape.
The matching rules partly follow the basic filter matching rules described above, but are enhanced to make your life easier:
Rule | Description |
---|---|
equals | One of attribute s values is exactly value . Please note that case sensitiviness depends on the matching rule defined in the attributes schema syntax. |
begins | One of attribute s values must begin with value |
ends | One of attribute s values must end with value |
contains | One of attribute s values must contain value |
present | any | The attribute can contain any value but must be existent |
greater | The attribute s value is greater than value |
less | The attribute s value is less than value |
greaterOrEqual | The attribute s value is greater or equal than value |
lessOrEqual | The attribute s value is less or equal than value |
approx | One of attribute s values sounds similar to value . The matching behavior depends on the server implementation. |
Since Net_LDAP2 2.0.12 you can also negate the match rules by using the not
keyword
to easily negate the basic filter expression. Prior to that, you had to use combine()
manually.
Creating LDAP filters
<?php
// Filter entries whose first name is 'Benedikt':
$filter = Net_LDAP2_Filter::create('givenName', 'equals', 'Benedikt');
// Filter entries whose first name is NOT 'Benedikt':
// (this was first introduced in 2.0.12)
$filter = Net_LDAP2_Filter::create('givenName', 'not equals', 'Benedikt');
$filter = Net_LDAP2_Filter::create('givenName', '! =', 'Benedikt'); // works too :)
// Filter entries whose first name starts with 'Steph':
$filter = Net_LDAP2_Filter::create('givenName', 'begins', 'Steph');
// Filter entries containing 'Lone*'; matching the star character.
// The automatic escaping of $value will conveniently escape the star for us.
$filter = Net_LDAP2_Filter::create('givenName', 'contains', 'Lone*');
// Filter entries containing 'Foo[something]Bar'; not matching the star character.
// For this to work, we need to disable automatic escaping of $value by passing
// false as fourth parameter. This however implies, that we take care of
// proper escaping, which is showed in the example.
$escaped_values = Net_LDAP2_Util::escape_filter_value(array('Foo', 'Bar'));
$foo =& $escaped_values[0];
$bar =& $escaped_values[1];
$filter = Net_LDAP2_Filter::create('givenName', 'contains', "$foo*$bar", false);
// Filter entries whose first name sounds similar to "Stephane"
// This should also find "Stephen" and "Stefan" (depending on implementation)
$filter = '(givenName=~Stephane)';
?>
Although the filters can be used stand alone, they can be combined to match sophisticated
search requiremets. This is done by using the combine() to combine
several present Net_LDAP2_Filter objects using a logical operator.
The execption is the not
operator since it only allows one filter
object to be negated.
Rule | Description |
---|---|
and | All filter components must evaluate to true for the combined filter to be true |
or | At least one filter component must evaluate to true for the combined filter to be true |
not | The result of the filter component is inversed (true becomes false and vice versa).
Note that this operator only accepts one filter object. |
Combining LDAP filters
<?php
// Create some test filters
$filter_benedikt = Net_LDAP2_Filter::create('givenName', 'equals', 'Benedikt');
$filter_steph = Net_LDAP2_Filter::create('givenName', 'begins', 'Steph');
$filter_foobar = Net_LDAP2_Filter::create('sureName', 'equals', 'Foobar');
$filter_height = Net_LDAP2_Filter::create('personHeight', 'greater', '175');
// Negate 'foobar' filter.
// This filters every entry whose sure name is not 'Foobar'
$filter_not_foobar = Net_LDAP2_Filter::combine('not', $filter_foobar);
// Build a 'and' combination to be able to search for people whose
// first names start with 'Steph' and who are are taller than 175
// except those whose surname is 'Foobar'
$filter_stephs_tall = Net_LDAP2_Filter::combine('and',
array($filter_steph, $filter_height, $filter_not_foobar));
// In any case, add every person whose first name is
// 'Benedikt' to the search result.
$filter_add_benedikt = Net_LDAP2_Filter::combine('or', array($filter_benedikt, $filter_stephs_tall));
?>
Since version 2.1.0 the filter class can filter entries client side. Just pass an entry or an array of entries to the filter objects matches() method. With this you can check that an given entry matches some LDAP filter as well as select matching entries out of an array containing many of them. The method always returns the number of matched entries. If you want to retrieve the filtered entries you have to provide some result array to which the matching entries are appended.
Feature | Description | Since |
---|---|---|
Matching Operators: equals , begins , ends , contains , present|any | Matching of the basic "=" filter argument with wildcards in value. As of 2.1.0, the match performed is not case sensitive (this will change once schema attribute syntax is honored). | 2.1.0 |
Matching Operators: greater , greaterOrEqual , less , lessOrEqual , approx | Comparisons and approximate literal matching | unsupported |
Matching rules of attribute syntax | Using of the matching rule of the schema attribute syntax (eg. date/time comparisons, case sensitiveness, etc) | unsupported |
Logical Operators: AND , OR , NOT | Combining filters for more advanced expressions. | 2.1.0 |
Client side filter matching
<?php
// Create a simple test filter (may be arbitary complex and combined)
$filter = Net_LDAP_Filter::create('givenName', 'equals', 'Benedikt');
// see if the filter matches a given Entry
// (note that you should check for Net_LDAP2_Error)
$matches_count = $filter->matches($singleEntry);
// $matches_count contains the number of matched entries, here 0 or 1.
// see, if the filter matches some entries of a given array of entries
$matches_count = $filter->matches($arrayOfEntries);
// $matches_count contains again the number of matched entries, here 0 or n.
// what entries do match exactly?
$filteredEntries = array(); // provide a results array (doesn't have to be empty)
$matches_count = $filter->matches($arrayOfEntries, $filteredEntries);
// $filteredEntries array contains now all matched entries, eg a filtered result.
?>
You may need some advanced functionality if you have to deal with string representation of filters.
Method | Description |
---|---|
parse() | Takes an filter string and parses it into a Net_LDAP2_Filter object. It also verifies, that the filter syntax is correct. |
printMe() | In PERLs interface, this method is called "print" but due to language constraints, we cannot use that name. Prints the string representation of this filter object to standard output or to an optional filehandle passed as parameter. |
asString() | Returns the string representation of the filter object. |
LDIF support was added to Net_LDAP2 in release 1.1.0a1.
LDIF files are in detail described at RFC 2849. Shortly, they contain data records in an plain text, human readable format, much like a SQL file does. An LDIF file specifies a set of directory entries, or a set of changes to be applied to directory entries, but not both at the same time: the formats cannot be mixed inside the same file. LDIF-content files are very usable for manual transporting data or for full backups. LDIF-change files are an easy way to perform adjustments to a database like automated data synchonisation. LDIF files can contain comments (first character on line is a hash "#") which are very useful in case humans need to interpret or read the data.
Example LDIF content file
# # This is a content LDIF file. # It contains one single entry featuring several # attributes and one comment (this one). # attr1, attr4 and cn are single valued, the others are # multivalued. objectclass is a special case, LDAP servers # will interpret this operational attribute to define the # classes the object will belong to and thus, which attributes # it may contain. the OCL-attribute is usually multivalued. # version: 1 dn: cn=test1,ou=example,dc=cno objectclass: someobjectclass attr1: 12345 attr2: 1234 attr2: baz attr3: foo attr3: bar attr4: brrrzztt cn: test1
LDIF files could describe not only the data an entry contains, but also various changes to the entry itself. If such an LDIF file would then be given to a LDAP server, he would interpret those changes instead just importing the data. It is comparable to a diff file on unix and familiar to the SQL dump.
Example LDIF change file
# Delete attr1, replace values of attr2 and add new attribute attr42 # The attribute "changetype" is special: it says, what to do with # this entries dataset. It could also be "delete" or "add" to delete # a whole entry or to add a completely fresh one. "modrdn" will # move the entry to a new location once the LDIF file is imported. dn: cn=test2,ou=example,dc=cno changetype: modify delete: attr1 - replace: attr2 attr2: 123456_newtest - add: attr42 attr42: the answer
Before we can start using Net_LDAP2_LDIF we must say some short words about how error handling works. Net_LDAP2_LDIF was designed to have mostly the same API as the original PERL Net::LDAP::LDIF has. Because of this, the methods of Net_LDAP2_LDIF do not return a Net_LDAP2_Error object. You must use the error() method that will return a Net_LDAP2_Error object in case of failure or true in case everything was ok. In LDIF reading mode, you can additionally use error_lines() to get knowledge about where in the input file the error occured.
Regardless if you want to read or write a LDIF file, you always have to use the constructor of Net_LDAP2_LDIF to initialize your access to the LDIF file. You need to pass at least one parameter to Net_LDAP2_LDIF(): the path of the file that should be read or written. You may pass the open mode as second parameter. The possible file open modes are "r" (read), "w" (write, clears the file first) and "a" (append to the end). In case you omit the open mode, read mode is assumed. The third optional parameter is an associative array containing one or several of the following options:
Name | Description | Default |
---|---|---|
encode |
Some DN values in LDIF cannot be written verbatim and have to be encoded in some way. Possible values are: "none", "canonical" and "base64" (RFC default) | base64 |
onerror |
What should be done on errors? "undef" will let error handling in your hands, in this case you use error() and error_lines() to process errors manually. "die" aborts the script printing the error - this is sometimes useful for CLI scripts. "warn" just prints out the error but continues like "undef" would. | undef |
change |
Turning this to "1" (true) will tell Net_LDAP2_LDIF to write change sets instead of content files. | false |
lowercase |
Set this to true to convert attribute names to lowercase when writing. | 0 |
sort |
If true, sort attribute names when writing entries according to the rule: objectclass first then all other attributes alphabetically sorted by attribute name | 0 |
version |
Set the LDIF version to write to the resulting LDIF file. According to RFC 2849 currently the only legal value for this option is 1. | 1 |
wrap |
Number of columns where output line wrapping shall occur. Setting it to 40 or lower inhibits wrapping. Useful for better human readability of the resulting file. | 78 |
raw |
Using this option, you are able to tell Net_LDAP2_LDIF which attributes to treat as binary data. If you pass in entries having a valid LDAP connection (eg from some Net_LDAP2->search() operation) this additionally will be detected by automatic checks against the schema. | empty |
For advanced users: instead of passing a file path, you also may pass an already initialized file handle. In this case, the mode parameter will be ignored. You may use this, if you want to mix LDIF content and LDIF change mode by using two Net_LDAP2_LDIF instances to write to the same filehandle, but it could be very useful in other cases too. To initialize the second instance of Net_LDAP2_LDIF, you can use handle() to get the filehandle from the first instance.
One of the two modes how Net_LDAP2_LDIF can be used is to read a LDIF file and parse its contents into an array of Net_LDAP2_Entry objects. This is done using the read_entry()-method which will return the next entry. If you want to fetch all entries, you use the eof() to detect the end of the input file:
Parsing a LDIF file into Net_LDAP2_Entry objects
<?php
// open some LDIF file for reading
$ldif = new Net_LDAP2_LDIF('somefile.ldif', 'r');
if ($ldif->error()) {
$error_o = $ldif->error(); // get Net_LDAP2_Error object on error
die('ERROR: '.$error_o->getMessage());
}
// parse the entries of the LDIF file into objects
do {
$entry = $ldif->read_entry();
if ($ldif->error()) {
// in case of error, print error.
// here we use the shorthand parameter, so error()
// returns a string instead of a Net_LDAP2_Object
die('ERROR AT INPUT LINE '.$ldif->error_lines().': '.$ldif->error(true));
} else {
// No error: do something with the entry
// Here we just print the entries DN
echo 'sucessfully parsed '.$entry->dn();
}
} while (!$ldif->eof());
// We should call done() once we are finished
$ldif->done();
?>
Since version 2.1.0 Net_LDAP2_Filter can do client side filtering on entry objects. This may be especially useful when combined with LDIF reading support as it allows the developer to execute select querys on the LDIF content. That enables for example the development of reports on those files without the need for an LDAP server. Please refer to the documentation of LDAP filters.
Writing an LDIF file is very easy too. Just pass the entries you want to have written to the write_entry()-method. Beware, that if you have opened the file in "w" write mode this will clear any previous data of that file. Use "a" (append) if you just want add data.
Writing entries
<?php
// Assume we have some valid Net_LDAP2_Entry objects inside $entries
// $entries = array( ... );
// open some file for writing
$ldif = new Net_LDAP2_LDIF('somewritefile.ldif', 'w');
if ($ldif->error()) die('ERROR: '.$error_o->getMessage());
// write the data and check for error
// you could pass one single Net_LDAP2_Entry object or
// several objects inside an array
$ldif->write_entry($entries);
if ($ldif->error()) die('WRITE ERROR: '.$error_o->getMessage());
?>
The process of writing changes is exactly the same like writing entry contents. However there are two differences: Firstly you need to pass the "changes" option and secondly, the entries you want to write need changes. Entries not containing changes will silently be ignored since there is nothing to write.
Writing entry changes
<?php
// cast some test data and three entries
$testattrs = array(
'attr1' => '1234',
'attr2' => 'foo',
'attr3' => array('bar', 'baz')
);
$entries = array(
Net_LDAP2_Entry::createFresh('cn=foo,dc=example,dc=cno',
array_merge(array('cn' => 'foo'), $testattrs)),
Net_LDAP2_Entry::createFresh('cn=bar,dc=example,dc=cno',
array_merge(array('cn' => 'bar'), $testattrs)),
Net_LDAP2_Entry::createFresh('cn=baz,dc=example,dc=cno',
array_merge(array('cn' => 'baz'), $testattrs))
);
// make some changes to the first and the last entry
$entries[0]->add(array('someattr' => 'added'));
$entries[0]->replace(array('attr1' => 'replaced'));
$entries[2]->delete(array('attr2'));
$entries[2]->delete(array('attr3' => 'bar'));
// open some file for writing, but in change mode
$ldif = new Net_LDAP2_LDIF('somewritefile.ldif', 'w', array('change' => true));
if ($ldif->error()) die('ERROR: '.$error_o->getMessage());
// write the data and check for error
// you could pass one single Net_LDAP2_Entry object or
// several objects inside an array
$ldif->write_entry($entries);
if ($ldif->error()) die('WRITE ERROR: '.$error_o->getMessage());
// Now, only two entries are contained in the LDIF file,
// cn=foo,dc=example,dc=cno and cn=baz,dc=example,dc=cno.
// cn=bar,dc=example,dc=cno had no changes and was skipped.
?>
Resulting LDIF change file
version: 1 dn: cn=foo,dc=example,dc=cno changetype: modify add: someattr someattr: added - replace: attr1 attr1: replaced - dn: cn=baz,dc=example,dc=cno changetype: modify delete: attr2 - delete: attr3 attr3: bar -
Sometimes you are interested in the lines inside the LDIF file. For those cases you can use the current_lines() and next_lines() methods. They work in the current context, which may be confusing: current_lines() will always return the lines that have built up the current Net_LDAP2_Entry object when called current_entry() after read_entry() has been called. next_lines() will always return the lines, that will build up the next entry from the current point of view, meaning "relative to the entry that was just been read". However, you can override this by activating the "force" parameter of next_lines() which allows you to loop over all entries. current_entry() behaves exactly like current_lines().
If you think, that the lines you have read would be better in form of an Net_LDAP2_Entry object, use the parseLines() method to parse those lines into an entry. This is a good way if you need just a few specific entries of a large LDIF file.
Reading LDIF lines
<?php
// open some LDIF file for reading
// (error checking code is ommitted in this example for
// better readability - in production, test for errors!)
$ldif = new Net_LDAP2_LDIF('somefile.ldif', 'r');
// since nothing has been read until now, this will
// return an empty array
$empty_array = $ldif->current_lines();
// so let's read the first entries data
$first_entry_lines = $ldif->next_lines();
// if we call it again, we will not read ahead to the
// second entry - we again read the first one!
$first_entry_lines_again = $ldif->next_lines();
// If we call current_lines() now, we haven't read ahead
// like we learned from the last statement.
$empty_array_again = $ldif->current_lines();
// If we want to shift, we must use
// the read_entry() method, which will read ahead.
$first_entry = $ldif->read_entry();
// Now, current_lines() returns the lines of the
// first entry and next_lines() the lines of the second:
$first_entry_lines = $ldif->current_lines();
$second_entry_lines = $ldif->next_lines();
// There is another way to shift the lines which is faster if
// you are just interested in the LDIFs content - you
// need to pass the "force" parameter to next_lines():
$third_entry_lines = $ldif->next_lines(true);
$fourth_entry_lines = $ldif->next_lines(true);
$fifth_entry_lines = $ldif->next_lines(true);
// If you want to convert the lines to an Net_LDAP2_Entry,
// you may do so anytime by using parseLines()
$fourth_entry = $ldif->parseLines($fourth_entry_lines);
// Since we shifted manually only the lines,
// current_lines() will return the lines that built up the
// last (e.g. the first entry) Net_LDAP2_Entry object:
$first_entry_lines = $ldif->current_lines();
// If we decide to read the next entry, we can do that:
$sixth_entry = $ldif->read_entry();
// current_lines() is shifted now:
$sixth_entry_lines = $ldif->current_lines();
?>
Net_LDAP2 features an easy schema caching facility. Caching the schema can gain some performance, especially with slow servers or connections. The facility works with an plugin object that must be passed to Net_LDAP2s registerSchemaCache() method. The cache object can be registered (or unregistered) at any time, but of course it is the best time right after initializing Net_LDAP2.
Enabling/disabling Net_LDAP2s schema caching facility
<?php
// registering a valid schema cache object is enough to enable the caching facility:
$ldap->registerSchemaCache($myCacheObject);
// unregistering is easy too: just supply null as schema cache object:
$ldap->registerSchemaCache(null);
?>
The object that gets passed to registerSchemaCache() must implement the Net_LDAP2_SchemaCache interface which demands two methods. Initialisation of the cache object is dependent on the class itself but should be handled inside the cache class constructor, however this may vary. Please refer to the cache class documentation for those details.
Method | Parameter | Return value | Description |
---|---|---|---|
loadSchema() | none |
Net_LDAP2_Schema,
Net_LDAP2_Error or false |
Returns the cached schema object. Net_LDAP2 will consider anything returned invalid, except a valid Net_LDAP2_Schema object. In case you return a Net_LDAP2_Error, this error will be routed to the return of the $ldap->schema() call. If you return something else, Net_LDAP2 will fetch a fresh Schema object from the LDAP server and tries to cache it via store(). You may also want to implement a cache aging mechanism here too. |
storeSchema() | Net_LDAP2_Schema object | true or (in special cases) Net_LDAP2_Error |
Stores a schema object in the cache. This method will be called, if Net_LDAP2 has fetched a fresh schema object from LDAP and wants to init or refresh the cache. In case of errors you may return a Net_LDAP2_Error which will be routet to the client. Note that doing this prevents, that the schema object fetched from LDAP will be given back to the client, so only return errors if storing of the cache is something crucial (e.g. for doing something else with it). Normaly you dont want to give back errors in which case Net_LDAP2 needs to fetch the schema once per script run and use the error functionality of loadSchema(). |
As of Net_LDAP2 2.0.0, there is one default schema caching class: Net_LDAP2_SimpleFileSchemaCache to make your life a little easier. Caching to files should also be the most commonly used case.
This cache class is built to be flexible yet simple to use and may serve as example to write own caching classes. This cache stores the schema object in a flat file. The path is freely configurable. It also servers a cache aging mechanism that can be used to invalidate the cached schema after some time so it will be refreshed regularly.
To use this cache, you firstly need to initialize and configure a fresh cache object. Then the cache must be registered with the Net_LDAP2 instance. After that, Net_LDAP2 will use the cache.
Initializing the SimpleFileSchemaCache
<?php
$myCacheConfig = array(
'path' => '/tmp/Net_LDAP_Schema.cache', // may reside on an linux tmpfs for improved performance
'max_age' => 1200 // in seconds, use 0 for endlessly
);
$myCacheObject = new Net_LDAP2_SimpleFileSchemaCache($myCacheConfig);
$ldap->registerSchemaCache($myCacheObject);
?>
Option | Mandatory? | Default | Description |
---|---|---|---|
path |
No | /tmp/Net_LDAP_Schema.cache |
The full path to the cache file. To improve the caches performance under linux, you can place the cache file in a tmpfs mounted directory. This will put the file in the computers memory instead on disk, enabling nearly instant access. |
max_age |
No | 1200 |
Maximum cache age in seconds. The age of the cache is determined by the files
last change time. If max_age is reached, Net_LDAP2 will fetch
a fresh Net_LDAP2_Schema object which is then stored in the cache file again.
Setting this to "0" will make the cache endlessly valid.
|
However this is basicly an easy task, this is beyond the scope of this manual. If you want to write your
own custom schema cache, please refer to the detailed example at
/your/pear/path/docs/Net_LDAP2/examples/schema_cache.php
as well as the source/APIdoc of
the interface Net_LDAP2_SchemaCache.
This package validates and cleanly formats Media Access Control (MAC) addresses. The Net_MAC class can also import a list of MAC address vendors and store them in a database which the class can then use to identify vendors of any MAC address.
Constant to represent the maximum length of a line in the manufacturers file.
Error constant: signifies no problem (OK)
Error constant: signifies a bad option being passed to a function
Error constant: signifies bad data being passed to a function
Error constant: signifies a bad database connection
Error constant: signifies a bad manufacturers file
string Net_MAC::check (
string $input
, string $delimiter=':'
)
This function will check a MAC address to make sure it is valid.
string $input
- The
string containing the MAC Address
string $delimiter
- The
string representing the delimiter to use when checking
the MAC Address
boolean - TRUE if the MAC address is valid, FALSE otherwise
This function should be called statically.
Using check()
<?php
require_once "Net/MAC.php";
$macaddr = 'AB:CD:EF:00:11:22';
$mac = Net_MAC::check($macaddr);
if ($mac) {
echo "$macaddr is valid";
}
else {
echo "$macaddr is invalid";
}
?>
This would output the following:
ab:cd:ef:00:11:22 is valid
Using check() to get a MAC address with a different delimiter
<?php
require_once "Net/MAC.php";
$macaddr = 'AB:CD:EF:00:11:22';
$mac = Net_MAC::check($macaddr, '-');
if ($mac) {
echo "$macaddr is valid";
}
else {
echo "$macaddr is invalid";
}
?>
This would output the following:
AB:CD:EF:00:11:22 is invalid
since the delimiter '-' was not used in the provided MAC address.
string Net_MAC::format (
string $input
, string $delimiter=':'
, boolean $uppercase
= true
)
This function will format a MAC address into XX:XX:XX:XX:XX:XX format from whatever format is passed to the function. The delimiter (':' in the example above) will be replaced with whatever string is passed to the $delimiter parameter (default ':').
string $input
- The
string containing the MAC Address
string $delimiter
- The
string representing the delimiter to use when
formatting the MAC Address
string $uppercase
- If set to
TRUE (default), the alpha characters in the hexadecimal
values in the MAC Address will be returned in uppercase. If
FALSE, the alpha characters in the hexadecimal values will be
returned in lowercase.
string - The formatted MAC Address or FALSE if the syntax of the MAC address is invalid
This function should be called statically.
Using format()
<?php
require_once "Net/MAC.php";
$macaddr = 'AB:CD:EF:00:11:22';
if (!Net_MAC::check($macaddr)) {
echo "$macaddr is invalid";
exit;
}
$mac = Net_MAC::format($macaddr);
if ($mac) {
echo "$mac";
}
else {
echo "$macaddr could not be formatted";
}
?>
This would output the following:
AB:CD:EF:00:11:22
Using format() to get a MAC address with a different delimiter
<?php
require_once "Net/MAC.php";
$macaddr = 'AB:CD:EF:00:11:22';
if (!Net_MAC::check($macaddr)) {
echo "$macaddr is invalid";
exit;
}
$mac = Net_MAC::format($macaddr, '-');
if ($mac) {
echo "$mac";
}
else {
echo "$macaddr could not be formatted";
}
?>
This would output the following:
AB-CD-EF-00-11-22
Using format() to get a MAC address with all capital alpha characters
<?php
require_once "Net/MAC.php";
$macaddr = 'ab:cd:ef:00:11:22';
if (!Net_MAC::check($macaddr)) {
echo "$macaddr is invalid";
exit;
}
$mac = Net_MAC::format($macaddr, '', true);
if ($mac) {
echo "$mac";
}
else {
echo "$macaddr could not be formatted";
}
?>
This would output the following:
ABCDEF001122
Net_MAC::__construct (
object $db
, array $options
)
This is the constructor that will create and populate a valid Net_MAC object.
object $db
- This parameter must
be a valid MDB2
object.
array $options
- An array
of options to use with the database in retrieving MAC address
vendors. The associative array should have key/value
pairs as follows:
Option | Description |
---|---|
tablename | The name of the table where MAC address vendor information lives |
macaddrcol | The name of the column containing the MAC address prefixes |
vendorcol | The name of the column containing the vendor name |
desccol | The name of the column containing any extra descriptive information derived from the vendor list |
void - No return value. A Net_MAC_Exception Exception object will be thrown if there is an error during construction
The constructor can throw exceptions on error, so the constructor should always be called from inside a try/catch block.
Instantiating a Net_MAC object
<?php
require_once 'Net/MAC.php';
require_once 'MDB2.php';
$db_type = 'pgsql';
$db_host = 'localhost';
$db_user = 'username';
$db_name = 'dbname';
$db_pass = 'password';
$dsn = "$db_type://$db_user:$db_pass@$db_host/$db_name";
$dbh =& MDB2::factory($dsn);
if (MDB2::isError($dbh)) {
echo "MDB2 Error: ".$dbh->getUserInfo();
}
$dboptions = array('tablename' => 'macvendors',
'macaddrcol' => 'macaddr',
'vendorcol' => 'vendor',
'desccol' => 'description');
try {
$nmh =& new Net_MAC($dbh, $dboptions);
} catch (Net_MAC_Exception $e) {
echo 'Net_MAC Error: ' . $e->getMessage();
exit;
}
?>
throws Net_MAC_Exception
mixed setMAC (
string $macaddr
, string $delimiter = ':'
)
This method will set the MAC address in the object given the passed MAC address and the MAC address delimiter. This method also makes use of the check() method to make sure that the MAC address is valid.
string $macaddr
- The
string representing the MAC address
string $delimiter
- The
string representing the delimiter to use when verifying
the MAC Address
boolean - Returns TRUE if the MAC address is set correctly, FALSE otherwise (i.e. the MAC address is not valid).
This function can not be called statically.
Using setMAC()
<?php
require_once 'Net/MAC.php';
require_once 'MDB2.php';
$db_type = 'pgsql';
$db_host = 'localhost';
$db_user = 'username';
$db_name = 'dbname';
$db_pass = 'password';
$dsn = "$db_type://$db_user:$db_pass@$db_host/$db_name";
$dbh =& MDB2::factory($dsn);
if (MDB2::isError($dbh)) {
echo "MDB2 Error: ".$dbh->getUserInfo();
}
$dboptions = array('tablename' => 'macvendors',
'macaddrcol' => 'macaddr',
'vendorcol' => 'vendor',
'desccol' => 'description');
try {
$nmh =& new Net_MAC($dbh, $dboptions);
} catch (Net_MAC_Exception $e) {
echo 'Net_MAC Error: ' . $e->getMessage();
exit;
}
$nmh->setMAC('00:11:22:33:44:55');
?>
mixed importVendors (
string $file
, boolean $doReturn
= false
)
This method will parse a manufacturers' file, such as the one from
http://anonsvn.wireshark.org/wireshark/trunk/manuf
,
containing a list of MAC address prefix-to-vendor relationships.
If the $doReturn parameter is FALSE, then the data will be
imported into the database defined by the factory of this class.
However, if $doReturn is TRUE, then the return will be an
associative array with the key being the MAC address prefix
and the data being an associative array with the keys
'vendor' and 'description'.
string $file
- The filename or
URL of the manufacturers' file to parse
string $doReturn
- If TRUE, an
array will be returned, if FALSE, the data will be
imported into the database.
mixed - If $doReturn is TRUE, the method will return an array. Otherwise, the method will return TRUE on success. A Net_MAC_Exception Exception object will be thrown on failure in either case.
This function can not be called statically.
This method can throw exceptions on error, so the method should always be called from inside a try/catch block.
Using importVendors() with a URL
<?php
require_once 'Net/MAC.php';
require_once 'MDB2.php';
$db_type = 'pgsql';
$db_host = 'localhost';
$db_user = 'username';
$db_name = 'dbname';
$db_pass = 'password';
$dsn = "$db_type://$db_user:$db_pass@$db_host/$db_name";
$dbh =& MDB2::factory($dsn);
if (MDB2::isError($dbh)) {
echo "MDB2 Error: ".$dbh->getUserInfo();
}
$dboptions = array('tablename' => 'macvendors',
'macaddrcol' => 'macaddr',
'vendorcol' => 'vendor',
'desccol' => 'description');
try {
$nmh =& new Net_MAC($dbh, $dboptions);
} catch (Net_MAC_Exception $e) {
echo 'Net_MAC Error: ' . $e->getMessage();
exit;
}
try {
$nmh->importVendors('http://anonsvn.wireshark.org/wireshark/trunk/manuf');
} catch (Net_MAC_Exception $e) {
echo 'Net_MAC Error: ' . $e->getMessage();
exit;
}
?>
This would output an error only if there is an error importing the file from the URL.
Using importVendors() with a file, returning an array
<?php
require_once 'Net/MAC.php';
require_once 'MDB2.php';
$db_type = 'pgsql';
$db_host = 'localhost';
$db_user = 'username';
$db_name = 'dbname';
$db_pass = 'password';
$dsn = "$db_type://$db_user:$db_pass@$db_host/$db_name";
$dbh =& MDB2::factory($dsn);
if (MDB2::isError($dbh)) {
echo "MDB2 Error: ".$dbh->getUserInfo();
}
$dboptions = array('tablename' => 'macvendors',
'macaddrcol' => 'macaddr',
'vendorcol' => 'vendor',
'desccol' => 'description');
$nmh =& Net_MAC::factory($dbh, $dboptions);
if (PEAR::isError($nmh)){
echo 'Net_MAC Error: '.$nmh->getMessage();
}
$vendorArr = $nmh->importVendors('./manuf')
if (PEAR::isError(!$vendorArr)) {
echo 'Net_MAC: ' . $err->getUserInfo();
exit;
}
print_r($vendorArr);
?>
This would output the entire list of MAC address vendors on success and an error message on failure.
throws Net_MAC_Exception
mixed findVendor (
string $getDescription
= false
, string $macList
= null
)
This method will search through the database to find a vendor that matches the MAC address stored in the class using setMac(). If the $macList parameter is set, the method will use the array stored in $macList as the data source to find the MAC vendor instead of the database. The array would have to be an array with the same characteristics as one returned from the importVendors() method when using the $doReturn parameter.
string $getDescription
- If set
to TRUE, the return value will be an array with keys
'vendor' and 'description'. Normally the method will simply
return the vendor name.
string $macList
- An optional
list of MAC-to-vendor relationships to search instead of using
the database.
mixed - Returns an associative array if $getDescription is TRUE, returns a string with the vendor name if $getDescription is FALSE. If the MAC vendor cannot be found in the vendor list, FALSE is returned.
This function can not be called statically.
Using findVendor()
<?php
require_once 'Net/MAC.php';
require_once 'MDB2.php';
$db_type = 'pgsql';
$db_host = 'localhost';
$db_user = 'username';
$db_name = 'dbname';
$db_pass = 'password';
$dsn = "$db_type://$db_user:$db_pass@$db_host/$db_name";
$dbh =& MDB2::factory($dsn);
if (MDB2::isError($dbh)) {
echo "MDB2 Error: ".$dbh->getUserInfo();
}
$dboptions = array('tablename' => 'macvendors',
'macaddrcol' => 'macaddr',
'vendorcol' => 'vendor',
'desccol' => 'description');
try {
$nmh =& new Net_MAC($dbh, $dboptions);
} catch (Net_MAC_Exception $e) {
echo 'Net_MAC Error: ' . $e->getMessage();
exit;
}
$nmh->setMAC('00:11:22:33:44:55');
$result = $nmh->findVendor(true);
if (is_array($result)) {
foreach($result as $key => $value) {
echo "$key: $value<br>\n";
}
}
else {
echo $result;
}
?>
This would output the following:
vendor: Cimsys description: CIMSYS Inc
This class is a simple derivation of the PEAR_Exception class and simply exists for the use of throwing exceptions for this class.
Implementation of the NNTP protocol
The Net_NNTP class is considered deprecated as of v0.10.x; Net_NNTP_Client should be used instead in new applications. A 'backport' of Net_NNTP_Client has replaced Net_NNTP to maintain backward compatibility, but this is temporary and will only last for some time.
NNTP client implementation
default NNTP hostname: localhost
default NNTP port: 119
authentication provided by the NNTP-server
authentication provided by the NNTP-server
authentication provided by the NNTP-server
authenticate
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::authenticate
(
string
$$user
,
string
$$pass
[,
integer
$$authmode = NET_NNTP_AUTHORIGINAL
] )Authenticate on an already open connection
$user
- Username to authenticate with
$pass
- Password to authenticate with
$authmode
- Type of authentication. Default=NET_NNTP_AUTHORIGINAL
boolean
-
Returns TRUE on success,
PEAR_Error on failure.
This function can not be called statically.
boolean Net_NNTP_Client::connect (
string $host = NET_NNTP_PROTOCOL_DEFAULT_HOST
,
integer $port = NET_NNTP_PROTOCOL_DEFAULT_PORT
)
Connect to a specific NNTP-server
$host
- Hostname of the NNTP-server. Default=NET_NNTP_PROTOCOL_DEFAULT_HOST
$port
- Port, where the NNTP-server listens. Default=NET_NNTP_PROTOCOL_DEFAULT_PORT
boolean
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"Could not connect to NNTP-server $host"
or
"Not connected"
|
The connection couldn't be established because
|
Check for server name, the connection to the net and possible firewalls on client or server side. |
This function can not be called statically.
Using connect()
<?php
require_once 'Net/NNTP/Client.php';
$nntp = new Net_NNTP_Client();
$ret = $nntp->connect('news.php.net');
if( PEAR::isError($ret)) {
// handle error
} else {
// success
}
?>
boolean Net_NNTP_Client::connectAuthenticated (
integer $user
= null
,
integer $pass
= null
,
string $host = NET_NNTP_PROTOCOL_DEFAULT_HOST
,
integer $port = NET_NNTP_PROTOCOL_DEFAULT_PORT
,
integer $authmode = NET_NNTP_AUTHORIGINAL
)
Connect and authenticate to a specific NNTP-server
This function is deprecated. That means that future versions of this package may not support it anymore.
$user
- Username to authenticate
$pass
- Password to authenticate
$host
- Hostname of the NNTP-server. Default=NET_NNTP_PROTOCOL_DEFAULT_HOST
$port
- Port, where the NNTP-server listens. Default=NET_NNTP_PROTOCOL_DEFAULT_PORT
$authmode
- Type of authentication. Default=NET_NNTP_AUTHORIGINAL
boolean
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"Could not connect to NNTP-server $host"
or
"Not connected"
|
The connection couldn't be established because
|
Check for server name, the connection to the net and possible firewalls on client or server side. |
since 0.3
This function can not be called statically.
Using connectauthenticated()
<?php
require_once 'Net/NNTP/Client.php';
$nntp = new Net_NNTP_Client();
$ret = $nntp->connectAuthenticated('news.php.net');
if( PEAR::isError($ret)) {
// handle error
} else {
// success
}
?>
count
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::count
( void
)Retrieves the number of articles in the current newsgroup.
integer
- number of articles in newsgroup
since 0.3
This function can not be called statically.
Using count()
<?php
...
$nntp->selectGroup("php.pear.dev");
echo "number of articles: ".$nntp->count();
?>
getDescriptions
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::getDescriptions
( void
)This method is currently not documented.
This function is EXPERIMENTAL. That means, that the behaviour of this function, the function name, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this function at your own risk.
first
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::first
( void
)Retrieves the number of the first article in the current newsgroup.
integer
- lowest article number in newsgroup
since 0.3
This function can not be called statically.
Using first()
<?php
...
$nntp->selectGroup("php.pear.dev");
echo "lowest message number: ".$nntp->first();
?>
mixed Net_NNTP_Client::getArticle (
string $article
)
This function is currently not documented.
mixed Net_NNTP_Client::getArticleRaw (
string $article
,
boolean $implode = false
)
Returns the whole article from the currently selected newsgroup
string $article
- article number or Message-ID of the article to fetch
boolean $implode
- Determines if the resulting array is to be imploded into a string.
array/string
- If message exists the message
or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article |
since 0.3
This function can not be called statically.
Using getArticleRaw()
<?php
...
$article = $nntp->getArticleRaw($msg_id);
if( PEAR::isError($article)) {
// handle error
} else {
// success
}
?>
getGroupArticles
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::getGroupArticles
(
string
$$newsgroup
)This method is currently not documented.
This function is EXPERIMENTAL. That means, that the behaviour of this function, the function name, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this function at your own risk.
mixed Net_NNTP_Client::getBodyRaw (
string $article
,
boolean $implode = false
)
Returns the whole body of an article from the currently selected newsgroup
string $article
- article number or Message-ID of the article to fetch
boolean $implode
- Determines if the resulting array is to be imploded into a string.
string/array
- If message exists the body
or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article |
since 0.3
This function can not be called statically.
getBody() makes no converting of the body content to any character set. You get the content 'as is'.
Using getBodyRaw()
<?php
...
$body = $nntp->getBodyRaw($msgId);
if( PEAR::isError($body)) {
// handle error
} else {
// success - print body
echo $body;
}
?>
mixed Net_NNTP_Client::getDate (
integer $format = 1
)
Retrieves the date from the NNTP-server.
$format
- Tetermines which format to return.
$format | returns |
---|---|
0 |
timestamp
|
1 |
array - a hash with the date
|
since 0.3
This function can not be called statically.
Using getDate()
<?php
...
$date = $nntp->getDate();
echo "Date: ".$date['m']."-".$date['d']."-".$date['y'];
?>
array Net_NNTP_Client::getGroups (
)
Returns a list of all avaible newsgroups
array
- a two dimensional, nested array
indicated by the name of the newsgroup, every entry contains
information about the newsgroup:
$groups[newsgroup_name]['group']
Name of the newsgroup
$groups[newsgroup_name]['last']
Number of the last article
$groups[newsgroup_name]['first']
Number of the first article
$groups[newsgroup_name]['posting']
values: y
- yes,
n
- no,
m
- moderated)
This function can not be called statically.
Especially public news server can provide more then 30.000 newsgroup. So this function may runs longer then the maximum execution time set in the
php.ini
.
Using getGroups()
<?php
...
$ret = $nntp->connect('news.php.net');
if( PEAR::isError($ret)) {
// handle error
} else {
// success
$groups = $nntp->getGroups();
// Print a list of avaible newsgroups
foreach($groups as $group) {
echo $group['group'].'<br>';
}
}
?>
object Net_NNTP_Client::getHeader (
string $article
)
This function is currently not documented.
string Net_NNTP_Client::getHeaderRaw (
string $article
,
boolean $implode = false
)
Returns the whole header of an article in the currently selected newsgroup
string $article
- article number or Message-ID of the article to fetch
boolean $implode
- Determines if the resulting array is to be imploded into a string.
array/string
- If message exists the header
or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article |
since 0.3
This function can not be called statically.
Using getHeaderRaw()
<?php
...
$headers = $nntp->getHeaderRaw($msgId);
if( PEAR::isError($headers)) {
// handle error
} else {
// success - split the string into a array
$headersArray = explode( "\n", $headers);
}
?>
getNewGroups
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::getNewGroups
(
mixed
$$time
)This method is currently not documented.
This function is EXPERIMENTAL. That means, that the behaviour of this function, the function name, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this function at your own risk.
getNewNews
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::getNewNews
(
mixed
$$time
[,
string
$$pass
] )This method is currently not documented.
This function is EXPERIMENTAL. That means, that the behaviour of this function, the function name, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this function at your own risk.
array Net_NNTP_Client::getOverview (
string $first
,
string $last
)
Returns (a certain range of) the overview of the currently selected newsgroup. selected newsgroup
$first
- first article number, start of the range
$last
- last article number, end of the range
array
- a nested array
indicated by the message id of the article, every entry contains
the header as array
<?php
$msgs[message_id][headername] = headercontent
?>
This function can not be called statically.
Be careful with choosing the range. It could requires some time to get a huge number of message headers.
Using getOverview()
<?php
...
$ret = $nntp->connect('news.php.net');
if( PEAR::isError($ret)) {
// handle error
} else {
// print the last 10 messages
$data = $nntp->selectGroup('php.pear.dev');
$msgs = $nntp->getOverview( $data['last'] - 10, $data[last]);
foreach($msgs as $msg) {
// print subjects
echo $msg['subject'].'<br>';
}
}
?>
getReferencesOverview
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::getReferencesOverview
(
integer
$$first
,
integer
$$last
)This method is currently not documented.
This function is EXPERIMENTAL. That means, that the behaviour of this function, the function name, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this function at your own risk.
group
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::group
(
string
$$user
,
string
$$pass
[,
integer
$$authmode = NET_NNTP_AUTHORIGINAL
] )This method is currently not documented.
This function is EXPERIMENTAL. That means, that the behaviour of this function, the function name, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this function at your own risk.
isConnected
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::isConnected
( void
)This method is currently not documented.
This function is EXPERIMENTAL. That means, that the behaviour of this function, the function name, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this function at your own risk.
last
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::last
( void
)Retrieves the number of the last article in the current newsgroup.
integer
- highest article number in newsgroup
since 0.3
This function can not be called statically.
Using last()
<?php
...
$nntp->selectGroup("php.pear.dev");
echo "highest message number: ".$nntp->last();
?>
post
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::post
(
string
$$newsgroups
,
string
$$subject
,
string
$$body
,
string
$$from
[,
integer
$$additional = ''
] )This method is currently not documented.
This function is EXPERIMENTAL. That means, that the behaviour of this function, the function name, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this function at your own risk.
quit
()require_once 'Net/NNTP/Client.php';
Net_NNTP_Client::post
( void
)Close the connection to the NNTP-server
This function can not be called statically.
array Net_NNTP_Client::selectGroup (
string $newsgroup
)
Selects a specific newsgroup on the NNTP-server
string $newsgroup
- Name of the newsgroup to
access
array
- If the newsgroup exists, an array
is returned:
Key | Value |
---|---|
'count' | Number of articles in the group |
'first' | The first article number in the group |
'last' | The last article number in the group |
'group' | Groupname |
otherwise a PEAR_Error is returned on fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article |
This function can not be called statically.
Using selectGroup()
<?php
...
$ret = $nntp->connect('news.php.net');
if( PEAR::isError($ret)) {
// handle error
} else {
// success
$data = $nntp->selectGroup('php.pear.dev');
// Print the count of articles
echo "Count: ", $data['last'] - $data['first'];
}
?>
This function is deprecated. That means that future versions of this package may not support it anymore.
The historical Net_NNTP class
default NNTP hostname: localhost
default NNTP port: 119
authentication provided by the NNTP-server
authentication provided by the NNTP-server
authentication provided by the NNTP-server
deprecated (never used)
deprecated (never used)
deprecated (never used)
deprecated (use NET_NNTP_AUTHORIGINAL instead)
deprecated (use NET_NNTP_AUTHSIMPLE instead)
deprecated (use NET_NNTP_AUTHGENERIC instead)
string Net_NNTP::command (
string $cmd
,
boolean $auth
)
Command() sends a string command to a newsserver. So you can send customized and/ or non-standard commands to the newsserver.
string $cmd
- the command to send
boolean $auth
- if TRUE, an auth request
is issued before the command
string
- the unprocessed server response
This function can not be called statically.
Command() does no checks on the given command and/ or proccesses the server response. So you should know what are you doing.
Using command()
<?php
...
$response = $nntp->command("ARTICLE 1004853");
?>
boolean Net_NNTP::connect (
string $host = NET_NNTP_PROTOCOL_DEFAULT_HOST
,
integer $port = NET_NNTP_PROTOCOL_DEFAULT_PORT
)
Connect to a specific newsserver
$host
- Hostname of the newsserver
$port
- Port, where the newsserver listens
boolean
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"Could not connect to NNTP-server $host"
or
"Not connected"
|
The connection couldn't be established because
|
Check for server name, the connection to the net and possible firewalls on client or server side |
This function can not be called statically.
Using connect()
<?php
require_once "Net/NNTP.php";
$nntp = new Net_NNTP;
$ret = $nntp->connect("news.php.net");
if( PEAR::isError($ret)) {
// handle error
} else {
// success
}
?>
boolean Net_NNTP::connectAuthenticated (
integer $user
= null
,
integer $pass
= null
,
string $host = NET_NNTP_PROTOCOL_DEFAULT_HOST
,
integer $port = NET_NNTP_PROTOCOL_DEFAULT_PORT
,
integer $authmode = NET_NNTP_AUTHORIGINAL
)
Connect and authenticate to a specific newsserver
$user
- Username to authenticate
$pass
- Password to authenticate
$host
- Hostname of the NNTP-server
$port
- Port, where the newsserver listens
$authmode
- Type of authentication,
at the moment only
NET_NNTP_AUTHORIGINAL
boolean
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"Could not connect to NNTP-server $nntpserver"
or
"Not connected"
|
The connection couldn't be established because
|
Check for server name, the connection to the net and possible firewalls on client or server side |
since 0.3
This function can not be called statically.
Using connectauthenticated()
<?php
require_once "Net/NNTP.php";
$nntp = new Net_NNTP;
$ret = $nntp->connectAuthenticated("news.php.net");
if( PEAR::isError($ret)) {
// handle error
} else {
// success
}
?>
array Net_NNTP::date (
)
This function is deprecated. That means that future versions of this package may not support it anymore.
Retrieves the date from the news server
array
- a hash with the date
$date['y']
Year
$date['m']
Month
$date['d']
Day
This function can not be called statically.
Using date()
<?php
...
$date = $nntp->date();
echo "Date: ".$date['m']."-".$date['d']."-".$date['y'];
?>
integer Net_NNTP::first (
)
Retrieves the lowest message number in the current selected newsgroup
integer
- lowest message number
since 0.3
This function can not be called statically.
Using first()
<?php
...
$nntp->selectGroup("php.pear.dev");
echo "lowest message number: ".$nntp->first();
?>
mixed Net_NNTP::getArticle (
string $articleId
)
Returns the whole article from the current selected newsgroup
This function is deprecated. That means that future versions of this package may not support it anymore.
Consider this method deprecated and subject to changes - use Net_NNTP::getArticleRaw() instead.
string $articleID
- Message-ID of the message
number to fetch
string
- If message exists the message
as string or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article | Check the article ID or if your are still connected to the server ( Net_NNTP::isConnected()) |
This function can not be called statically.
Using getArticle()
<?php
...
$article = $nntp->getArticle($msg_id);
if( PEAR::isError($article)) {
// handle error
} else {
// success
}
?>
mixed Net_NNTP::getArticleRaw (
string $articleId
)
Returns the whole article from the current selected newsgroup
string $articleID
- Message-ID of the message
number to fetch
string
- If message exists the message
as string or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article | Check the article ID or if your are still connected to the server ( Net_NNTP::isConnected()) |
since 0.3
This function can not be called statically.
Using getArticleRaw()
<?php
...
$article = $nntp->getArticleRaw($msg_id);
if( PEAR::isError($article)) {
// handle error
} else {
// success
}
?>
string Net_NNTP::getBody (
string $articleId
)
Returns the whole body of an article in the current selected newsgroup from the webserver
This function is deprecated. That means that future versions of this package may not support it anymore.
Consider this method deprecated and subject to changes - use Net_NNTP::getBodyRaw() instead.
string $articleId
- Message-ID or Message number
string
- If message exists the body as
string or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article | Check the article ID or if your are still connected to the server ( Net_NNTP::isConnected()) |
This function can not be called statically.
getBody() makes no converting of the body content to any character set. You get the content 'as is'.
Net_NNTP::getHeaders() , Net_NNTP::splitHeaders() , Net_NNTP::getArticle() , Net_NNTP::getOverview()
Using getBody()
<?php
...
$body = $nntp->getBody($msgId);
if( PEAR::isError($body)) {
// handle error
} else {
// success - print body
echo $body;
}
?>
string Net_NNTP::getBodyRaw (
string $articleId
)
Returns the whole body of an article in the current selected newsgroup from the webserver
string $articleId
- Message-ID or Message number
string
- If message exists the body as
string or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article | Check the article ID or if your are still connected to the server ( Net_NNTP::isConnected()) |
since 0.3
This function can not be called statically.
getBody() makes no converting of the body content to any character set. You get the content 'as is'.
Net_NNTP::getHeaderRaw() , Net_NNTP::splitHeaders() , Net_NNTP::getArticleRaw() , Net_NNTP::getOverview()
Using getBodyRaw()
<?php
...
$body = $nntp->getBodyRaw($msgId);
if( PEAR::isError($body)) {
// handle error
} else {
// success - print body
echo $body;
}
?>
array Net_NNTP::getDate (
)
Retrieves the date from the news server
array
- a hash with the date
$date['y']
Year
$date['m']
Month
$date['d']
Day
since 0.3
This function can not be called statically.
Using getDate()
<?php
...
$date = $nntp->getDate();
echo "Date: ".$date['m']."-".$date['d']."-".$date['y'];
?>
array Net_NNTP::getGroups (
string $fetch
= true
)
Returns a list of all avaible newsgroups from the connected news server
$fetch
- can be irgnored, unused
array
- a two dimensional, nested array
indicated by the name of the newsgroup, every entry contains
information about the newsgroup:
$groups[newsgroup_name]['group']
name of the newsgroup
$groups[newsgroup_name]['last']
message number of the last message
$groups[newsgroup_name]['first']
message number of the first message
$groups[newsgroup_name]['posting_allowed']
values: y
- yes,
n
- no,
m
- moderated)
$groups[newsgroup_name]['desc']
newsgroup description
This function can not be called statically.
Especially public news server can provide more then 30.000 newsgroup. So this function may runs longer then the maximum execution time set in the
php.ini
.
Using getGroups()
<?php
...
$ret = $nntp->connect("news.php.net");
if( PEAR::isError($ret)) {
// handle error
} else {
// success
$groups = $nntp->getGroups();
// Print a list of avaible newsgroups
foreach($groups as $group) {
echo $group['group'].': '.$group['desc'].'<br>';
}
}
?>
string Net_NNTP::getHeaderRaw (
string $articleId
)
Returns all avaible header lines of a specified message in the current selected newsgroup
string $articleId
- Message-ID or Message number
string
- If message exists the header
as string or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article | Check the article ID or if your are still connected to the server ( Net_NNTP::isConnected()) |
since 0.3
This function can not be called statically.
Using getHeaderRaw()
<?php
...
$headers = $nntp->getHeaderRaw($msgId);
if( PEAR::isError($headers)) {
// handle error
} else {
// success - split the string into a array
$headersArray = explode( "\n", $headers);
}
?>
string Net_NNTP::getHeaders (
string $articleId
)
Returns all avaible header lines of a specified message in the current selected newsgroup
This function is deprecated. That means that future versions of this package may not support it anymore.
Consider this method deprecated and subject to changes - use Net_NNTP::getHeaderRaw() instead.
string $articleId
- Message-ID or Message number
string
- If message exists the headers
as string or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article | Check the article ID or if your are still connected to the server ( Net_NNTP::isConnected()) |
This function can not be called statically.
Using getHeaders()
<?php
...
$headers = $nntp->getHeaders($msgId);
if( PEAR::isError($headers)) {
// handle error
} else {
// success - split the string into a array
$headersArray = explode( "\n", $headers);
}
?>
array Net_NNTP::getOverview (
string $first
,
string $last
)
Returns all message headers in a certain range of the current selected newsgroup
$first
- start message id, start of the
range
$last
- last message id, end of the range
array
- a nested array
indicated by the message id of the article, every entry contains
the header as array
<?php
$msgs[message_id][headername] = headercontent
?>
This function can not be called statically.
Be careful with choosing the range. It could requires some time to get a huge number of message headers.
Using getOverview()
<?php
...
$ret = $nntp->connect("news.php.net");
if( PEAR::isError($ret)) {
// handle error
} else {
// print the last 10 messages
$data = $nntp->selectGroup("php.pear.dev");
$msgs = $nntp->getOverview( $data['last'] - 10, $data[last]);
foreach($msgs as $msg) {
// print subjects
echo $msg['Subject'].'<br>';
}
}
?>
array Net_NNTP::getOverviewFmt (
)
Returns the name of message headers, which is provided by every message
array
- list of header names
This function can not be called statically.
Using getOverviewFmt()
<?php
...
$tblheaders = $nntp->getOverviewFmt();
// create a table headline
echo '<table>';
echo '<tr>';
foreach($tblheaders as $th) {
// print headernames
echo '<th>'.$th.'</th>';
}
echo '</tr>';
... // draw the header data of messages
echo '</table>';
?>
array Net_NNTP::getOverviewFormat (
)
Returns the name of message headers, which is provided by every message
array
- list of header names
This function can not be called statically.
Using getOverviewFmt()
<?php
...
$tblheaders = $nntp->getOverviewFormat();
// create a table headline
echo '<table>';
echo '<tr>';
foreach($tblheaders as $th) {
// print headernames
echo '<th>'.$th.'</th>';
}
echo '</tr>';
... // draw the header data of messages
echo '</table>';
?>
boolean Net_NNTP::isConnected (
)
Returns the status of the connection
boolean
- TRUE, if connected
This function can not be called statically.
Using isConnected()
<?php
...
if(PEAR::isError($response)) {
// something goes wrong, check if we are still connected
if($nntp->isConnected()) {
echo "disconnected from newsserver!"
...
}
}
?>
integer Net_NNTP::last (
)
Retrieves the highest message number in the current selected newsgroup
integer
- highest message number
since 0.3
This function can not be called statically.
Using last()
<?php
...
$nntp->selectGroup("php.pear.dev");
echo "highest message number: ".$nntp->last();
?>
integer Net_NNTP::max (
)
This function is deprecated. That means that future versions of this package may not support it anymore.
Retrieves the highest message number in the current selected newsgroup
integer
- highest message number
This function can not be called statically.
Using max()
<?php
...
$nntp->selectGroup("php.pear.dev");
echo "highest message number: ".$nntp->max();
?>
integer Net_NNTP::min (
)
This function is deprecated. That means that future versions of this package may not support it anymore.
Retrieves the lowest message number in the current selected newsgroup
integer
- lowest message number
This function can not be called statically.
Using min()
<?php
...
$nntp->selectGroup("php.pear.dev");
echo "lowest message number: ".$nntp->min();
?>
string Net_NNTP::post (
string $subject
,
string $newsgroup
,
string $from
,
string $body
,
string $additional
)
Post a message to a news server
$subject
- Subject of the message
$newsgroup
- Post to this newsgroup
$from
- EMail adress of sender
$body
- Body of the message
$additional
- String of headers to add
string
- Server response
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"Not connected"
|
You forgot the set up a connection to a news server or the connection was already again closed. | Open a connection before using post(). Check the connection status before using post(). |
This function can not be called statically.
post() doesn't care about character encoding of subject and body. Make sure to set correct headers, if you are using non ASCII-127 characters.
Using post()
<?php
...
$subject = "Testpost";
$newsgroup = "php.test";
$body = "Le Mardi 12 f=E9vrier 2002, this is a test message using special french chars";
$from = "test@example.com";
$addheader = "Content-Transfer-Encoding: quoted-printable\n".
"Content-Type: text/plain; charset=ISO-8859-1;";
$response = $nntp->post($subject, $newsgroup, $from, $body, $addheader);
...
?>
boolean Net_NNTP::prepareConnection (
string $nntpserver
,
integer $port = 119
,
integer $newsgroup
,
integer $user
= null
,
integer $pass
= null
,
integer $authmode = PEAR_NNTP_ORIGINAL
)
Connect to a specific newsserver and access the given newsgroup
This function is deprecated. That means that future versions of this package may not support it anymore.
Consider this method deprecated - use Net_NNTP::connectAuthenticated() instead.
$nntpserver
- Name of the newsserver to connect
$port
- Port, where the newsserver listens
$newsgroup
- Newsgroup to access
$user
- Username to authenticate
$user
- Username to authenticate
$pass
- Password to authenticate
$authmode
- Type of authentication,
at the moment only
PEAR_NNTP_AUTHORIGINAL
boolean
-
TRUE if successful
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL |
"Could not connect to NNTP-server $nntpserver"
or
"Not connected"
|
The connection couldn't be established because
|
Check for server name, the connection to the net and possible firewalls on client or server side |
NULL | Every other message | This message is directly passed from the news server, in the most cases caused by calling a non existing newsgroup | Check the given newsgroup name |
This function can not be called statically.
This function is deprecated. That means that future versions of this package may not support it anymore.
Fetching data with a connection created with prepareConnection() is faster then a created connection with connect()
Using prepareConnection()
<?php
require_once "Net/NNTP.php";
$nntp = new Net_NNTP;
$ret = $nntp->connect("news.php.net", 119, "php.pear.dev");
if( PEAR::isError($ret)) {
// handle error
} else {
// success
}
?>
void Net_NNTP::quit (
)
Close connection to a newsserver
This function can not be called statically.
array Net_NNTP::selectGroup (
string $newsgroup
)
Selects a specific newsgroup on the news server
string $newsgroup
- Name of the newsgroup to
access
array
- If the newsgroup exists an array
containing the message number of the first
(array key: ['first']
)
and the last message id (array key: ['last']
)
or a PEAR_Error, if fail.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article | Check the article ID or if your are still connected to the server ( Net_NNTP::isConnected()) |
This function can not be called statically.
Using selectGroup()
<?php
...
$ret = $nntp->connect("news.php.net");
if( PEAR::isError($ret)) {
// handle error
} else {
// success
$data = $nntp->selectGroup("php.pear.dev");
// Print the count of articles
echo "Count: ", $data['last'] - $data['first'];
}
?>
void Net_NNTP::setDebug (
boolean $on
= true
)
Enables or disables debug mode for Net_NNTP
boolean $on
- TRUE = debug on,
FALSE = debug off
This function can not be called statically.
array Net_NNTP::splitHeaders (
string $articleId
)
Returns all avaible header lines of a specified message of the current selected newsgroup into an array
string $articleId
- Message-ID or Message number
array
- if message exists the headers
as array or a PEAR_Error, if fail.
The array is an associative array with the header names as key.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | Different error messages | The messages are directly passed from the news server, in the most cases caused by calling a non existing article | Check the article ID or if your are still connected to the server ( Net_NNTP::isConnected()) |
This function can not be called statically.
Using splitHeaders()
<?php
...
$headers = $nntp->splitHeaders($msg_id);
if( PEAR::isError($headers)) {
// handle error
} else {
// success - print all headers line
foreach($headers as $headerName => $headerValue) {
echo $headerName.': '.$headerValue.'<br>';
}
}
?>
This module is EXPERIMENTAL. That means that the behaviour of these functions, these function names, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this module at your own risk.
This package is not documented yet.
This module is EXPERIMENTAL. That means that the behaviour of these functions, these function names, in concreto ANYTHING documented here can change in a future release of this package WITHOUT NOTICE. Be warned, and use this module at your own risk.
This package is not documented yet.
Low level NNTP client implementation.
This package is not documented yet.
default NNTP hostname: localhost
default NNTP port: 119
API for the Ping command
void
Net_Ping::Net_Ping
(
)
Please use Net_Ping::factory() instead of using this constructor directly.
string
Net_Ping::ping (
string
host
)
Executes a ping command
string $host
- the name of the server or the
IP-adress
string
-
the return message of the ping command
This function can not be called statically.
Sending a ping request
<?php
require_once "Net/Ping.php";
$ping = Net_Ping::factory();
if (PEAR::isError($ping)) {
echo $ping->getMessage();
} else {
$ping->setArgs(array('count' => 2));
var_dump($ping->ping('example.com'));
}
?>
boolean
Net_Ping::setArgs (
array
$args
)
Set the arguments for a ping command
array $args
-
an array of the arguments to set
Key name | Description | NOT supported on |
---|---|---|
"count" | Number of ping packages to send | |
"quiet" | Level of output verbosity | Windows |
"iface" | FreeBSD, Linux, Windows | |
"ttl" | ||
"timeout" | Time to wait for a ACK of a ping package | |
"size" | Size of a ping package | FreeBSD, Darwin, Linux, Windows |
boolean
-
Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | PING_INVALID_ARGUMENTS | A not supported argument was given | Check typing of argument (key name) and OS-support of the argument. |
This function can not be called statically.
Provides a POP3 class to access POP3 server.
Provides a POP3 class to access POP3 servers. Supports all POP3 commands including UIDL listings and APOP authentication.
Usage of this class compared to native PHP extensions such as IMAP is slow and may be feature deficient. If available you are STRONGLY recommended to use the PHP extensions.
Usage
<?php
include 'Net/POP3.php';
$pop3 =& new Net_POP3();
/*
* Connect to localhost on usual port
* If not given, defaults are localhost:110
*/
$pop3->connect('localhost', 110);
/*
* Login using username/password. APOP will
* be tried first if supported, then basic.
*/
$pop3->login('richard', 'Alien3');
/*
* Get the raw headers of message 1
*/
echo '<h2>getRawHeaders()</h2>';
echo '<pre>' . htmlspecialchars($pop3->getRawHeaders(1)) . '</pre>';
/*
* Get structured headers of message 1
*/
echo '<h2>getParsedHeaders()</h2> <pre>';
print_r($pop3->getParsedHeaders(1));
echo '</pre>';
/*
* Get body of message 1
*/
echo '<h2>getBody()</h2>';
echo '<pre>' . htmlspecialchars($pop3->getBody(1)) . '</pre>';
/*
* Get number of messages in maildrop
*/
echo '<h2>getNumMsg</h2>';
echo '<pre>' . $pop3->numMsg() . '</pre>';
/*
* Get entire message
*/
echo '<h2>getMsg()</h2>';
echo '<pre>' . htmlspecialchars($pop3->getMsg(1)) . '</pre>';
/*
* Get listing details of the maildrop
*/
echo '<h2>getListing()</h2>';
echo '<pre>';
print_r($pop3->getListing());
echo '</pre>';
/*
* Get size of maildrop
*/
echo '<h2>getSize()</h2>';
echo '<pre>' . $pop3->getSize() . '</pre>';
$pop3->disconnect();
?>
Net_POP3::Net_POP3 (
)
Constructor. Sets up the object variables, and instantiates the socket object.
This function can not be called statically.
boolean
Net_POP3::connect (
string $host
,
integer $port
)
Connects to the given host on the given port. Also looks for the timestamp in the greeting needed for APOP authentication.
string $host
- Hostname/IP address to connect
to
integer $port
- Port to use to connect to on
host
boolean
- Returns TRUE on success, FALSE on failure.
This function can not be called statically.
boolean
Net_POP3::deleteMsg (
integer $msg_id
)
Marks a message for deletion. Only will be deleted if the disconnect() method is called.
integer $msg_id
- Message to delete
mixed
- Returns TRUE on success, FALSE on failure.
This function can not be called statically.
boolean
Net_POP3::disconnect (
)
Disconnect function. Sends the QUIT command and closes the socket.
boolean
- Returns TRUE on success, FALSE on failure.
This function can not be called statically.
mixed
Net_POP3::getBody (
integer $msg_id
)
Returns the body of the message with given message number.
integer $msg_id
- Message number
mixed
- Either message body
or FALSE on error.
This function can not be called statically.
mixed
Net_POP3::getListing (
integer $msg_id
)
Listing message in maildrop. Combination of LIST/UIDL commands, returns an array of data.
integer $msg_id
- Optional message number
mixed
- Array of data or false on error.
This function can not be called statically.
mixed
Net_POP3::getMsg (
integer $msg_id
)
Returns the entire message with given message number.
integer $msg_id
- Message number
mixed
- Either entire message
or FALSE on error.
This function can not be called statically.
mixed
Net_POP3::getParsedHeaders (
integer $msg_id
)
Returns the headers of the specified message in an associative array. Array keys are the header names, array values are the header values. In the case of multiple headers having the same names, eg Received:, the array value will be an indexed array of all the header values.
integer $msg_id
- Message number
mixed
- Either array of headers
or FALSE on error.
This function can not be called statically.
mixed
Net_POP3::getRawHeaders (
integer $msg_id
)
Returns the raw headers of the specified message.
integer $msg_id
- Message number
strings
- Either raw headers
or FALSE on error.
This function can not be called statically.
mixed
Net_POP3::getSize (
)
Returns the size of the maildrop.
mixed
- Either size of maildrop or FALSE
on error.
This function can not be called statically.
boolean
Net_POP3::login (
string $user
string $pass
boolean $apop
)
Performs the login procedure. If there is a timestamp stored, APOP will be tried first, then basic USER/PASS.
string $user
- Username to use
string $pass
- Password to use
boolean $apop
- Whether to try APOP first
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
mixed
Net_POP3::numMsg (
)
Returns number of messages in this maildrop.
mixed
- Either number of messages or FALSE
on error.
This function can not be called statically.
Provides an API for scanning ports
boolean Net_Portscan::checkPort (
string $host
, integer $port
, integer
$timeout = 30
)
This function checks if there is a service available at the specified port on the specified machine.
string $host
- address of the host
to check
string $port
- port to check
string $timeout
-
time in seconds to wait for response from host
If you run into timeout problems despite setting this parameter to a reasonably high value, you need to make sure that the configuration directive
default_socket_timeout
in thephp.ini
configuration file does not force the maximum timeout down to a lower value.
boolean
- Returns TRUE on success, FALSE on failure.
This function can be called statically.
Using checkPort
<?php
require_once "Net_Portscan/Portscan.php";
if (Net_Portscan::checkPort("localhost", 80) == NET_PORTSCAN_SERVICE_FOUND) {
echo "There is a service on localhost on port 80 (" . Net_Portscan::getService(80) . ").\n";
}
?>
array Net_Portscan::checkPortRange (
string $host
, integer $minPort
, integer $maxPort
, integer
$timeout = 30
)
This function checks if there are services available at the specified ports on the specified machine.
string $host
- address of the host
to check
string $minPort
- lowest port to test
to check
string $maxPort
- highest port to test
to check
string $timeout
-
time in seconds to wait for every response from host
If you run into timeout problems despite setting this parameter to a reasonably high value, you need to make sure that the configuration directive
default_socket_timeout
in thephp.ini
configuration file does not force the maximum timeout down to a lower value.
array
- An associative array containing the
scanning results for each port.
This function can be called statically.
Using checkPortRange
<?php
require_once "Net_Portscan/Portscan.php";
echo "Scanning localhost ports 70-90\n";
$result = Net_Portscan::checkPortRange("localhost", 70, 90);
foreach ($result as $port => $element) {
if ($element == NET_PORTSCAN_SERVICE_FOUND) {
echo "A service has been found on port " . $port . ".\n";
} else {
echo "No service has been found on port " . $port . ".\n";
}
}
?>
integer Net_Portscan::getPort (
string $service
, string
$protocol = 'tcp'
)
This function returns the port which corresponds to service for the specified protocol as per /etc/services.
string $service
-
name of the service to check
string $protocol
-
access protocol
integer
-
port number of the service
This function can be called statically.
The port number is retrieved from the machine where Net_Portscan is executed, not from the remote host, which you possibly used in Net_Portscan::checkPort().
string
Net_Portscan::checkPortRange (
integer
$port
, string
$protocol = 'tcp'
)
This function returns the service associated with port for the specified protocol as per '/etc/services'.
integer $port
- port to check
string $protocol
-
access protocol
string
-
name of service running on the port
This function can be called statically.
The name of the service is retrieved from the machine where Net_Portscan is executed, not from the remote host, which you possibly used in Net_Portscan::checkPort().
Generic server package for creating daemons.
Net_Server provides an interface to create daemon application based on the sockets extension of PHP 4. In order to use Net_Server you need a PHP version greater or equal PHP 4.2, it is meant to be used with PHP-CLI.
Net_Server uses a driver based architecture that allows you to switch the drivers for your daemons. Currently a forking and a non-forking driver are available, others will follow.
As Net_Server provides an abstraction layer for the sockets extension, you do not need internal knowledge about the PHP functions. All you have to do is implement callback functions for events in your daemon, like handling new connections or incoming data.
The following example shows, how easy it is to build a forking server that receives data and sends it back to the user.
Creating a simple talkback daemon
#!/usr/local/bin/php
<?php
// server base class
require_once 'Net/Server.php';
// base class for the handler
require_once 'Net/Server/Handler.php';
/**
* simple example that implements a talkback.
*
* Normally this should be a bit more code and in a separate file
*/
class Net_Server_Handler_Talkback extends Net_Server_Handler
{
/**
* If the user sends data, send it back to him
*
* @access public
* @param integer $clientId
* @param string $data
*/
function onReceiveData( $clientId = 0, $data = "" )
{
$this->_server->sendData( $clientId, "You said: $data" );
}
}
// create a server that forks new processes
$server = &Net_Server::create('fork', 'localhost', 9090);
$handler = &new Net_Server_Handler_Talkback;
// hand over the object that handles server events
$server->setCallbackObject($handler);
// start the server
$server->start();
?>
This class must only be used to create a new server using the static method 'create()'.
To handle the events that happen while the server is running you have to create a new class that handles all events.
1 require_once 'myHandler.php';
2 require_once 'Net/Server.php';
3
4 $server = &
Net_Server::
create('fork', 'localhost', 9090);
5
6 $handler = &new myHandler;
7
8 $server->setCallbackObject($handler);
9
10 $server->start();
See Server/Handler.php for a baseclass that you can use to implement new handlers.
Net_Server
void& Net_Server::create (
string $type
, string $host
, integer $port
)
Currently two types of servers are supported:
'sequential', creates a server where one process handles all request from all clients sequentially
'fork', creates a server where a new process is forked for each client that connects to the server. This only works on *NIX
$type
type of the server
$host
hostname
$port
port
throws no exceptions thrown
static
This function can not be called statically.
This package is not documented yet.
PEAR
Net_Server_Driver
Class | Summary |
---|---|
Net_Server_Driver_Fork | Forking server class. |
Net_Server_Driver_Sequential | Sequential server class. |
string Net_Server_Driver::getLastSocketError (
mixed &$fd
)
This package is not documented yet.
&$fd
returns last error
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver::setCallbackObject (
object &$object
)
This package is not documented yet.
&$object
callback object
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver::setDebugMode (
mixed $debug
, string $dest = "stdout"
)
This package is not documented yet.
$debug
[text|htmlFALSE]
$dest
destination of debug message (stdout to output or filename if log should be written)
throws no exceptions thrown
This function can not be called statically.
This class will fork a new process for each connection. This allows you to build servers, where communication between the clients is no issue.
Events that can be handled:
onStart
onConnect
onClose
onReceiveData
PEAR
Net_Server_Driver_Fork
Net_Server_Driver_Fork Inherited Methods
Method Name | Summary |
---|---|
Net_Server_Driver::getLastSocketError() | return string for last socket error |
Net_Server_Driver::setCallbackObject() | register a callback object, that is used to handle all events |
Net_Server_Driver::setDebugMode() | set debug mode |
void Net_Server_Driver_Fork::broadcastData (
string $data
, array $exclude = array()
)
This package is not documented yet.
$data
data to send
$exclude
client ids to exclude
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Fork::closeConnection (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
array Net_Server_Driver_Fork::getClientInfo (
)
This package is not documented yet.
returns information about the client
throws no exceptions thrown
This function can not be called statically.
PEAR_Error Net_Server_Driver_Fork::getClients (
)
not possible with forking
throws no exceptions thrown
This function can not be called statically.
boolean Net_Server_Driver_Fork::isConnected (
integer $id
)
This package is not documented yet.
$id
client id
returns true if client is connected, false otherwise
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Fork::sendData (
string $data
, boolean $debugData
= true
)
This package is not documented yet.
$data
data to send
$debugData
flag to indicate whether data that is written to socket should also be sent as debug message
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Fork::serviceRequest (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Fork::setMaxClients (
int $maxClients
)
this is not possible as each client gets its own process
$maxClients
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Fork::shutDown (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Fork::start (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
This class will handles all connections in one server process. This allows you to build servers, where communication between the clients is easy. The drawback is that clients are served sequentially (hence the name). If you send large blocks of data to a client, the others will have to wait. For servers where communication between clients is not needed, use Net_Server_Fork instead.
Events that can be handled:
onStart
onConnect
onConnectionRefused
onClose
onIdle
onReceiveData
onShutdown
PEAR
Net_Server_Driver_Sequential
Net_Server_Driver_Sequential Inherited Methods
Method Name | Summary |
---|---|
Net_Server_Driver::getLastSocketError() | return string for last socket error |
Net_Server_Driver::setCallbackObject() | register a callback object, that is used to handle all events |
Net_Server_Driver::setDebugMode() | set debug mode |
int Net_Server_Driver_Sequential::acceptConnection (
resource &$socket
)
This package is not documented yet.
&$socket
socket that received the new connection
returns internal ID of the client
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Sequential::broadcastData (
string $data
, array $exclude = array()
)
This package is not documented yet.
$data
data to send
$exclude
client ids to exclude
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Sequential::closeConnection (
mixed $id = 0
, int $clientID
)
This package is not documented yet.
$id
$clientID
internal ID of the client
throws no exceptions thrown
This function can not be called statically.
array Net_Server_Driver_Sequential::getClientInfo (
int $clientId
)
This package is not documented yet.
$clientId
ID of the client
returns information about the client
throws no exceptions thrown
This function can not be called statically.
int Net_Server_Driver_Sequential::getClients (
)
This package is not documented yet.
returns amount of clients
throws no exceptions thrown
This function can not be called statically.
boolean Net_Server_Driver_Sequential::isConnected (
integer $id
)
This package is not documented yet.
$id
client id
returns true if client is connected, false otherwise
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Sequential::sendData (
int $clientId
, string $data
, boolean $debugData
= true
)
This package is not documented yet.
$clientId
ID of the client
$data
data to send
$debugData
flag to indicate whether data that is written to socket should also be sent as debug message
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Sequential::setMaxClients (
int $maxClients
)
This package is not documented yet.
$maxClients
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Sequential::shutDown (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Driver_Sequential::start (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
This package is not documented yet.
Net_Server_Handler
void Net_Server_Handler::onClose (
integer $clientId = 0
)
This handler is called, when a client disconnects from the server Available in:
Net_Server_Sequential
Net_Server_Fork
$clientId
unique id of the client, in Net_Server_Fork, this is always 0
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Handler::onConnect (
integer $clientId = 0
)
This handler is called, when a new client connects Available in:
Net_Server_Sequential
Net_Server_Fork
$clientId
unique id of the client, in Net_Server_Fork, this is always 0
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Handler::onConnectionRefused (
integer $clientId = 0
)
This handler is called, when a new client tries to connect but is not allowed to Available in:
Net_Server_Sequential
$clientId
unique id of the client
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Handler::onIdle (
)
This handler is called whenever the server is idle (has nothing to do), and that for a given number of seconds (timeout). Currently, only the sequential driver supports this handler.
The timeout can be specified by passing the desired number of seconds to
setIdleTimeout(). By default, the timeout is set to
NULL
, which means that the handler is deactivated.
This function can not be called statically.
void Net_Server_Handler::onReceiveData (
integer $clientId = 0
, string $data = ""
)
This handler is called, when a client sends data to the server Available in:
Net_Server_Sequential
Net_Server_Fork
$clientId
unique id of the client, in Net_Server_Fork, this is always 0
$data
data that the client sent
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Handler::onShutdown (
)
This handler is called, when the server is stopped. Available in:
Net_Server_Sequential
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Handler::onStart (
)
This handler is called, when the server starts. Available in:
Net_Server_Sequential
Net_Server_Fork
throws no exceptions thrown
This function can not be called statically.
void Net_Server_Handler::setServerReference (
object Net_Server_* &$server
)
This is done automatically when the handler is passed over to the server
&$server
object
throws no exceptions thrown
This function can not be called statically.
Name | Value | Line Number |
---|---|---|
NET_SERVER_ERROR_DRIVER_CORRUPT | 52 | 39 |
NET_SERVER_ERROR_NOT_SUPPORTED | 53 | 44 |
NET_SERVER_ERROR_PCNTL_REQUIRED | 54 | 49 |
NET_SERVER_ERROR_UNKNOWN_DRIVER | 51 | 34 |
Net_SmartIRC is a PHP class for communication with IRC networks, which conforms to the RFC 2812 (IRC protocol). It's an API that handles all IRC protocol messages. This class is designed for creating IRC bots, chats and show irc related info on webpages.
Net_SmartIRC is a PHP class for communication with IRC networks, which conforms to the RFC 2812 (IRC protocol). It's an API that handles all IRC protocol messages. This class is designed for creating IRC bots, chats and show irc related info on webpages.
This package implements the Simple Mail Transfer Protocol (SMTP), Internet's standard host-to-host mail transport protocol, using the Net_Socket:: class for the server connection. The SMTP provides mechanisms for the transmission of mail.
Net_SMTP
The Net_SMTP package supports the SMTP authentication standard (as defined by RFC-2554). Net_SMTP supports several authentication methods like DIGEST-MD5, CRAM-MD5, LOGIN and PLAIN.
The Net_SMTP package uses the PEAR_Error class for all of its error handling.
The Net_Socket package is used as the basis for all network communications.
The Auth_SASL package is an optional dependency. If it is available, the Net_SMTP package will be able to support the DIGEST-MD5 and CRAM-MD5 SMTP authentication methods. Otherwise, only the LOGIN and PLAIN methods will be available.
All of the Net_SMTP class's public methods return a PEAR_Error object if an error occurs. The standard way to check for a PEAR_Error object is by using PEAR::isError()
The Net_SMTP package supports the SMTP authentication standard (as defined by RFC-2554). The Net_SMTP package supports the following authentication methods, in order of preference:
The DIGEST-MD5 authentication method uses RSA Data Security Inc.'s MD5 Message Digest algorithm. It is considered the most secure method of SMTP authentication.
The DIGEST-MD5 authentication method is only supported if the AUTH_SASL package is available.
The CRAM-MD5 authentication method has been superseded by the DIGEST-MD5 method in terms of security. It is provided here for compatibility with older SMTP servers that may not support the newer DIGEST-MD5 algorithm.
The CRAM-MD5 authentication method is only supported if the AUTH_SASL package is available.
The LOGIN authentication method encrypts the user's password using the Base64 encoding scheme. Because decrypting a Base64-encoded string is trivial, LOGIN is not considered a secure authentication method and should be avoided.
The PLAIN authentication method sends the user's password in plain text. This method of authentication is not secure and should be avoided.
If secure socket transports have been enabled in PHP, it is possible to establish a secure connection to the remote SMTP server:
<?php
$smtp = new Net_SMTP('ssl://mail.example.com', 465);
?>
This example connects to mail.example.com on port 465 (a common SMTPS port) using the ssl:// transport.
By default, all outbound string data is quoted in accordance with SMTP standards. This means that all native Unix (\n) and Mac (\r) line endings are converted to Internet-standard CRLF (\r\n) line endings. Also, because the SMTP protocol uses a single leading period (.) to signal an end to the message data, single leading periods in the original data string are "doubled" (e.g. "..").
These string transformation can be expensive when large blocks of data are involved. For example, the Net_SMTP package is not aware of MIME parts (it just sees the MIME message as one big string of characters), so it is not able to skip non-text attachments when searching for characters that may need to be quoted.
Because of this, it is possible to extend the Net_SMTP class in order to implement your own custom quoting routine. Just create a new class based on the Net_SMTP class and reimplement the quotedata() method:
<?php
require 'Net/SMTP.php';
class Net_SMTP_custom extends Net_SMTP
{
function quotedata($data)
{
/* Perform custom data quoting */
}
}
?>
Note that the $data parameter will be passed to the quotedata() function by reference. This means that you can operate directly on $data. It also the overhead of copying a large $data string to and from the quotedata() method.
The Net_SMTP package contains built-in debugging output routines (disabled by default). Debugging output must be explicitly enabled via the setDebug() method:
<?php
$smtp->setDebug(true);
?>
The debugging messages will be sent to the standard output stream.
The following script demonstrates how a simple email message can be sent using the Net_SMTP package:
<?php
require 'Net/SMTP.php';
$host = 'mail.example.com';
$from = 'user@example.com';
$rcpt = array('recipient1@example.com', 'recipient2@example.com');
$subj = "Subject: Test Message\n";
$body = "Body Line 1\nBody Line 2";
/* Create a new Net_SMTP object. */
if (! ($smtp = new Net_SMTP($host))) {
die("Unable to instantiate Net_SMTP object\n");
}
/* Connect to the SMTP server. */
if (PEAR::isError($e = $smtp->connect())) {
die($e->getMessage() . "\n");
}
/* Send the 'MAIL FROM:' SMTP command. */
if (PEAR::isError($smtp->mailFrom($from))) {
die("Unable to set sender to <$from>\n");
}
/* Address the message to each of the recipients. */
foreach ($rcpt as $to) {
if (PEAR::isError($res = $smtp->rcptTo($to))) {
die("Unable to add recipient <$to>: " . $res->getMessage() . "\n");
}
}
/* Set the body of the message. */
if (PEAR::isError($smtp->data($subj . "\r\n" . $body))) {
die("Unable to send data\n");
}
/* Disconnect from the SMTP server. */
$smtp->disconnect();
?>
void constructor Net_SMTP::Net_SMTP (
string $host
= null
, int $port
= null
, string $localhost
= null
)
Constructor,Instantiates a new Net_SMTP object.
$host
The server to connect to.
$port
The port to connect to.
$localhost
The value to give when sending EHLO or HELO.
throws no exceptions thrown
This function can not be called statically.
mixedNet_SMTP::auth (
string $uid
, string $pwd
, string $method = ''
)
This function initializes an authentication session using the supported methods. These are, in order of preference: Digest-MD5,CRAMMD5,LOGIN, and PLAIN.
$uid
The userid to authenticate as.
$pwd
The password to authenticate with.
$method
The requested authentication method.If none is specified, the best supported method will be used.
$tls
Flag indicating whether or not TLS should be attempted.
$authz
An optional authorization identifier. If specified, this identifier will be used as the authorization proxy.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
mixed Net_SMTP::connect (
int $timeout
= null
)
Attempt to establish a connection with the SMTP server
$timeout
The timeout value (in seconds) for thesocket connection.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
mixedNet_SMTP::data (
string $data
)
This function sends the message body with the DATA command and terminates the message session.
$data
The message body to send.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
mixedNet_SMTP::disconnect (
)
Attempt to close the connection with the SMTP server.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
array Net_SMTP::getResponse (
)
Return a two-element array containing the last response from the SMPT server.
returns A two-element array: the first element contains the response code as an integer and the second element contains the response's arguments as a string.
throws no exceptions thrown
This function can not be called statically.
mixed Net_SMTP::helo (
string $domain
)
Send the HELO command which is the first part of a mail-sending SMTP transaction where the Internet extender first tries to send mail.
$domain
The domain name to say we are.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
booleanNet_SMTP::identifySender (
)
Backwards-compatibility method.identifySender()'s functionality is now handled internally.
returns This method always return true.
throws no exceptions thrown
This function can not be called statically.
mixedNet_SMTP::mailFrom (
string $sender
)
Send the MAIL FROM: command
$sender
The sender (reverse path) to set.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
mixedNet_SMTP::noop (
)
Send the NOOP command.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
void Net_SMTP::quotedata (
string &$data
)
This is provided as a separate public function to facilitate easier overloading for the cases where it is desirable to customize the quoting behavior.
&$data
The message text to quote. The string must be passed by reference, and the text will be modified in place.
throws no exceptions thrown
This function can not be called statically.
mixedNet_SMTP::rcptTo (
string $recipient
)
sends the RCPT TO SMTP protocol command
$recipient
The recipient (forward path) to add.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
mixed Net_SMTP::rset (
)
Sends the RSET SMTP protocol command.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
mixedNet_SMTP::saml_from (
string $path
)
Sends the SAML FROM SMTP protocol command.
$path
The reverse path to send.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
mixed Net_SMTP::send_from (
string $path
)
Sends the SEND FROM SMTP protocol command.
$path
The reverse path to send.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
voidNet_SMTP::setDebug (
boolean $debug
, callback $handler
)
Enables or disables debugging.
Once debugging is enabled, messages preceded by
"DEBUG:
" are being echoed out.
See also Debugging introduction
$debug
New value for the debugging flag.
$handler
Callback function that gets called with Net_SMTP object and message to display.
throws no exceptions thrown
This function can not be called statically.
mixed Net_SMTP::soml_from (
string $path
)
Sends the SOML FROM: SMTP protocol command.
$path
The reverse path to send.
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
mixed Net_SMTP::vrfy (
string $string
)
Sends the VRFY SMTP protocol command.
$string
The string to verify
returns Returns a PEAR_Error with an error message on any kind of failure, or true on success.
throws no exceptions thrown
This function can not be called statically.
Net_Socket provides a generic API for communication over TCP/IP-sockets.
boolean Net_Socket::connect (
string $addr
, integer $port
, boolean $persistent
= null
, integer $timeout
= null
, array $options
= null
)
Connect to the specified port. If called when the socket is already connected, it disconnects and connects again.
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
every | every |
The connection could not be established because
|
Check for server name, the connection to the net and possible firewalls on client or server side |
This function can not be called statically.
boolean Net_Socket::disconnect (
)
Disconnects froms server, closes the socket.
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | The connection to close was not open or is already closed. | Ensure a successfull call of connect() |
This function can not be called statically.
boolean Net_Socket::eof (
)
Test for the end of data transmission.
boolean
- TRUE, if no more data avaible
or connection is closed
This function can not be called statically.
mixed Net_Socket::gets (
integer $size
)
Returns a string containing data from the socket connection
integer $size
- maximum number of chars to recieve
string
- the data or a PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
array Net_Socket::getStatus (
)
Returns information about an existing socket resource.
array
- the data as array or a PEAR_Error
The content of the array is:
boolean 'timed_out'
- the socket timed out waiting for data
boolean 'blocked'
- blocking mode
boolean 'eof'
- indicates EOF event
integer 'unread_bytes'
- number of bytes left in the socket buffer
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
boolean Net_Socket::isBlocking (
)
Find out if the socket is in blocking mode.
boolean
- if TRUE, the connection is in
blocking mode
This function can not be called statically.
mixed Net_Socket::read (
integer $size
)
Read a specified amount of data. This is guaranteed to return, and has the added benefit of getting everything in one fread() chunk; if you know the size of the data you're getting beforehand, this is definitely the way to go.
integer $size
- maximum number of chars to recieve
mixed
- the data or a PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
mixed Net_Socket::readAll (
)
Read until the socket closes.
This function will not exit, if the socket is in blocking mode until the socket closes.
mixed
- the data or a PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
mixed Net_Socket::readByte (
)
Read a byte from the socket
mixed
- the data or a PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
mixed Net_Socket::readInt (
)
Read an integer from the socket.
mixed
- the data or a PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
string Net_Socket::readIPAddress (
)
Reads a IP from the socket. The function expects an integer which will converted to a IP address. This function only works with IPv4 addresses.
string
- the data or a PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
mixed Net_Socket::readLine (
)
Read until either the end of the socket or a newline, whichever comes first. Strips the trailing newline from the returned data.
mixed
- the data or a PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
mixed Net_Socket::readString (
)
Read a zeroterminated string ("\x00") from the socket.
mixed
- the data or a PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
mixed Net_Socket::readWord (
)
Read a word from the socket.
mixed
- the data or a PEAR_Error
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
"Word" means not a word in literary sense. A word is a statement of size for data.
boolean Net_Socket::setBlocking (
boolean $mode
)
Sets whether the socket connection should be blocking or not. A read call to a non-blocking socket will return immediately if there is no data available, whereas it will block until there is data for blocking sockets.
boolean $mode
- TRUE for blocking sockets,
FALSE for non blocking
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
boolean Net_Socket::setTimeout (
integer $seconds
, integer $microseconds
)
Sets the timeout value on socket descriptor, expressed in the sum of seconds and microseconds.
integer $seconds
- seconds part
integer $microseconds
- microseconds part
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
boolean Net_Socket::write (
string $data
)
Write data to a socket connection
string $data
- the data to write
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
boolean Net_Socket::writeLine (
string $data
)
Write a line of data to a socket connection, followed by a trailing "\r\n".
string $data
- the data to write
boolean
- Returns TRUE on success,
PEAR_Error on failure.
Error code | Error message | Reason | Solution |
---|---|---|---|
NULL | "not connected" | There is no open connection. | You must establish a connection before ( Net_Socket::connect()). |
This function can not be called statically.
Sending and recieving data
<?php
$socket = new Net_Socket() ;
// open connection
$socket->connect("http://www.example.com", 80, true, 30);
// Send data including linebreak
$socket->writeLine("String with data");
// receive data until linebreak
$result = $socket->readLine();
// receive a number of data
$result = $socket->read(512);
// close connection
$socket->disconnect();
?>
This package is not documented yet.
Net_Traceroute
object Net_Traceroute Net_Traceroute::factory (
)
Call this method to create a new instance of Net_Traceroute
throws no exceptions thrown
This function can not be called statically.
mixed Net_Traceroute::setArgs (
array $args
)
This package is not documented yet.
$args
Hash with options
returns true or PEAR_error
throws no exceptions thrown
This function can not be called statically.
object Net_Traceroute_Result Net_Traceroute::traceroute (
string $host
)
This package is not documented yet.
$host
hostname or IP of destination
throws no exceptions thrown
This function can not be called statically.
This package is not documented yet.
Net_Traceroute_Result
void Net_Traceroute_Result::factory (
array $result
, string $sysname
)
This package is not documented yet.
$result
Net_Traceroute result
$sysname
OS_Guess::sysname
throws no exceptions thrown
This function can not be called statically.
array Net_Traceroute_Result::getHops (
)
This package is not documented yet.
returns Hops
throws no exceptions thrown
see _hops
This function can not be called statically.
array Net_Traceroute_Result::getRawData (
)
This package is not documented yet.
returns raw data
throws no exceptions thrown
see _raw_data
This function can not be called statically.
string Net_Traceroute_Result::getSystemName (
)
This package is not documented yet.
returns OS_Guess::sysname
throws no exceptions thrown
see _sysname
This function can not be called statically.
string Net_Traceroute_Result::getTargetIp (
)
This package is not documented yet.
returns IP address
throws no exceptions thrown
see _target_ip
This function can not be called statically.
int Net_Traceroute_Result::getTTL (
)
This package is not documented yet.
returns TTL
throws no exceptions thrown
see _ttl
This function can not be called statically.
mixed Net_Traceroute_Result::getValue (
string $name
)
This package is not documented yet.
$name
property name
returns property value
throws no exceptions thrown
This function can not be called statically.
Name | Value | Line Number |
---|---|---|
NET_TRACEROUTE_CANT_LOCATE_TRACEROUTE_BINARY | 3 | 57 |
NET_TRACEROUTE_CANT_LOCATE_TRACEROUTE_BINARY_MSG | 'unable to locate the traceroute binary' | 51 |
NET_TRACEROUTE_FAILED | 54 | |
NET_TRACEROUTE_FAILED_MSG | 'execution of traceroute failed' | 48 |
NET_TRACEROUTE_HOST_NOT_FOUND | 1 | 55 |
NET_TRACEROUTE_HOST_NOT_FOUND_MSG | 'unknown host' | 49 |
NET_TRACEROUTE_INVALID_ARGUMENTS | 2 | 56 |
NET_TRACEROUTE_INVALID_ARGUMENTS_MSG | 'invalid argument array' | 50 |
NET_TRACEROUTE_RESULT_UNSUPPORTED_BACKEND | 4 | 58 |
NET_TRACEROUTE_RESULT_UNSUPPORTED_BACKEND_MSG | 'Backend not Supported' | 52 |
Provides an easy parsing of URLs
string Net_URL (
string $url = null
, boolean $useBrackets = true
)
Instantiates a new Net_URL object
string $url
-
The url to parse. By default this is getting $_SERVER['PHP_SELF']
boolean $useBrackets
-
Whether to use sqare brackets or not when there is multiple
query variable names using the same name.
Using Net_URL
<?php
require_once 'Net/URL.php';
$url = new Net_URL();
print_r($url->querystring);
?>
string setOption (
string $optionName
, string $value
)
Setting a Net_URL option
Name | Description |
---|---|
encode_query_keys | This will encode the query keys if set to true. By default set to false. |
string $optionName
-
The option name to set.
string $value
-
The value of the option to set.
Using Net_URL
<?php
require_once 'Net/URL.php';
$url = new Net_URL;
$url->setOption('encode_query_keys', true);
print_r($url->querystring);
?>
A package to map (and generate) pretty urls.
Net_URL_Mapper makes it easy to map an URL to application logic.
The URL syntax is similar to what can be found in Ruby on Rails or Python Routes module. The API and the design are different and try to offer a more OO approach while making use of interesting PHP5 only features. The URL syntax and the possibilities it offers are also more advanced and elegant than what can be found in the Zend Framework Controller at the moment.
Net_URL_Mapper does not perform the dispatching so it can be used with your own dispatcher. This way, it is a lot more flexible and reusable, and consequently more compliant with PEAR objectives. Net_URL_Mapper objectives are to provide a simple, common and flexible way to build nice URLs for your web applications and then use the results to do something. Dealing with Net_URL_Mapper results is left as an excercise to the developers.
Net_URL_Mapper can handle different types of mapping:
In the following examples we assume that URLs are mapped to a desginated controller
and an action. But the controller
and action
keys provided could be called anything and are not mandatory.
The following code snippet maps the URL /home
:
<?php
require_once 'Net/URL/Mapper.php';
$m = Net_URL_Mapper::getInstance();
$m->connect('home', array('controller' => 'index', 'action' => 'index'));
var_dump($m->match($_SERVER['REQUEST_URI']));
?>
The following code snippet maps a URL such as /news/1
,
/news/2
to a designated controller and action.
<?php
require_once 'Net/URL/Mapper.php';
$m = Net_URL_Mapper::getInstance();
$m->connect('news/:id', array('controller' => 'news', 'action' => 'read'));
var_dump($m->match($_SERVER['REQUEST_URI']));
?>
The following code snippet maps all URLs with /content
to a
controller called content
and an action called
display
. Optionally, we'll determine the section on the page using a
wildcard. The default for section is #toc
(Table Of Contents).
<?php
require_once 'Net/URL/Mapper.php';
$m = Net_URL_Mapper::getInstance();
$path = 'content/*(section)';
$defaults = array(
'controller' => 'content',
'action' => 'display',
'section' => '#toc',
);
$m->connect($path, $defaults);
var_dump($m->match($_SERVER['REQUEST_URI']));
?>
The coming examples assume/need the following setup:
.htaccess
init.php
<?php
require_once 'Net/URL/Mapper.php';
$m = Net_URL_Mapper::getInstance();
?>
The following snippets maps a couple blog URLs.
blog-router.php
<?php
require 'init.php';
$m->connect('blog', array('page' => 'frontpage.php'));
$m->connect(
'blog/feed/:type',
array('page' => 'feed.php', 'type' => 'rss')
);
$m->connect(
'blog/archives/:year/:month',
array(
'page' => 'archive.php',
'year' => date('Y'),
'month' => date('m')
)
);
$route = $m->match($_SERVER['REQUEST_URI']);
if ($route === null) {
// no match
$route = array(
'page' => 'frontpage.php',
);
}
include dirname(__FILE__) . '/inc/' . $route['page'];
?>
A really simple example for Zend Framework-style URL routing. ;-) This is not meant to work in production, it's not necessarily secure and serves as a proof of concept or base for a real implementation.
zf-router.php
<?php
require 'init.php';
$path = '/:module/:controller/:action';
$defaults = array(
'module' => 'default',
'controller' => 'index',
'action' => 'index',
);
$m->connect($path, $defaults);
$route = $m->match($_SERVER['REQUEST_URI']);
// Fix the controller's name to adhere to ZF's standard, FooController
$controllerClass = ucfirst(strtolower($route['controller'])) . 'Controller';
// Fix the action's name to adhere to ZF's standard, barAction()
$controllerAction = strtolower($route['action']) . 'Action';
// load the controller class
require 'app/modules/' . $route['module'] . '/' . $class . '.php';
$controllerObj = new $controllerClass;
call_user_func(array($controllerObj, $controllerAction)); // pseudo dispatcher
?>
The following snippet explains how to generate a fancy URL automatically from the defined routes.
generate-url.php
<?php
require 'init.php';
$m->connect('blog', array('page' => 'frontpage.php'));
$m->connect(
'blog/feed/:type',
array('page' => 'feed.php', 'type' => 'rss')
);
$m->connect(
'blog/archives/:year/:month',
array(
'page' => 'archive.php',
'year' => date('Y'),
'month' => date('m')
)
);
$url1 = $m->generate(array(
'page' => 'feed.php',
'type' => 'atom',
)); // blog/feed/atom
$url2 = $m->generate(array(
'page' => 'archive.php',
'year' => '2008',
'month' => '06',
)); // blog/archives/2008/06
?>
Provides an API for accessing Whois services
string Net_Whois::query (
string $query
, string $server
)
Executes a Whois query on a server
string $query
-
the Whois database object to look up
string $server
- the name of the server
or its IP address
string
- the data from the Whois request.
Error code | Error message | Reason | Solution |
---|---|---|---|
every |
"
Error connecting to server
(Net_Socket says: Error-Message .)
"
|
Connection to server failed | Check typing of server address and make sure the host is connected to the network. |
You will not get a PEAR_Error, if the query fails due to a not existing whois object. This can be only done by checking the data returned by query().
Using Whois-query()
<?php
require_once "Net/Whois.php";
$server = "whois.denic.de";
$query = "phpcrawler.de"; // get information about
// this domain
$whois = new Net_Whois;
$data = $whois->query($query, $server);
echo $data;
?>
WebFinger client library for PHP.
Discover meta data about users by just their email address. Discoverable data may be the user's OpenID, profile page URL, link to portable contacts, hcard, foaf and other user pages.
Distributed social networks use WebFinger to distribute public encryption keys, OStatus and Salmon URLs.
Net_WebFinger supports draft-ietf-appsawg-webfinger-13 and can fall back to RFC 6415 (host-meta + lrdd).
At first, include the PHP file, create a
Net_WebFinger
object and run
finger()
on it, passing the identifier
(often the e-mail address, always user@host
).
finger()
returns a
Net_WebFinger_Reaction object
which you can use to get links from.
<?php
require_once 'Net_WebFinger.php';
$wf = new Net_WebFinger();
$reaction = $wf->finger('user@example.org');
?>
Now you have access to the links, errors and security information.
The Net_WebFinger_Reaction object you get from Net_WebFinger::finger() lets you access the the user's links in three ways:
By one of the short name properties.
The get()
method.
Iterate with foreach()
over the reaction object.
Often used link relations have a dedicated "short name" in Net_WebFinger to make accessing the link easy:
Short name | Link relation URL |
---|---|
contacts |
http://portablecontacts.net/spec/1.0
|
hcard |
http://microformats.org/profile/hcard
|
openid |
http://specs.openid.net/auth/2.0/provider
|
profile |
http://webfinger.net/rel/profile-page
|
xfn |
http://gmpg.org/xfn/11
|
You can use each short name as property on the reaction object like this:
<?php
require_once 'Net/WebFinger.php';
$wf = new Net_WebFinger();
$react = $wf->finger('user@example.org');
if ($react->openid !== null) {
echo 'OpenID provider found: ' . $react->openid . "\n";
}
?>
Net_WebFinger_Reaction::get() can always be used to access any link, especially those that have no short name. Just pass the URL of the link relation.
It returns NULL
in case the link is not available,
and the string if its there.
<?php
require_once 'Net/WebFinger.php';
$wf = new Net_WebFinger();
$react = $wf->finger('user@example.org');
$openIdProvider = $react->get('http://specs.openid.net/auth/2.0/provider');
if ($openIdProvider !== null) {
echo 'OpenID provider found: ' . $openIdProvider . "\n";
}
?>
You may use foreach()
on the
Net_WebFinger_Reaction
object to get all links.
This will give you a XML_XRD_Element_Link object for each single link.
<?php
require_once 'Net/WebFinger.php';
$wf = new Net_WebFinger();
$react = $wf->finger('user@example.org');
foreach ($react as $link) {
echo 'Link: ' . $link->rel . ' to ' . $link->href . "\n";
}
?>
The underlying XRD files will be retrieved via SSL when possible, with fallback to normal HTTP. In the latter case, the XRD files need to have valid signatures in order to be seen as secure.
The XRD subject is also verified. When it does not match the host name of the email address, then the information are seen as insecure.
You should not trust the information if they are not secure.
<?php
require_once 'Net/WebFinger.php';
$wf = new Net_WebFinger();
$react = $wf->finger('user@example.org');
if (!$react->secure) {
die("Those data may not be trusted\n");
}
?>
You often still want to use the data, since not all hosts have SSL enabled.
Net_WebFinger does not throw any exceptions. If an error occurs during WebFinger discovery, it is stored as Net_WebFinger_Error object in the reaction's $error property. If you really need it, you can access it there.
When accessing links that are not defined, you will get a
NULL
value instead of a string.
With caching, all retrieved XRD/JRD files will be stored locally, which leads to faster lookup times when the same identifier (email address) is loaded again.
You should use caching when your application often does WebFinger requests. For older RFC 6415-based webfinger implementations, it reduces the number of HTTP requests to the same server from 2 to 1 for each lookup.
<?php
require_once 'Net/WebFinger.php';
require_once 'Cache.php';
$wf = new Net_WebFinger();
$wf->setCache(
new Cache('file', array('cache_dir' => sys_get_temp_dir() . '/myapp'))
);
$react = $wf->finger('user@example.org');
$openIdProvider = $react->get('http://specs.openid.net/auth/2.0/provider');
?>
By default, the HTTP(S) XRD files are loaded by XML_XRD internally using file_get_contents.
If you want to set custom HTTP request headers or modify redirection handling, you may use an own HTTP adapter that will be used to fetch the files:
<?php
require_once 'HTTP/Request2.php';
require_once 'Net/WebFinger.php';
$req = new HTTP_Request2();
$req->setConfig('follow_redirects', true);//needed for full compatibility
$req->setHeader('User-Agent', 'MyApp 1.42');
$wf = new Net_WebFinger();
$wf->setHttpClient($req);
$react = $wf->finger('foo@example.org');
?>
Current specification: draft-ietf-appsawg-webfinger-13
XML_XRD package to load and create the XRD files; may be used to serve WebFinger on your own server.
Scans for wireless networks
Net_Wifi uses the command line
tools iwconfig
and iwlist
to gather information about local wireless network interfaces,
and to give information about wireless networks reachable by
your computer.
To work with the class, you need to make sure that both programs
are found. By default, /sbin/
and
/usr/sbin/
are looked up to find them when
the class is instantiated.
If that fails, the system's which
tool
is used to determine the location.
You can retrieve and set their locations with getPathIwconfig() and setPathIwconfig(), as well as getPathIwlist() and setPathIwlist().
At first, you can get a list of network interfaces on your computer via getSupportedInterfaces(). It returns an array of strings that are the names of the network interfaces, e.g. "eth1" or "wlan0".
The full information about the state of a network interface can be retrieved with getCurrentConfig(). It returns a Net_Wifi_Config object. To just check if an interface is connected, use the isConnected() method also takes the interface name as only parameter.
A Net_Wifi_Config looks like this:
Net_Wifi_Config {
activated => bool(true)
associated => bool(true)
ap => string(17) "00:03:C9:44:34:2C"
ssid => string(4) "free"
mode => string(7) "managed"
nick => NULL
rate => string(2) "54"
power => string(2) "20"
protocol => string(12) "IEEE 802.11g"
rssi => string(3) "-37"
}
To scan for available wireless networks in your environment, use the scan() method. It takes the network interface name as only parameter and returns an array of Net_Wifi_Cell objects.
You get an array of local wireless interfaces via the getSupportedInterfaces() method.
A cell object looks like this:
Net_Wifi_Cell {
cell => string(2) "01"
mac => string(17) "00:03:C9:44:34:2C"
ssid => string(8) "<hidden>"
mode => string(6) "master"
channel => int(1)
encryption=> bool(false)
frequency => NULL
protocol => string(13) "IEEE 802.11bg"
rate => float(54)
rates => array(12) {
0 => float(1)
1 => float(2)
2 => float(5.5)
3 => float(6)
}
rssi => string(3) "-39"
beacon => int(336)
quality => string(6) "90/100"
}
This package contains a parser for URI Templates as defined in the URI Template draft that is currently being proposed to the IETF.
Please note that the standardization initiative regarding URI Templates is not yet finished. Thus the API and the inner workings of this package may undergo major revisions in the future.
URI Templates are strings that contain embedded variables that are transformed into URIs after embedded variables are substituted.
More information about URI Templates can be found at the following locations:
Constructing or Traversing URIs? on XML.com
In a nutshell, URI Templates allow actors to specify a certain structure for URIs in order to make it possible for other actors to fill the structure with concrete information later. Joe Gregorio has posted an example of a possible use case.
URI_Template requires PHP 5.
The URI_Template package can be installed using the PEAR installer command pear install URI_Template.
Alternative installation methods for situations where one has no access to the pear installer command can be found in the general installation chapter.
Uninstalling the package can be done with pear uninstall URI_Template.
<?php
require_once "URI/Template.php";
$values = array("a" => "foo", "b" => "bar", "data" => "10,20,30",
"points" => array(10, 20, 30), "list0" => array(),
"str0" => "", "reserved" => ":/?#[]@!$&'()*+,;=",
"a_b" => "baz");
$t = new URI_Template("/{-append|/|a}{-opt|data|points}{-neg|@|a}{-prefix|#|b}");
echo $t->substitute($values); /* /foo/data#bar */
$t = new URI_Template("relative/{reserved}/");
echo $t->substitute($values); /* relative/%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D/ */
$t = new URI_Template("http://example.org/{foo=%25}/");
echo $t->substitute($values); /* http://example.org/%25/ */
$t = new URI_Template("http://example.org/?{-join|&|a,data}");
echo $t->substitute($values); /* http://example.org/?a=foo&data=10%2C20%2C30 */
$t = new URI_Template("http://example.org/?d={-listjoin|,|points}&{-join|&|a,b}");
echo $t->substitute($values); /* http://example.org/?d=10,20,30&a=foo&b=bar */
$t = new URI_Template("http://example.org/?d={-listjoin|,|list0}&{-join|&|foo}");
echo $t->substitute(array()); /* http://example.org/?d=& */
$t = new URI_Template("http://example.org/?d={-listjoin|&d=|points}");
echo $t->substitute($values); /* http://example.org/?d=10&d=20&d=30 */
$t = new URI_Template("http://example.org/{a}{b}/{a_b}");
echo $t->substitute($values); /* http://example.org/foobar/baz */
$t = new URI_Template("http://example.org/{a}{-prefix|/-/|a}/");
echo $t->substitute($values); /* http://example.org/foo/-/foo/ */
?>
Provides Packages for working with numbers
Numbers_Roman provides methods
to convert Arabic numbers like 23
into Roman numerals
like XXIII
and back.
Both class methods are static; you do not need to instantiate an object.
The static method toNumeral
does exactly this.
It can be used for numbers from 1
to
5 999 999
.
Using it to convert higher numbers works, but does not produce historically
correct results.
The second parameter $uppercase
determines if the letters
should be UPPERCASE (default) or not.
Parameter number 3 sets if HTML code for overscores shall be generated;
this is necessary for numbers greater than 3999. If the parameter is set to
false, letters will be prefixed with an underscore _
.
Converting Arabic numbers to Roman numerals
<?php
require_once 'Numbers/Roman.php';
echo Numbers_Roman::toNumeral(23);
//returns: XXIII
echo Numbers_Roman::toNumeral(23, false);
//returns: xxiii
?>
Using the static method toNumber
you can convert Roman
numerals like XLII
to Arabic numbers like
42
.
Letters prefixed with an underscore represent numbers larger than 1000.
Roman | Arabic |
---|---|
I | 1 |
V | 5 |
X | 10 |
L | 50 |
C | 100 |
D | 500 |
M | 1000 |
S, _V | 5000 |
R, _X | 10 000 |
P, _L | 50 000 |
Q, _C | 100 000 |
O, _D | 500 000 |
N, _M | 1 000 000 |
Converting Roman numerals to Arabic numbers
<?php
require_once 'Numbers/Roman.php';
echo Numbers_Roman::toNumber('XLII');
//returns: 42
?>
Provides Packages to work with/get info from the PEAR core
PEAR_Frontend_Gtk is a PHP-Gtk1 based frontend for PEAR. It allows you to view, install and uninstall packages from different channels.
You can start the program with pear -G if the package is installed.
PEAR_Frontend_Gtk2 is a PHP-Gtk2 based frontend for PEAR. It allows you to view, install and uninstall packages from different channels.
Since PEAR 1.4.6, you can start the program with pear -G if the package is installed.
The program requires the php-gtk extension that is not a PEAR/PECL package and can be obtained from http://gtk.php.net/.
Displays Information about your current PEAR install. Akin to PHP's phpinfo().
You can download and use it for free. But don't delete the copyright notice. You can read terms of the PHP license.
YES if there is no answer in this Guide and if you are ready to share some informations such as : your configuration (platform Win *nix mac, PHP version, PEAR packages installed) and perharps your script.
You can report it with the bug tracker at PEAR.
PEAR (an acronym for PHP Extension and Application Repository) is a framework and distribution system for reusable PHP components.
Don't forget to read also the PEAR Manual and PEAR FAQ.
If your system file (see command pear config-show) does not exists, or is locate in another directory, then you should give this information on PEAR_Info class constructor.
<?php
// @link http://pear.php.net/bugs/bug.php?id=12878
// Search for pear.conf inside /etc/pear/
require_once 'PEAR/Info.php';
$pear_dir = '';
$user_file = '';
$syst_file = '/etc/pear/pear.conf';
$info = new PEAR_Info($pear_dir, $user_file, $syst_file);
$info->display();
?>
First copy the default stylesheet named pearinfo.css
(locate in {data_dir}/PEAR_Info
:
see command pear config-show) as a pattern.
Then second, change colors and whatever you want until you keep all CSS selectors.
See for example the blue layout pattern skin locate in package installation
under name {doc_dir}/PEAR_Info/examples/blueskin.css
.
Last, as {doc_dir}/PEAR_Info/examples/pear_info2.php
script,
set the new stylesheet with PEAR_Info::setStyleSheet
.
<?php
require_once 'PEAR/Info.php';
$info = new PEAR_Info();
$info->setStyleSheet(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'blueskin.css');
$info->display();
?>
By default PEAR_Info display only informations from pear.php.net
channel.
To remove this limit, give as option to the class constructor an empty channels list, as below :
<?php
require_once 'PEAR/Info.php';
$pear_dir = '';
$user_file = '';
$syst_file = '/opt/lampp/etc/pear.conf';
$options = array('channels' => array() );
$info = new PEAR_Info($pear_dir, $user_file, $syst_file, $options);
$info->display();
?>
If you want to filter package listing from a channel list, then give the full qualified list; something like :
<?php
$options = array('channels' => array('pear.php.net', 'pear.phpunit.de', '__uri') );
?>
With static method PEAR_Info::packageInstalled
, you
can check installation of a package from all channels declared, and also
filter by version.
<?php
require_once 'PEAR/Info.php';
$inst = PEAR_Info::packageInstalled('Role_Web', '1.1.0', 'pearified');
if ($inst === false) {
echo 'sorry it seems that "pearified/Role_Web" package is not installed'
. ' or version is less than 1.1.0';
}
?>
Initial PEAR public release occured at end of April 2003.
Moved output of the PEAR_Info
to PEAR_Info::show()
Local PEAR logo copy included
Added PEAR Configuration section to the output
Allows Off-Line viewing
Shows *all* installed packages, whether in PEAR or not
You can now specify a custom PEAR config file
Added static method,
PEAR_Info::packageInstalled()
.
Allows to check if a package is installed or not.
Easy change look and feel with a simple CSS file
Show packages dependencies list
HTML page output is XHTML compliant
Introduce a full and flexible configuration system to show only information you want
Show channel list
Show maintainer inactive status in Credits page
Support REST 1.0 protocol
Drop support of package xml 1.0
Credits page is now customizable with PEAR_INFO_CREDITS_* constant
You may have a standalone html page (default behavior) with PEAR_INFO_FULLPAGE, or part of it
Removed support of PEAR 1.3.x
Upgrade requirement to PHP 4.3.0 and PEAR 1.5.4 (to avoid security vulnerability)
Coding Standard fixes (errors/warnings) following recommandation by PHP_CodeSniffer
PEAR_Info is also available now on command line (CLI), and display a text report as phpinfo() did.
PEAR_Info obtains and displays information about your current PEAR Install. The PEAR_Info page features an A-Z index for easy searching, aswell as anchors for each package in the form of pkg_Package_Name (i.e. url.tld/pearinfo.php#pkg_PEAR_Info) PEAR_Info also features a full 'PEAR Credits' page, with information about the authors of the packages you currently have installed. PEAR_Info will also display any later versions that are available from PEAR to help keep you up to date.
Using PEAR_Info
<?php
require_once 'PEAR/Info.php';
/*
If you need to set a http_proxy value at run-time that is not set in
your user or system pear configuration file, you can use the
following, this must be called BEFORE instantiating the PEAR_Info object
*/
PEAR_Info::setProxy('your.proxy.here');
/*
Optional pear_dir variable, allows you to choose where your PEAR
install is, in case it's not found
*/
$pear_dir = "/path/to/your/pear/files";
/*
Instantiate PEAR_Info object
*/
$info = new PEAR_Info($pear_dir);
/*
Show PEAR_Info output
*/
$info->display();
?>
The output may be customized by passing one or more of the following constants bitwise values summed together in the optional options hash parameter. One can also combine the respective constants or bitwise values together with the or operator.
Name | Value | Description |
---|---|---|
PEAR_INFO_GENERAL | 1 | The configuration file location, PEAR logo and current install version. |
PEAR_INFO_CREDITS | 2 | A link to PEAR Credits page. See also PEAR_INFO_CREDITS_* constants. |
PEAR_INFO_CONFIGURATION | 4 | All PEAR settings (keys and values). |
PEAR_INFO_CHANNELS | 8 | List available channels |
PEAR_INFO_PACKAGES | 4080 | All package informations. See all others PEAR_INFO_PACKAGES_* constants. |
PEAR_INFO_PACKAGES_CHANNEL | 2048 | Show the package channel |
PEAR_INFO_PACKAGES_SUMMARY | 1024 | A short description of the package |
PEAR_INFO_PACKAGES_VERSION | 512 | Package version with state and release date. |
PEAR_INFO_PACKAGES_LICENSE | 256 | License of the package release |
PEAR_INFO_PACKAGES_DESCRIPTION | 128 | A long description of the package |
PEAR_INFO_PACKAGES_DEPENDENCIES | 64 | Show package dependency list |
PEAR_INFO_PACKAGES_XML | 32 | Tell what PEAR packager and package xml version (1.0 or 2.0) was used to build and install the package. |
PEAR_INFO_PACKAGES_UPDATE | 16 | Show the latest version available. Displayed only if different than current install version. |
PEAR_INFO_ALL | 4095 | Shows all of the above. This is the default value. |
Name | Value | Description |
---|---|---|
PEAR_INFO_CREDITS_ALL | 61440 | All credits informations. See all others PEAR_INFO_CREDITS_* constants. |
PEAR_INFO_CREDITS_GROUP | 4096 | Show PEAR Group members list |
PEAR_INFO_CREDITS_DOCS | 8192 | Show PEAR Documentation Team list |
PEAR_INFO_CREDITS_WEBSITE | 16384 | Show PEAR Website Team list |
PEAR_INFO_CREDITS_PACKAGES | 32768 | Show authors (and roles) of package maintainers |
Name | Value | Description |
---|---|---|
PEAR_INFO_FULLPAGE | 65536 | Indicates that a complete stand-alone HTML page needs to be printed including the information indicated by the other flags. This is the default value. |
Customize PEAR_Info output
<?php
require_once 'PEAR/Info.php';
/*
Optional resume option, allows you to choose what information to display.
Here we will display a quick list of package (version only) group by channel.
*/
$options = array('resume' => PEAR_INFO_FULLPAGE |
PEAR_INFO_GENERAL | PEAR_INFO_CHANNELS | PEAR_INFO_PACKAGES_VERSION,
'channels' => array()
);
/*
Instantiate PEAR_Info object, using PEAR default install configuration
*/
$info = new PEAR_Info('', '', '', $options);
/*
Set you own presentation size and colors, with a simple style sheet.
*/
$css_file = "/path/to/your/file.css";
$info->setStyleSheet($css_file);
/*
Show PEAR_Info output
*/
$info->display();
?>
void constructor PEAR_Info::PEAR_Info (
string $pear_dir
= ''
, string $user_file
= ''
, string $system_file
= ''
, array $options
= null
)
$user_file
,$system_file
and$options
parameters are available since version 1.7.0RC1, allowing to define more easily the PEAR configuration files, and choose what information to display akin to PHP's phpinfo().
$pear_dir
(optional) The PEAR base install directory
$user_file
(optional) file to read PEAR user-defined options from
$system_file
(optional) file to read PEAR system-wide defaults from
$options
(optional) configure PEAR information output
throws no exceptions thrown
since version 1.0.1 (2003-04-24)
This function can not be called statically.
<?php
require_once 'PEAR/Info.php';
// Display only PEAR version and logo, reference to config file, list of channels
// and quick list of packages (with only version info) installed thru channels
// pear, __uri, pecl.
$options = array('resume' => PEAR_INFO_GENERAL | PEAR_INFO_CHANNELS | PEAR_INFO_PACKAGES_VERSION,
'channels' => array('pear.php.net', '__uri', 'pecl.php_net')
);
// Give PEAR install directory where to find pear.ini or pearsys.ini files
$info = new PEAR_Info('c:\wamp\php', '', '', $options);
$info->display();
?>
void PEAR_Info::display (
)
Displays PEAR_Info output depending of style applied (style sheet).
throws no exceptions thrown
since version 1.7.0RC1 (2007-07-01)
This function can not be called statically.
array
PEAR_Info::getMembers
(
string
$group
= 'all'
,
bool
$sort
= true
)
Retrieve the members list of PEAR group, PEAR doc team, or PEAR website team
$group
(optional) Member list category. Either president, group, docs or website
$sort
(optional) Return a member list sorted in alphabetic order
throws no exceptions thrown
since version 1.7.0RC3 (2007-07-10)
This function can not be called statically.
string PEAR_Info::getStyleSheet (
boolean $content
= true
)
Default behavior is to return css string contents.
Sets $content
parameter to FALSE will return css filename
reference (defined by setStyleSheet function).
Easy for a <link rel="stylesheet" type="text/css"
href="" /> html tag integration (see example below).
$content
(optional) Either return css filename or string contents
throws no exceptions thrown
since version 1.7.0RC1 (2007-07-01)
This function can not be called statically.
string - returns css filename or css string contents
<?php
// require the PEAR_Info file
require_once 'PEAR/Info.php';
class PEAR_Info3 extends PEAR_Info
{
function PEAR_Info3($pear_dir = '', $user_file = '', $system_file = '')
{
$this->__construct($pear_dir, $user_file, $system_file);
}
function toHtml()
{
$styles = basename($this->getStyleSheet(false));
$body = $this->info;
$html = <<<HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
<meta name="author" content="Laurent Laville" />
<title>My PEAR_Info()</title>
<link rel="stylesheet" type="text/css" href="$styles" />
</head>
<body>
<div id="header">
<h1>Laurent-Laville.org</h1>
</div>
<div id="footer">
</div>
<div id="contents">
$body
</div>
</body>
</html>
HTML;
return $html;
}
}
// Create PEAR_Info object
$info = new PEAR_Info3();
// set your own styles, rather than use the default stylesheet
$info->setStyleSheet(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pearinfo3.css');
// Display PEAR_Info output
$info->display();
?>
boolean PEAR_Info::packageInstalled (
string $name
, string $version
= null
, string $channel
= null
, string $user_file
= ''
, string $system_file
= ''
)
Simple function to check if a package is installed under user or system PEAR installation. Minimal version and channel info are supported.
$name
Package name
$version
(optional) The minimal version that should be installed
$channel
(optional) The package channel distribution
$user_file
(optional) file to read PEAR user-defined options from
$system_file
(optional) file to read PEAR system-wide defaults from
since version 1.6.0 (2005-01-03)
throws no exceptions thrown
This function should be called statically.
<?php
require_once 'PEAR/Info.php';
$res = PEAR_Info::packageInstalled('Role_Web', '1.1.0', 'pearified');
if ($res) {
print "Package pearified/Role_Web 1.1.0 or greater is installed \n";
} else {
print "Package pearified/Role_Web is not yet installed \n";
}
$res = PEAR_Info::packageInstalled('PEAR_PackageFileManager');
if ($res) {
print "Package pear/PEAR_PackageFileManager is installed \n";
} else {
print "Package pear/PEAR_PackageFileManager is not yet installed \n";
}
?>
void
PEAR_Info::pearImage
(
)
Display the PEAR logo (gif image) on browser output
throws no exceptions thrown
since version 1.0.1 (2003-04-24)
This function can not be called statically.
boolean PEAR_Info::setProxy (
string $proxy
)
Sets http_proxy config setting at runtime.
Since version 1.7.0RC1, it become unnecessary, unless you don't want to use the http_proxy config setting of your PEAR config file.
$proxy
PEAR HTTP Proxy Server Address
since version 1.0.6 (2003-05-11)
throws no exceptions thrown
This function should be called statically.
boolean - TRUE if the http proxy server address was set successfully, FALSE otherwise.
boolean PEAR_Info::setStyleSheet (
string $css
= null
)
Sets the custom style sheet (colors, sizes) to applied to PEAR_Info output. If you don't give any parameter, you'll then apply again the default style.
$css
(optional) File to read user-defined styles from
throws no exceptions thrown
since version 1.7.0RC1 (2007-07-01)
This function can not be called statically.
If you don't want to have any style applied, then build an empty style sheet (css file) and give it as function parameter (See example below).
boolean - TRUE if custom styles, FALSE if default styles applied.
<?php
// require the PEAR_Info file
require_once 'PEAR/Info.php';
// Create PEAR_Info object
$info = new PEAR_Info();
// set your own styles, rather than use the default stylesheet
$info->setStyleSheet(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'blueskin.css');
// don't use any style (empty.css is an empty file)
//$info->setStyleSheet(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'empty.css');
// Display PEAR_Info output
$info->display();
?>
void PEAR_Info::show (
)
Displays PEAR_Info output depending of style applied (style sheet).
deprecated use display() instead
throws no exceptions thrown
since version 1.0.1 (2003-04-24)
This function can not be called statically.
string PEAR_Info::toHtml (
)
Returns html code. This code is XHTML 1.1 compliant since version 1.7.0
throws no exceptions thrown
since version 1.7.0RC1 (2007-07-01)
This function can not be called statically.
The PEAR_PackageFileManager class uses a plugin system to generate the list of files in a package. This allows both standard recursive directory parsing (plugin type file) and more intelligent options such as the CVS browser PEAR_PackageFileManager_Cvs, which grabs all files in a local CVS checkout to create the list, ignoring any other local files.
Other options include specifying roles for file extensions (all .php files are role="php", for example), roles for directories (all directories named "tests" are given role="tests" by default), and exceptions. Exceptions are specific pathnames with * and ? wildcards that match a default role, but should have another. For example, perhaps a debug.tpl template would normally be data, but should be included in the docs role. Along these lines, to exclude files entirely, use the ignore option.
Required options for a release include version, baseinstalldir, state, and packagedirectory (the full path to the local location of the package to create a package.xml file for)
Example usage:
1 <?php
2 require_once('PEAR/PackageFileManager.php');
3 $packagexml = new
PEAR_PackageFileManager;
4 $e = $packagexml->
setOptions(
5 array('baseinstalldir' => 'PhpDocumentor',
6 'version' => '1.2.1',
7 'packagedirectory' => 'C:/Web Pages/chiara/phpdoc2/',
8 'state' => 'stable',
9 'filelistgenerator' => 'cvs', // generate from cvs, use file for directory
10 'notes' => 'We\'ve implemented many new and exciting features',
11 'ignore' => array('TODO', 'tests/'), // ignore TODO, all files in tests/
12 'installexceptions' => array('phpdoc' => '/*'), // baseinstalldir ="/" for phpdoc
13 'dir_roles' => array('tutorials' => 'doc'),
14 'exceptions' => array('README' => 'doc', // README would be data, now is doc
15 'PHPLICENSE.txt' => 'doc'))); // same for the license
16 if (PEAR::isError($e)) {
17 echo $e->getMessage();
18 die();
19 }
20 $e = $test->
addPlatformException('pear-phpdoc.bat', 'windows');
21 if (PEAR::isError($e)) {
22 echo $e->getMessage();
23 exit;
24 }
25 $packagexml->
addRole('pkg', 'doc'); // add a new role mapping
26 if (PEAR::isError($e)) {
27 echo $e->getMessage();
28 exit;
29 }
30 // replace @PHP-BIN@ in this file with the path to php executable! pretty neat
31 $e = $test->
addReplacement('pear-phpdoc', 'pear-config', '@PHP-BIN@', 'php_bin');
32 if (PEAR::isError($e)) {
33 echo $e->getMessage();
34 exit;
35 }
36 $e = $test->
addReplacement('pear-phpdoc.bat', 'pear-config', '@PHP-BIN@', 'php_bin');
37 if (PEAR::isError($e)) {
38 echo $e->getMessage();
39 exit;
40 }
41 // note use of
debugPackageFile()- this is VERY important
42 if (isset($_GET['make']) || $_SERVER['argv'][1] == 'make') {
43 $e = $packagexml->
writePackageFile();
44 } else {
45 $e = $packagexml->
debugPackageFile();
46 }
47 if (PEAR::isError($e)) {
48 echo $e->getMessage();
49 die();
50 }
51 ?>
In addition, a package.xml file can now be generated from scratch, with the usage of new options package, summary, description, and the use of the addMaintainer() method
PEAR_PackageFileManager
void constructor PEAR_PackageFileManager::PEAR_PackageFileManager (
)
The constructor is not used in order to be able to return a PEAR_Error from setOptions
throws no exceptions thrown
This function can not be called statically.
void|PEAR_Error PEAR_PackageFileManager::addConfigureOption (
string $name
, string $prompt
, string $default
= null
)
This option is only useful to PECL projects that are built upon installation
$name
name of the option
$prompt
prompt to display to the user
$default
default value
throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
This function can not be called statically.
void|PEAR_Error PEAR_PackageFileManager::addDependency (
string $name
, string $version
= false
, string $operator = 'ge'
, string $type = 'pkg'
, boolean $optional
= false
)
This will overwrite an existing dependency if it is found. In other words, if a dependency on PHP 4.1.0 exists, and addDependency('php', '4.3.0', 'ge', 'php') is called, the existing dependency on PHP 4.1.0 will be overwritten with the new one on PHP 4.3.0
$name
Dependency element name
$version
Dependency version
$operator
A specific operator for the version, this can be one of: 'has', 'not', 'lt', 'le', 'eq', 'ne', 'ge', or 'gt'
$type
Dependency type. This can be one of: 'pkg', 'ext', 'php', 'prog', 'os', 'sapi', or 'zend'
$optional
TRUE if dependency is optional
throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
throws PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE
This function can not be called statically.
void PEAR_PackageFileManager::addMaintainer (
string $handle
, lead|developer|contributor|helper $role
, string $name
, string $email
)
Every maintainer must have a valid account at pear.php.net. The first parameter is the account name (for instance, cellog is the handle for Greg Beaver at pear.php.net). Every maintainer has one of four possible roles:
lead: the primary maintainer
developer: an important developer on the project
contributor: self-explanatory
helper: ditto
Finally, specify the name and email of the maintainer
$handle
username on pear.php.net of maintainer
$role
role of maintainer
$name
full name of maintainer
$email
email address of maintainer
throws no exceptions thrown
This function can not be called statically.
void PEAR_PackageFileManager::addPlatformException (
string $path
, string $platform
)
The format of the platform string must be OS-version-cpu-extra if any more specific information is needed, and the OS must be in lower case as in "windows." The match is performed using a regular expression, but uses * and ? wildcards instead of .* and .?. Note that hpux/aix/irix/linux are all exclusive. To select non-windows, use an expression like (*ix|*ux|darwin). If using PEAR 1.3.2 and newer, use !windows.
This information is based on eyeing the source for OS/Guess.php, so if you are unsure of what to do, read that file.
$path
relative path of file (relative to packagedirectory option)
$platform
platform descriptor string
throws no exceptions thrown
This function can not be called statically.
void PEAR_PackageFileManager::addglobalreplacement (
string $type
, string $from
, string $to
)
This sets an install-time complex search-and-replace function allowing the setting of platform-specific variables in all installed files.
if $type is php-const, then $to must be the name of a PHP Constant. If $type is pear-config, then $to must be the name of a PEAR config variable accessible through a PEAR_Config::get() method. If type is package-info, then $to must be the name of a section from the package.xml file used to install this file.
$type
variable type, either php-const, pear-config or package-info
$from
text to replace in the source file
$to
variable name to use for replacement
throws PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE
This function can not be called statically.
void PEAR_PackageFileManager::addReplacement (
string $path
, string $type
, string $from
, string $to
)
This sets an install-time complex search-and-replace function allowing the setting of platform-specific variables in an installed file.
if $type is php-const, then $to must be the name of a PHP Constant. If $type is pear-config, then $to must be the name of a PEAR config variable accessible through a PEAR_Config::get() method. If type is package-info, then $to must be the name of a section from the package.xml file used to install this file.
$path
relative path of file (relative to packagedirectory option)
$type
variable type, either php-const, pear-config or package-info
$from
text to replace in the source file
$to
variable name to use for replacement
throws PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE
This function can not be called statically.
void PEAR_PackageFileManager::addRole (
string $extension
, string $role
)
Roles influence both where a file is installed and how it is installed. Files with role="data" are in a completely different directory hierarchy from the program files of role="php"
In PEAR 1.3b2, these roles are
php (most common)
data
doc
test
script (gives the file an executable attribute)
src
$extension
file extension
$role
role
throws PEAR_PACKAGEFILEMANAGER_INVALID_ROLE
This function can not be called statically.
void|PEAR_Error PEAR_PackageFileManager::detectDependencies (
)
Use addDependency() to add package dependencies
throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
throws PEAR_PACKAGEFILEMANAGER_PHP_COMPAT_NOT_INSTALLED
throws PEAR_PACKAGEFILEMANAGER_NO_PHPCOMPATINFO
This function can not be called statically.
void PEAR_PackageFileManager::debugPackageFile (
)
This method instructs writePackageFile() to simply print the package.xml to output, either command-line or web-friendly (this is automatic based on the existence of $_SERVER['PATH_TRANSLATED']
throws no exceptions thrown
see PEAR_PackageFileManager::writePackageFile() - calls with the debug parameter set based on whether it is called from the command-line or web interface
This function can not be called statically.
array PEAR_PackageFileManager::getWarnings (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
boolean PEAR_PackageFileManager::isIncludeable (
string $filename
)
then calls http://www.php.net/is_readable when it finds the file
$filename
throws no exceptions thrown
static
This function can not be called statically.
void PEAR_PackageFileManager::pushWarning (
mixed $code
, mixed $info
)
This package is not documented yet.
$code
$info
throws no exceptions thrown
This function can not be called statically.
PEAR_Error PEAR_PackageFileManager::raiseError (
mixed $code
, mixed $i1 = ''
, mixed $i2 = ''
)
1 function
raiseError($code, $i1 = '', $i2 = '')
2 {
3 return PEAR::raiseError('PEAR_PackageFileManager Error: ' .
4
sprintf($GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'][$this->_options['lang']][$code],
5 $i1, $i2), $code);
6 }
$code
$i1
$i2
throws no exceptions thrown
static
This function can not be called statically.
void|PEAR_Error PEAR_PackageFileManager::setOptions (
array $options = array()
)
The options array is indexed as follows:
1 $options = array('option_name' => <optionvalue>);
The documentation below simplifies this description through the use of option_name without quotes
Configuration options:
lang: lang controls the language in which error messages are displayed. There are currently only English error messages, but any contributed will be added over time. Possible values: en (default)
packagefile: the name of the packagefile, defaults to package.xml
pathtopackagefile: the path to an existing package file to read in, if different from the packagedirectory
packagedirectory: the path to the base directory of the package. For package PEAR_PackageFileManager, this path is /path/to/pearcvs/pear/PEAR_PackageFileManager where /path/to/pearcvs is a local path on your hard drive
outputdirectory: the path in which to place the generated package.xml by default, this is ignored, and the package.xml is created in the packagedirectory
filelistgenerator: the <filelist> section plugin which will be used. In this release, there are two generator plugins, file and cvs. For details, see the docs for these plugins
usergeneratordir: For advanced users. If you write your own filelist generator plugin, use this option to tell PEAR_PackageFileManager where to find the file that contains it. If the plugin is named foo, the class must be named PEAR_PackageFileManager_Foo no matter where it is located. By default, the Foo plugin is located in PEAR/PackageFileManager/Foo.php. If you pass /path/to/foo in this option, setOptions will look for PEAR_PackageFileManager_Foo in /path/to/foo/Foo.php
doctype: Specifies the DTD of the package.xml file. Default is http://pear.php.net/dtd/package-1.0
pearcommonclass: Specifies the name of the class to instantiate, default is PEAR_Common, but users can override this with a custom class that implements PEAR_Common's method interface
changelogoldtonew: True if the ChangeLog should list from oldest entry to newest. Set to false if you would like new entries first
simpleoutput: True if the package.xml should not contain md5sum or <provides /> for readability
addhiddenfiles: True if you wish to add hidden files/directories that begin with . like .bashrc. This is only used by the File generator. The CVS generator will use all files in CVS regardless of format
package.xml simple options:
baseinstalldir: The base directory to install this package in. For package PEAR_PackageFileManager, this is "PEAR", for package PEAR, this is "/"
license: The license this release is released under. Default is PHP License if left unspecified
notes: Release notes, any text describing what makes this release unique
changelognotes: notes for the changelog, this should be more detailed than the release notes. By default, PEAR_PackageFileManager uses the notes option for the changelog as well
version: The version number for this release. Remember the convention for numbering: initial alpha is between 0 and 1, add b<beta number> for beta as in 1.0b1, the integer portion of the version should specify backwards compatibility, as in 1.1 is backwards compatible with 1.0, but 2.0 is not backwards compatible with 1.10. Also note that 1.10 is a greater release version than 1.1 (think of it as "one point ten" and "one point one"). Bugfix releases should be a third decimal as in 1.0.1, 1.0.2
package: [optional] Package name. Use this to create a new package.xml, or overwrite an existing one from another package used as a template
summary: [optional] Summary of package purpose
description: [optional] Description of package purpose. Note that the above three options are not optional when creating a new package.xml from scratch
WARNING: all complex options that require a file path are case-sensitive
package.xml complex options:
cleardependencies: since version 1.3.0, this option will erase any existing dependencies in the package.xml if set to true
ignore: an array of filenames, directory names, or wildcard expressions specifying files to exclude entirely from the package.xml. Wildcards are operating system wildcards * and ?. file*foo.php will exclude filefoo.php, fileabrfoo.php and filewho_is_thisfoo.php. file?foo.php will exclude fileafoo.php and will not exclude fileaafoo.php. test/ will exclude all directories and subdirectories of ANY directory named test encountered in directory parsing. *test* will exclude all files and directories that contain test in their name
include: an array of filenames, directory names, or wildcard expressions specifying files to include in the listing. All other files will be ignored. Wildcards are in the same format as ignore
roles: this is an array mapping file extension to install role. This specifies default behavior that can be overridden by the exceptions option and dir_roles option. use addRole() to add a new role to the pre-existing array
dir_roles: this is an array mapping directory name to install role. All files in a directory whose name matches the directory will be given the install role specified. Single files can be excluded from this using the exceptions option. The directory should be a relative path from the baseinstalldir, or "/" for the baseinstalldir
exceptions: specify file role for specific files. This array maps all files matching the exact name of a file to a role as in "file.ext" => "role"
deps: dependency array. Pass in an empty array to clear all dependencies, and use addDependency() to add new ones/replace existing ones
maintainers: maintainers array. Pass in an empty array to clear all maintainers, and use addMaintainer() to add a new maintainer/replace existing maintainer
installexceptions: array mapping of specific filenames to baseinstalldir values. Use this to force the installation of a file into another directory, such as forcing a script to be in the root scripts directory so that it will be in the path. The filename must be a relative path to the packagedirectory
platformexceptions: array mapping of specific filenames to the platform they should be installed on. Use this to specify unix-only files or windows-only files. The format of the platform string must be OS-version-cpu-extra if any more specific information is needed, and the OS must be in lower case as in "windows." The match is performed using a regular expression, but uses * and ? wildcards instead of .* and .?. Note that hpux/aix/irix/linux are all exclusive. To select non-windows, use (*ix|*ux)
scriptphaseexceptions: array mapping of scripts to their install phase. This can be one of: pre-install, post-install, pre-uninstall, post-uninstall, pre-build, post-build, pre-setup, or post-setup
installas: array mapping of specific filenames to the filename they should be installed as. Use this to specify new filenames for files that should be installed. This will often be used in conjunction with platformexceptions if there are two files for different OSes that must have the same name when installed.
replacements: array mapping of specific filenames to complex text search-and-replace that
should be performed upon install. The format is:
filename => array('type' => php-const|pear-config|package-info
'from' => text in file
'to' => name of variable)
if type is php-const, then 'to' must be the name of a PHP Constant.
If type is pear-config, then 'to' must be the name of a PEAR config
variable accessible through a PEAR_Config class->get() method. If
type is package-info, then 'to' must be the name of a section from
the package.xml file used to install this file.
globalreplacements: a list of replacements that should be performed on every single file. The format is the same as replacements (since 1.4.0)
configure_options: array specifies build options for PECL packages (you should probably use PECL_Gen instead, but it's here for completeness)
$options
throws PEAR_PACKAGEFILEMANAGER_NOPKGDIR
throws PEAR_PACKAGEFILEMANAGER_NOVERSION
throws PEAR_PACKAGEFILEMANAGER_NOSTATE
throws PEAR_PACKAGEFILEMANAGER_NOBASEDIR
throws PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE
throws PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND
This function can not be called statically.
void|PEAR_Error PEAR_PackageFileManager::writePackageFile (
boolean $debuginterface
= null
)
ALWAYS use debugPackageFile() to verify that output is correct before overwriting your package.xml
$debuginterface
NULL if no debugging, TRUE if web interface, FALSE if command-line
throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
throws PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS
throws PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE
throws PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE
throws PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE
throws PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE
see PEAR_PackageFileManager::debugPackageFile() - calls with the debug parameter set based on whether it is called from the command-line or web interface
This function can not be called statically.
Note that this will NOT work on a repository, only on a checked out CVS module
PEAR_PackageFileManager_CVS
PEAR_PackageFileManager_CVS Inherited Methods
Method Name | Summary |
---|---|
Constructor PEAR_PackageFileManager_File::PEAR_PackageFileManager_File() | Set up the File filelist generator |
PEAR_PackageFileManager_File::dirList() | Retrieve a listing of every file in $directory and all subdirectories. |
PEAR_PackageFileManager_File::getFileList() | Generate the <filelist></filelist> section of the package file. |
array PEAR_PackageFileManager_CVS::dirList (
string $directory
)
This function is like parent::dirList() except that instead of retrieving a regular filelist, it first retrieves a listing of all the CVS/Entries files in $directory and all of the subdirectories. Then, it reads the Entries file, and creates a listing of files that are a part of the CVS repository. No check is made to see if they have been modified, but newly added or removed files are ignored.
$directory
full path to the directory you want the list of
returns list of files in a directory
throws no exceptions thrown
see _readCVSEntries()
see _recurDirList()
This function can not be called statically.
This class is used to retrieve a raw directory listing. Use the PEAR_PackageFileManager_CVS class to only retrieve the contents of a cvs repository when generating the package.xml
PEAR_PackageFileManager_File
Class | Summary |
---|---|
PEAR_PackageFileManager_CVS | Generate a file list from a CVS checkout |
void constructor PEAR_PackageFileManager_File::PEAR_PackageFileManager_File (
PEAR_PackageFileManager &$parent
, array $options
)
'ignore' and 'include' are the only options that this class uses. See PEAR_PackageFileManager::setOptions() for more information and formatting of this option
&$parent
$options
throws no exceptions thrown
This function can not be called statically.
array PEAR_PackageFileManager_File::dirList (
string $directory
)
The return format is an array of full paths to files
$directory
full path to the directory you want the list of
returns list of files in a directory
throws PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST
This function can not be called statically.
array PEAR_PackageFileManager_File::getFileList (
)
This function performs the backend generation of the array containing all files in this package
throws no exceptions thrown
This function can not be called statically.
This package is not documented yet.
PEAR_Common
PEAR_PackageFileManager_XMLOutput
Name | Value | Line Number |
---|---|---|
PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS | 19 | 50 |
PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE | 9 | 40 |
PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE | 10 | 41 |
PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE | 7 | 38 |
PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED | 26 | 57 |
PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE | 8 | 39 |
PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST | 13 | 44 |
PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND | 5 | 36 |
PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE | 6 | 37 |
PEAR_PACKAGEFILEMANAGER_IGNORED_EVERYTHING | 21 | 52 |
PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE | 22 | 53 |
PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE | 23 | 54 |
PEAR_PACKAGEFILEMANAGER_INVALID_ROLE | 24 | 55 |
PEAR_PACKAGEFILEMANAGER_NOBASEDIR | 4 | 35 |
PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES | 12 | 43 |
PEAR_PACKAGEFILEMANAGER_NODESC | 18 | 49 |
PEAR_PACKAGEFILEMANAGER_NOPACKAGE | 15 | 46 |
PEAR_PACKAGEFILEMANAGER_NOPKGDIR | 3 | 34 |
PEAR_PACKAGEFILEMANAGER_NOSTATE | 1 | 32 |
PEAR_PACKAGEFILEMANAGER_NOSUMMARY | 17 | 48 |
PEAR_PACKAGEFILEMANAGER_NOVERSION | 2 | 33 |
PEAR_PACKAGEFILEMANAGER_NO_FILES | 20 | 51 |
PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST | 11 | 42 |
PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE | 25 | 56 |
PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS | 14 | 45 |
PEAR_PACKAGEFILEMANAGER_WRONG_MROLE | 16 | 47 |
Name | Value | Line Number |
---|---|---|
$GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'] | 1 array(
2 'en' =>
3 array(
4
PEAR_PACKAGEFILEMANAGER_NOSTATE=>
5 'Release State (option \'state\') must by specified in PEAR_PackageFileManager setOptions (alpha|beta|stable)',
6
PEAR_PACKAGEFILEMANAGER_NOVERSION=>
7 'Release Version (option \'version\') must be specified in PEAR_PackageFileManager setOptions',
8
PEAR_PACKAGEFILEMANAGER_NOPKGDIR=>
9 'Package source base directory (option \'packagedirectory\') must be ' .
10 'specified in PEAR_PackageFileManager setOptions',
11
PEAR_PACKAGEFILEMANAGER_NOBASEDIR=>
12 'Package install base directory (option \'baseinstalldir\') must be ' .
13 'specified in PEAR_PackageFileManager setOptions',
14
PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND=>
15 'Base class "%s" can\'t be located',
16
PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE=>
17 'Base class "%s" can\'t be located in default or user-specified directories',
18
PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE=>
19 'Failed to write package.xml file to destination directory',
20
PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE=>
21 'Destination directory "%s" is unwritable',
22
PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE=>
23 'Failed to copy package.xml.tmp file to package.xml',
24
PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE=>
25 'Failed to open temporary file "%s" for writing',
26
PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST=>
27 'package.xml file path "%s" doesn\'t exist or isn\'t a directory',
28
PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES=>
29 'Directory "%s" is not a CVS directory (it must have the CVS/Entries file)',
30
PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST=>
31 'Package source base directory "%s" doesn\'t exist or isn\'t a directory',
32
PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS=>
33 'Run $managerclass->setOptions() before any other methods',
34
PEAR_PACKAGEFILEMANAGER_NOPACKAGE=>
35 'Package Name (option \'package\') must by specified in PEAR_PackageFileManager '.
36 'setOptions to create a new package.xml',
37
PEAR_PACKAGEFILEMANAGER_NOSUMMARY=>
38 'Package Summary (option \'summary\') must by specified in PEAR_PackageFileManager' .
39 ' setOptions to create a new package.xml',
40
PEAR_PACKAGEFILEMANAGER_NODESC=>
41 'Detailed Package Description (option \'description\') must be' .
42 ' specified in PEAR_PackageFileManager setOptions to create a new package.xml',
43
PEAR_PACKAGEFILEMANAGER_WRONG_MROLE=>
44 'Maintainer role must be one of "%s", was "%s"',
45
PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS=>
46 'Add maintainers to a package before generating the package.xml',
47
PEAR_PACKAGEFILEMANAGER_NO_FILES=>
48 'No files found, check the path "%s"',
49
PEAR_PACKAGEFILEMANAGER_IGNORED_EVERYTHING=>
50 'No files left, check the path "%s" and ignore option "%s"',
51
PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE=>
52 'Package validation failed:%s%s',
53
PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE=>
54 'Replacement Type must be one of "%s", was passed "%s"',
55
PEAR_PACKAGEFILEMANAGER_INVALID_ROLE=>
56 'Invalid file role passed to addRole, must be one of "%s", was passed "%s"',
57
PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE=>
58 'addDependency had PHP as a package, use type="php"',
59
PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED=>
60 'path "%path%" contains CVS directory',
61 ),)
|
63 |
PEAR::PackageUpdate gives other packages or applications the ability to automatically keep themselves up-to-date by checking their channel server for the latest release and self-updating with the user's permission. Auto-update features help developers by reducing the number of different versions of a package which are currently used. This reduces the likely hood of bugs related to outdated versions being reported.
This package automates the update process but still allows the user to remain in control of their computer. PEAR::PackageUpdate respects a user's preferences and allows the user to decide not only if the package should be updated but also when to be alerted to new updates. The user can decide to only be notified when a new release has a certain state or release type (bug fix, feature enhancement, or major version). The user can even turn off the updating features. All of these settings are on a package-by-package basis.
PEAR::PackageUpdate is designed to be a backend for other packages which provide different interface types. For example, this package can be used to drive a PHP-GTK 2, CLI or HTML frontend.
<?php
class Foo {
function __construct()
{
// Try to update the package if needed.
require_once 'PEAR/PackageUpdate.php';
// Load the PHP-GTK 2 driver to check for updates for pear://Foo
$ppu = PEAR_PackageUpdate::factory('Gtk2', 'Foo', 'pear');
// Check for trouble loading the driver.
if ($ppu !== false) {
// See if a new version is available (respects user prefs).
if ($ppu->checkUpdate()) {
// Ask for permission to update.
if ($ppu->presentUpdate()) {
if ($ppu->update()) {
// The update was a success. The app must be
// restarted.
$ppu->forceRestart();
}
}
}
}
// ...
}
// ...
}
?>
Rather than stop on first error/warning encountered, as it's done by other PHP4 PEAR packages, PEAR::PackageUpdate used the PEAR_ErrorStack for advanced error handling.
PEAR_ErrorStack implements error raising and handling using a stack pattern. So, to determine whether there are any errors on the stack, you should use the PEAR_PackageUpdate::hasErrors(). And to retrieve each error/warning, one by one, from the stack, you have to use the PEAR_PackageUpdate::popError().
See example below:
Usage of error levels with PEAR_PackageUpdate::hasErrors() is only available since version 0.7.0
<?php
class Foo {
function __construct()
{
// Try to update the package if needed.
require_once 'PEAR/PackageUpdate.php';
// Load the Cli driver to check for updates for pear://Foo
$ppu = PEAR_PackageUpdate::factory('Cli', 'Foo', 'pear');
// Check for trouble loading the driver.
if ($ppu !== false) {
// See if a new version is available (respects user prefs).
if ($ppu->checkUpdate()) {
// Ask for permission to update.
if ($ppu->presentUpdate()) {
if ($ppu->update()) {
// The update was a success. The app must be
// restarted.
$ppu->forceRestart();
} else {
// Error handling
if ($ppu->hasErrors('warning')) {
// Warning: specifying error levels is only allowed since version 0.7.0
// Retrieve only first warning, not all
$error = $ppu->popError();
echo "Warning occured when trying to update: pear/Foo package\n";
echo "Message: " . $error['message'] ."\n";
}
if ($ppu->hasErrors()) {
// Retrieve only first error, not all
$error = $ppu->popError();
echo "Error occured when trying to update: pear/Foo package\n";
echo "Message: " . $error['message'] ."\n";
if (isset($error['context']) {
// context is available
echo "*** Context: ***\n";
echo "File: " . $error['context']['file'] ."\n";
echo "Line: " . $error['context']['line'] ."\n";
echo "Function: " . $error['context']['function'] ."\n";
echo "Class: " . $error['context']['class'] ."\n";
}
exit();
}
}
}
}
}
// ...
}
// ...
}
?>
mixed PEAR_PackageUpdate::factory (
string $driver
, string $packageName
, string $channel
, string $user_file
= ''
, string $system_file
= ''
, string $pref_file
= ''
)
Factory method for creating PEAR_PackageUpdate frontend instances.
$driver
The name of a frontend driver class. Must be one of
Gtk2
, Cli
, or
Web
.
$packageName
The name of the package to be updated. Example: PEAR_PackageFileManager_Web.
$channel
The name of the channel $packageName
is hosted
on. This may be a fully qualified channel name such as
pear.php.net
or a short channel name like
pear
.
$user_file
The path to the file to read PEAR user-defined options from.
$system_file
The path to the file to read PEAR system-wide defaults from.
$pref_file
The path to the file to read user's preferences from.
throws PEAR_PACKAGEUPDATE_ERROR_NONEXISTENTDRIVER
,
when invalid driver name is used (i.e. Gtk2, Cli, Web).
since 0.4.0a1
This function should be called statically.
mixed - reference to a new object or FALSE if the object could not be created (i.e. invalid driver name).
boolean PEAR_PackageUpdate::isIncludable (
string $path
)
Determines whether or not a file is includable. This method is used to ensure that the driver class file can be found and included.
$path
The path to the file to check. The path should be a subdirectory of one of the directories in the include path.
since 0.4.2
This function can be called statically.
boolean - TRUE if the file is includable, FALSE otherwise.
boolean PEAR_PackageUpdate::loadPreferences (
string $pref_file
= ''
)
Loads the user's preferences from the preference file.
If the user is on a Windows machine, the file will be in the
PEAR_CONFIG_SYSCONFIG
directory and named
ppurc.ini
. If the user is on any other operating system, the
preferences file will be stored in the user's home directory as the file
.ppurc
. The file contains a serialized array of
preferences for each package that has been checked for updates so far.
Since version 0.7.0, you could also choose another directory and name for your preference file. Use then the optional parameter
$pref_file
.
$pref_file
The path to the file to read user's preferences from.
throws PEAR_PACKAGEUPDATE_ERROR_PREFFILE_READACCESS
,
when user's preference file has no READ access right.
throws PEAR_PACKAGEUPDATE_ERROR_PREFFILE_CORRUPTED
,
when user's preference file has invalid contents.
throws PEAR_PACKAGEUPDATE_ERROR_INVALIDINIFILE
,
when user's preference file given by parameter $pref_file
does not exist.
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the preferences were loaded successfully, FALSE otherwise.
string PEAR_PackageUpdate::determinePrefFile (
)
Returns the path to the preferences file.
The preferences file holds information about whether or not the user would
like to be notified about updates for individual packages. If the user is
on a Windows machine, the file will be in the
PEAR_CONFIG_SYSCONFIG
directory and named
ppurc.ini
. If the user is on any operating system, the
preferences file will be stored in the user's home directory as the file
.ppurc
.
since 0.4.0a1
This function can be called statically.
string - The full path to the preferences file.
boolean PEAR_PackageUpdate::checkUpdate (
)
Checks to see if an update is available and the user has asked not to be notified about the update.
This method takes the user's preferences in consideration when determining if an update is available. If a new bug fix release is available but the user has asked not to be notified until the next major release of the package, this method will return FALSE.
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if an update is available and the user has asked to be notified about the update, FALSE otherwise.
boolean PEAR_PackageUpdate::getPackageInfo (
)
Loads the latest package information from the channel server.
This method contacts the packages channel server using a PEAR_Remote instance. If any errors are encountered (channel server does not host the package, bad package name, etc.) they will be pushed onto the error stack.
Since version 0.5.2, protocol REST 1.0 is also supported. So we try to contact the packages channel server using a PEAR_REST instance, if this channel support the protocol, rather than default protocol XMLRPC.
throws PEAR_PACKAGEUPDATE_ERROR_NOPACKAGE
,
if package name was set to empty.
throws PEAR_PACKAGEUPDATE_ERROR_NOCHANNEL
,
if channel was set to empty.
throws PEAR_PACKAGEUPDATE_ERROR_NOINFO
,
when there are no information available about the package name hosted on the channel server.
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the information was retrieved successfully, FALSE otherwise. Errors will be pushed onto the error stack.
array PEAR_PackageUpdate::getPackagePreferences (
)
Returns an array of update preferences for the current package. This method is used to ensure that users are not asked about updates they do not want to be asked about.
The array returned may have up to four elements:
PEAR_PACKAGEUPDATE_PREF_NOUPDATES
if TRUE the user does not want to know about any new updates.
PEAR_PACKAGEUPDATE_PREF_NEXTRELEASE
if set, the user does not want to be asked until a version greater than the value of this element has been released.
PEAR_PACKAGEUPDATE_PREF_TYPE
if set, the user does not want to be notified unless the release is at least the given type (one of PEAR_PACKAGEUPDATE_TYPE_BUG, PEAR_PACKAGEUPDATE_TYPE_MINOR, PEAR_PACKAGEUPDATE_TYPE_MAJOR).
PEAR_PACKAGEUPDATE_PREF_STATE
if set, the user does not want to be notified unless the release has at least the given state (one of PEAR_PACKAGEUPDATE_PREF_STATE_DEVEL, PEAR_PACKAGEUPDATE_PREF_STATE_ALPHA, PEAR_PACKAGEUPDATE_PREF_STATE_BETA, PEAR_PACKAGEUPDATE_PREF_STATE_STABLE).
since 0.4.0a1
This function can not be called statically.
array - an array of user preferences.
boolean PEAR_PackageUpdate::savePreferences (
string $pref_file
= ''
)
Saves the user's preferences to the preference file.
$pref_file
The path to the file to save user's preferences to.
throws PEAR_PACKAGEUPDATE_ERROR_PREFFILE_WRITEACCESS
,
when user's preference file has no WRITE access right.
throws PEAR_PACKAGEUPDATE_ERROR_PREFFILE_WRITEERROR
,
when an I/O error occured while writing user's preference file contents.
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the user's preferences were saved successfully, FALSE othewise.
boolean PEAR_PackageUpdate::preferencesAllowUpdate (
)
Checks whether or not the user's preferences allow an update to the latest version of the package. The user's preferences may define restrictions such as: don't update at all, don't update until a new version has been released (remembers the last version asked), only ask for certain states such as beta or stable, only ask for minor or higher version updates (no bug fixes), or only ask for major version updates.
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the user's preferences allow an update for the latest version of the package, FALSE otherwise.
string PEAR_PackageUpdate::releaseType (
)
Returns the release type of the most recent version of the package compared
to the installed version. The result will be one of
PEAR_PACKAGEUPDATE_TYPE_MAJOR
,
PEAR_PACKAGEUPDATE_TYPE_MINOR
, or
PEAR_PACKAGEUPDATE_TYPE_BUG
.
This value is used to determine if the user's preferences allow an update for the current release.
since 0.4.0a1
This function can not be called statically.
string - The release type (bug|minor|major).
mixed PEAR_PackageUpdate::getInstalledRelease (
)
Loads the informations about current installed version of the package.
version
The version of installed package name.
license
The license this package is released under.
summary
A short description about the package.
description
A long description about the package.
releasedate
The date of the release of the version installed.
releasenotes
Description about changes on the package from previous release.
state
The state of the package release installed (snapshot|devel|alpha|beta|stable).
deps
The list of dependencies for this release of the package.
xsdversion
The version of the XML package used to install this release (1.0 or 2.0).
packagerversion
The version of the PEAR packager that was used to build this release.
since 0.6.0
This function can not be called statically.
boolean - FALSE if the information was not available.
array - informations about the current installed version of the package (version, license, summary, description, releasedate, releasenotes, state, deps, xsdversion, packagerversion).
mixed PEAR_PackageUpdate::getLatestRelease (
)
Loads the informations from the channel server.
license
The license the latest package version is released under.
summary
A short description about the package.
description
A long description about the package.
releasedate
The date of the latest release available from the channel server.
releasenotes
Description about changes on the package from previous release.
state
The state of the package release installed (snapshot|devel|alpha|beta|stable).
deps
The list of dependencies for this release of the package.
version
The version of the latest release available from the channel server.
since 0.6.0
This function can not be called statically.
boolean - FALSE if the information was not available.
array - informations about the current installed version of the package (license, summary, description, releasedate, releasenotes, state, deps, version).
boolean PEAR_PackageUpdate::setDontAskAgain (
boolean $dontAsk
)
Sets the user's "Don't Ask Again" preference to
$dontAsk
. If $dontAsk
is TRUE
the user will not be asked to update the package again.
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the preference was set properly, FALSE otherwise.
boolean PEAR_PackageUpdate::setDontAskUntilNextRelease (
boolean $dontAsk
)
Sets the user's "Don't Ask Again Until Next Release" preference
to $dontAsk
. If $dontAsk
is
TRUE the user will not be asked to update the package again until a new
version is released.
throws PEAR_PACKAGEUPDATE_ERROR_NOINFO
,
when there are no information available about the package name hosted on the channel server.
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the preference was set properly, FALSE otherwise.
boolean PEAR_PackageUpdate::setMinimumReleaseType (
string $minType
)
Sets the user's preference for asking about minimum release types.
throws PEAR_PACKAGEUPDATE_ERROR_INVALIDTYPE
,
when invalid type of release is used (bug|minor|major).
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the preference was set properly, FALSE otherwise.
boolean PEAR_PackageUpdate::setMinimumState (
string $minState
)
Sets the user's preference for asking about minimum release state.
throws PEAR_PACKAGEUPDATE_ERROR_INVALIDSTATE
,
when invalid state of release is used (snapshot|devel|alpha|beta|stable).
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the preference was set properly, FALSE otherwise.
boolean PEAR_PackageUpdate::setPreference (
string $pref
, string $value
)
Sets one of the given preference to the given value. Both preference and value, should have valid values.
throws PEAR_PACKAGEUPDATE_ERROR_INVALIDPREF
,
when invalid preference is used (PEAR_PACKAGEUPDATE_PREF_NOUPDATES,
PEAR_PACKAGEUPDATE_PREF_NEXTRELEASE, PEAR_PACKAGEUPDATE_PREF_TYPE, PEAR_PACKAGEUPDATE_PREF_STATE).
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the preference was set properly, FALSE otherwise.
boolean PEAR_PackageUpdate::setPreferences (
array $preferences
)
Sets a group of preference with associated values, all at once. Invalid preference is ignored (no error raised).
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if all the preferences were set properly, FALSE otherwise.
boolean PEAR_PackageUpdate::update (
)
Updates your current installation with the new source for the package.
throws PEAR_PACKAGEUPDATE_ERROR_NOTINSTALLED
,
if the package is not already installed.
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if the update was successful, FALSE otherwise.
array PEAR_PackageUpdate::popError (
)
Pops an error off the error stack.
This method is just for collecting errors that occur while checking for updates and updating a package. The child class is responsible for displaying all errors and handling them properly. This is because the way errors are handled varies greatly depending on the driver used.
since 0.4.0a1
This function can not be called statically.
array - details of an error or warning (with debug context if available).
boolean PEAR_PackageUpdate::hasErrors (
mixed $level
= false
)
Returns whether or not errors , and or, warnings (version 0.7.0 or better) have occurred.
$level
The level of errors to pop off of the stack (warning|error). Use FALSE if you want all errors and warnings at once.
since 0.4.0a1
This function can not be called statically.
boolean - TRUE if errors(/warnings) have been captured, FALSE othewise.
Find out how much disk space is consumed by an installed pear package or a number of pear packages - analagous to the unix command df.
Mandatory resources:
PHP 5.1.4 or newer.
PEAR 1.4.0 or newer.
PEAR::Console_Getargs 1.3.4 or newer.
PEAR::Console_Table 1.1.0 or newer.
PEAR::HTML_Table 1.8.2 or newer.
PEAR_Size started as a php script that was born of a need to determine if there were any large pear packages installed that could be removed to free up space; it is analagous to the unix command df - this tool lists how much filespace each installed package consumes; also a subset of packages can be specified as can channels.
That script has evolved into a reusable set of classes that can be used from your own code and can render its report into HTML, XML, CSV and plain text formats. This is extensible so if required you could generate PDFs and possibly even charts of the data that is generated.
PEAR_Size consists of several core classes plus some ancillary ones. Most important is the PEAR_Size class itself. This performs the data-gathering process and then initiates the generation of the output report (using the analyse() and generateReport() methods respectively). The format of the report is set using the setOutputDriver() method.
If writing a command-line script that will utilise the power of PEAR_Size it is best to use the PEAR_Size_CLI class and run() for parsing the details and options set at the command prompt. The usage() method is for displaying either a usage screen or some error message. This method uses the application name as defined with the setAppName() method in those screens. If you intend to embed PEAR_Size into your own application or script you can set its name with the aforementioned method and retrieve it with appName().
There are two ways in which you can use PEAR_Size: via the pear command line tool or integrated in a PHP script.
It's up to you to choose what is the best usage for you.
The pear size command can be used to generate reports.
Usage: pear size [OPTIONS] [PACKAGE] Display information on how much space an installed PEAR package required. -a, --all display information for all installed packages -A, --allchannels list packages from all channels, not just the default one -c, --channel specify which channel -C, --csv output results in CSV format (sizes are measured in bytes). -h, --human-readable print sizes in human readable format (for example: 492 B 1KB 7MB) -H, --si likewise, but use powers of 1000 not 1024 -t, --type specify what type of files are required for the report by default all types are assumed -s, --summarise display channel summary view -S, --fsort sort by file size -v, --verbose display more detailed information --help display this help and exit -V, --version output version information and exit -X, --xml output results in XML format -0, --killzero do not output zero values in verbose mode Types: You can specify a subset of roles/file-types to be listed in the report. These roles are those as supported by the PEAR installer. These are: data, doc, ext, php, script, src, test Examples: $ pear size --all $ pear size Console_Table $ pear size -ttest,doc Console_Table $ pear size --type=test,doc,php -h Console_Table Date_Holidays
PEAR_Size has a number of output drivers which provide a means of providing reports in a range of various formats in an extensible manner - they all extend the PEAR_Size_Output_Driver class.
Four drivers are currently available for use:
CSV, PEAR_Size_Output_CSV. Print a comma separate values of results.
HTML, PEAR_Size_Output_Html. Print reports in HTML.
Text, PEAR_Size_Output_Text. Prints the reports in pure text - no additional formatting.
XML, PEAR_Size_Output_XML. Prints the reports in XML.
If these drivers are not adequate for what you require, you should consider writing your own - based on the PEAR_Size_Output_Driver class.
integer
PEAR_Size_CLI::run
(
)
Invokes PEAR_Size for command line scripts.
returns an integer
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
Output drivers provide a means of presenting the same information in a variety of formats. The drivers that currently ship with PEAR_Size provide formatting for CVS, HTML, XML and plain text.
Below is an example of creating a driver for XML output:
<?php
$type = 'xml';
//get the factory
$factory = new PEAR_Size_OutputFactory();
//and create the driver...
$driver = $factory->createInstance($type);
?>
integer
PEAR_Size_OutputFactory::createInstance
(
)
Create an instance of an output driver.
returns instance of an Output Driver.
throws an exception if specified driver could not be found.
since version 0.1.2
This function can not be called statically.
void
PEAR_Size_Output_Driver::display
(
string
$text
)
Display text, formatted appropriately, according to the purpose of the driver.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
This is a base class and should not be used directly.
void
PEAR_Size_Driver::generateReport
(
array
$channel_stats
,
array
$search_roles
,
array
$grand_total
,
array
$display_params
)
Display text, formatted appropriately, according to the purpose of the driver.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
This is a base class and should not be used directly.
void
PEAR_Size_Output_CSV::display
(
string
$text
)
Display text in CSV (comma separated) format.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
void
PEAR_Size_Output_CSV::generateReport
(
array
$channel_stats
,
array
$search_roles
,
array
$grand_total
,
array
$display_params
)
Display text, formatted appropriately, according to the purpose of the driver.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
void
PEAR_Size_Output_Html::display
(
string
$text
)
Display text in HTML format.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
void
PEAR_Size_Output_Html::generateReport
(
array
$channel_stats
,
array
$search_roles
,
array
$grand_total
,
array
$display_params
)
Display report in HTML format.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
void
PEAR_Size_Output_Text::display
(
string
$text
)
Display text in Text (comma separated) format.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
void
PEAR_Size_Output_Text::generateReport
(
array
$channel_stats
,
array
$search_roles
,
array
$grand_total
,
array
$display_params
)
Display text, formatted appropriately, according to the purpose of the driver.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
void
PEAR_Size_Output_XML::display
(
string
$text
)
Display text in XML format.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
void
PEAR_Size_Output_XML::generateReport
(
array
$channel_stats
,
array
$search_roles
,
array
$grand_total
,
array
$display_params
)
Display report in XML.
returns void
throws no exceptions thrown
since version 0.1.2
This function can not be called statically.
Tools to help your development process.
The PHP_Archive package allows creation of self-contained cross-platform PHP libraries or applications, similar to Java jar files. PHP_Archive
PHP_Archive provides the PHP_Archive class, and
the phar://
stream wrapper. Using PHP_Archive, it is
possible to create self-contained cross-platform PHP libraries and
applications. The file format and principles are identical to the
Phar
extension, which is documented in the PHP
manual. PHP_Archive is used to create go-pear.phar
and install-pear-nozlib.phar
, distributed with PHP since
PHP version 5.1.0, and used to install the PEAR Installer itself.
The PHP_Archive class differs in approach from the
Phar class in that it does not support
the ArrayAccess interface for accessing internal files,
and does not support iteration over contents using foreach(). However,
full support for the phar://
stream wrapper, support of
Phar-specific metadata setting and retrieval using
PHP_Archive::getPharMetaData() and file-specific metadata
using PHP_Archive::getFileMetaData(), zlib and bzip2
compression of individual files, and archive-wide SHA1/MD5 signature makes
PHP_Archive just as powerful as the Phar extension for distributing phars.
The advanced Phar creation features of PHP_Archive are not yet documented, but information is available at the API documentation, and an example usage is in cvs at make-gopear-phar.php.
The phpDocumentor package provides automatic documenting of php api directly from the source.
The phpDocumentor tool is a standalone auto-documentor similar to JavaDoc written in PHP. It differs from PHPDoc in that it is MUCH faster, parses a much wider range of PHP files, and comes with many customizations including 11 HTML templates, Windows help file CHM output, PDF output, and XML DocBook output that is compatible with the markup used in this manual. In addition, it can do PHPXref source code highlighting and linking.
PHP_CodeSniffer is a PHP5 script that tokenises and "sniffs" PHP, JavaScript and CSS files to detect violations of a defined coding standard. It is an essential development tool that ensures your code remains clean and consistent. It can also help prevent some common semantic errors made by developers.
PHP_CodeSniffer is a PHP5 script that tokenises and "sniffs" PHP, JavaScript and CSS files to detect violations of a defined coding standard. It is an essential development tool that ensures your code remains clean and consistent. It can also help prevent some common semantic errors made by developers.
A coding standard in PHP_CodeSniffer is a collection of sniff files. Each sniff file checks one part of the coding standard only. Multiple coding standards can be used within PHP_CodeSniffer so that the one installation can be used across multiple projects. The default coding standard used by PHP_CodeSniffer is the PEAR coding standard.
To check a file against the PEAR coding standard, simply specify the file's location.
Checking a file with PHP_CodeSniffer
$ phpcs /path/to/code/myfile.php
FILE: /path/to/code/myfile.php
--------------------------------------------------------------------------------
FOUND 5 ERROR(S) AFFECTING 2 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
20 | ERROR | PHP keywords must be lowercase; expected "false" but found "FALSE"
47 | ERROR | Line not indented correctly; expected 4 spaces but found 1
51 | ERROR | Missing function doc comment
88 | ERROR | Line not indented correctly; expected 9 spaces but found 6
--------------------------------------------------------------------------------
Or, if you wish to check an entire directory, you can specify the directory location instead of a file.
Checking a directory with PHP_CodeSniffer
$ phpcs /path/to/code
FILE: /path/to/code/myfile.php
--------------------------------------------------------------------------------
FOUND 5 ERROR(S) AFFECTING 5 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
20 | ERROR | PHP keywords must be lowercase; expected "false" but found "FALSE"
47 | ERROR | Line not indented correctly; expected 4 spaces but found 1
51 | ERROR | Missing function doc comment
88 | ERROR | Line not indented correctly; expected 9 spaces but found 6
--------------------------------------------------------------------------------
FILE: /path/to/code/yourfile.php
--------------------------------------------------------------------------------
FOUND 1 ERROR(S) AND 1 WARNING(S) AFFECTING 1 LINE(S)
--------------------------------------------------------------------------------
21 | ERROR | PHP keywords must be lowercase; expected "false" but found
| | "FALSE"
21 | WARNING | Equals sign not aligned with surrounding assignments
--------------------------------------------------------------------------------
PHP_CodeSniffer requires PHP version 5.1.2 or greater.
Individual sniffs may have additional requirements, such as external applications and scripts. See the Configuration Options page for a list of these requirements.
The pre-commit hook requires PHP version 5.2.4 or greater due to its use of the vertical whitespace character.
Running PHP_CodeSniffer with the -h
or --help
command line arguments will print a list of commands that PHP_CodeSniffer will respond to. The output of phpcs -h
is shown below.
Usage: phpcs [-nwlsapvi] [-d key[=value]]
[--report=<report>] [--report-file=<reportfile>] [--report-<report>=<reportfile>] ...
[--report-width=<reportWidth>] [--generator=<generator>] [--tab-width=<tabWidth>]
[--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]
[--config-set key value] [--config-delete key] [--config-show]
[--standard=<standard>] [--sniffs=<sniffs>] [--encoding=<encoding>]
[--extensions=<extensions>] [--ignore=<patterns>] <file> ...
-n Do not print warnings (shortcut for --warning-severity=0)
-w Print both warnings and errors (on by default)
-l Local directory only, no recursion
-s Show sniff codes in all reports
-a Run interactively
-p Show progress of the run
-v[v][v] Print verbose output
-i Show a list of installed coding standards
-d Set the [key] php.ini value to [value] or [true] if value is omitted
--help Print this help message
--version Print version information
<file> One or more files and/or directories to check
<extensions> A comma separated list of file extensions to check
(only valid if checking a directory)
<patterns> A comma separated list of patterns to ignore files and directories
<encoding> The encoding of the files being checked (default is iso-8859-1)
<sniffs> A comma separated list of sniff codes to limit the check to
(all sniffs must be part of the specified standard)
<severity> The minimum severity required to display an error or warning
<standard> The name or path of the coding standard to use
<tabWidth> The number of spaces each tab represents
<generator> The name of a doc generator to use
(forces doc generation instead of checking)
<report> Print either the "full", "xml", "checkstyle", "csv", "emacs"
"source", "summary", "svnblame" or "gitblame" report
(the "full" report is printed by default)
<reportfile> Write the report to the specified file path
<reportWidth> How many columns wide screen reports should be printed
The --standard
command line argument is optional, even if you have more than one coding standard installed. If no coding standard is specified, PHP_CodeSniffer will default to checking against the PEAR coding standard, or the standard you have set as the default. View instructions for setting the default coding standard.
The simplest way of using PHP_CodeSniffer is to provide the location of a file or folder for PHP_CodeSniffer to check. If a folder is provided, PHP_CodeSniffer will check all files it finds in that folder and all its sub-folders.
If you do not want sub-folders checked, use the -l
command line argument to force PHP_CodeSniffer to run locally in the folders specified.
In the example below, the first command tells PHP_CodeSniffer to check the myfile.inc
file for coding standard errors while the second command tells PHP_CodeSniffer to check all PHP files in the my_dir
directory.
Checking a single file or folder
$ phpcs /path/to/code/myfile.inc
$ phpcs /path/to/code/my_dir
You can also specify multiple files and folders to check. The command below tells PHP_CodeSniffer to check the myfile.inc
file and all files in the my_dir
directory.
Checking multiple files and folders
$ phpcs /path/to/code/myfile.inc /path/to/code/my_dir
After PHP_CodeSniffer has finished processing your files, you will get an error report. The report lists both errors and warnings for all files that violated the coding standard. The output looks like this:
Sample PHP_CodeSniffer output
$ phpcs /path/to/code/myfile.php
FILE: /path/to/code/myfile.php
--------------------------------------------------------------------------------
FOUND 5 ERROR(S) AND 1 WARNING(S) AFFECTING 5 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
20 | ERROR | PHP keywords must be lowercase; expected "false" but found
| | "FALSE"
47 | ERROR | Line not indented correctly; expected 4 spaces but found 1
47 | WARNING | Equals sign not aligned with surrounding assignments
51 | ERROR | Missing function doc comment
88 | ERROR | Line not indented correctly; expected 9 spaces but found 6
--------------------------------------------------------------------------------
If you don't want warnings included in the output, specify the -n
command line argument.
Sample PHP_CodeSniffer output with no warnings
$ phpcs -n /path/to/code/myfile.php
FILE: /path/to/code/myfile.php
--------------------------------------------------------------------------------
FOUND 5 ERROR(S) AFFECTING 5 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
20 | ERROR | PHP keywords must be lowercase; expected "false" but found "FALSE"
47 | ERROR | Line not indented correctly; expected 4 spaces but found 1
51 | ERROR | Missing function doc comment
88 | ERROR | Line not indented correctly; expected 9 spaces but found 6
--------------------------------------------------------------------------------
By default, PHP_CodeSniffer will print a complete list of all errors and warnings it finds. This list can become quite long, especially when checking a large number of files at once. To print a summary report that only shows the number of errors and warnings for each file, use the --report=summary
command line argument. The output will look like this:
Sample PHP_CodeSniffer summary output
$ phpcs --report=summary /path/to/code
PHP CODE SNIFFER REPORT SUMMARY
--------------------------------------------------------------------------------
FILE ERRORS WARNINGS
--------------------------------------------------------------------------------
/path/to/code/myfile.inc 5 0
/path/to/code/yourfile.inc 1 1
/path/to/code/ourfile.inc 0 2
--------------------------------------------------------------------------------
A TOTAL OF 6 ERROR(S) AND 3 WARNING(S) WERE FOUND IN 3 FILE(S)
--------------------------------------------------------------------------------
As with the full report, you can suppress the printing of warnings with the -n
command line argument.
Sample PHP_CodeSniffer summary output with no warnings
$ phpcs -n --report=summary /path/to/code
PHP CODE SNIFFER REPORT SUMMARY
--------------------------------------------------------------------------------
FILE ERRORS
--------------------------------------------------------------------------------
/path/to/code/myfile.inc 5
/path/to/code/yourfile.inc 1
--------------------------------------------------------------------------------
A TOTAL OF 6 ERROR(S) WERE FOUND IN 2 FILE(S)
--------------------------------------------------------------------------------
By default, PHP_CodeSniffer will run quietly, only printing the report of errors and warnings at the end. If you are checking a large number of files, you may have to wait a while to see the report. If you want to know what is happening, you can turn on progress or verbose output.
With progress output enabled, PHP_CodeSniffer will print a single-character status for each file being checked. The possible status characters are:
.
: The file contained no errors or warningsE
: The file contained 1 or more errorsW
: The file contained 1 or more warnings, but no errorsS
: The file contained a @codingStandardsIgnoreFile comment and was skippedProgress output will look like this:
Sample PHP_CodeSniffer progress output
$ phpcs /path/to/code/CodeSniffer -p
......................S..................................... 60 / 572
..........EEEE.E.E.E.E.E.E.E.E..W..EEE.E.E.E.EE.E.E.E.E.E.E. 120 / 572
E.E.E.E.E.WWWW.E.W..EEE.E.................E.E.E.E...E....... 180 / 572
E.E.E.E.....................E.E.E.E.E.E.E.E.E.E.W.E.E.E.E.E. 240 / 572
E.W......................................................... 300 / 572
..........................................E.E.E.E...E.E.E.E. 360 / 572
E.E.E.E.E.E..E.E.E..E..E..E.E.WW.E.E.EE.E.E................. 420 / 572
...................E.E.EE.E.E.E.S.E.EEEE.E...E...EE.E.E..EEE 480 / 572
.E.EE.E.E..E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E.E..E..E..E.E.E..E 540 / 572
.E.E....E.E.E...E.....E.E.ES....
You can configure PHP_CodeSniffer to show progress information by default using the configuration option.
With verbose output enabled, PHP_CodeSniffer will print the file that it is checking, show you how many tokens and lines the file contains, and let you know how long it took to process. The output will look like this:
Sample PHP_CodeSniffer verbose output
$ phpcs /path/to/code/CodeSniffer -v
Registering sniffs in PEAR standard... DONE (24 sniffs registered)
Creating file list... DONE (572 files in queue)
Processing AbstractDocElement.php [1093 tokens in 303 lines]... DONE in < 1 second (0 errors, 1 warnings)
Processing AbstractParser.php [2360 tokens in 558 lines]... DONE in 2 seconds (0 errors, 1 warnings)
Processing ClassCommentParser.php [923 tokens in 296 lines]... DONE in < 1 second (2 errors, 0 warnings)
Processing CommentElement.php [988 tokens in 218 lines]... DONE in < 1 second (1 error, 5 warnings)
Processing FunctionCommentParser.php [525 tokens in 184 lines]... DONE in 1 second (0 errors, 6 warnings)
Processing File.php [10968 tokens in 1805 lines]... DONE in 5 seconds (0 errors, 5 warnings)
Processing Sniff.php [133 tokens in 94 lines]... DONE in < 1 second (0 errors, 0 warnings)
Processing SniffException.php [47 tokens in 36 lines]... DONE in < 1 second (1 errors, 3 warnings)
PHP_CodeSniffer can have multiple coding standards installed to allow a single installation to be used with multiple projects. When checking PHP code, PHP_CodeSniffer can be told which coding standard to use. This is done using the --standard
command line argument.
The example below checks the myfile.inc
file for violations of the PEAR coding standard (installed by default).
Specifying a coding standard to use
$ phpcs --standard=PEAR /path/to/code/myfile.inc
Can you also tell PHP_CodeSniffer to use an external standard by specifying the full path to the standard's root directory on the command line. An external standard is one that is stored outside of PHP_CodeSniffer'sStandards
directory.Specifying an external coding standard
$ phpcs --standard=/path/to/MyStandard /path/to/code/myfile.inc
PHP_CodeSniffer can print you a list of the coding standards that are installed so that you can correctly specify a coding standard to use for testing. You can print this list by specifying the -i
command line argument.
Generating a list of installed coding standards
$ phpcs -i
The installed coding standards are Zend, PEAR, PHPCS, Squiz and MySource
By default, PHP_CodeSniffer will check any file it finds with a .inc
or .php
extension. Sometimes, this means that PHP_CodeSniffer is not checking enough of your files. Sometimes, the opposite is true. PHP_CodeSniffer allows you to specify a list of valid file extensions using the --extensions
command line argument. Extensions are separated by commas.
Checking .php
files only
$ phpcs --extensions=php /path/to/code
Checking .php
, .inc
and .lib
files only
$ phpcs --extensions=php,inc,lib /path/to/code
If you have asked PHP_CodeSniffer to check a specific file rather than an entire directory, the extension of the specified file will be ignored. The file will be checked even if it has an invalid extension or no extension at all. In the following example, the main.inc file will be checked by PHP_CodeSniffer even though the--extensions
command line argument specifies that only.php
files should be checked.Extension ignored when checking specific file
$ phpcs --extensions=php /path/to/code/main.inc
The ignoring of file extensions for specific files is a feature of PHP_CodeSniffer and is the only way to check files without an extension. If you check an entire directory of files, all files without extensions will be ignored, so you must check each of these file separately.
Sometimes you want PHP_CodeSniffer to run over a very large number of files, but you want some files and folders to be skipped. The --ignore
command line argument can be used to tell PHP_CodeSniffer to skip files and folders that match one or more patterns.
In the following example, PHP_CodeSniffer will skip all files inside the package's tests
and data
directories. This is useful if you are checking a PEAR package but don't want your test or data files to conform to your coding standard.
Ignoring test and data files
$ phpcs --ignore=*/tests/*,*/data/* /path/to/code
You can also tell PHP_CodeSniffer to ignore a file using a special comment inserted at the top of the file. This will stop the file being checked even if it does not match the ignore pattern.Ignoring a file using a comment
<?php
// @codingStandardsIgnoreFile
$xmlPackage = new XMLPackage;
$xmlPackage['error_code'] = get_default_error_code_value();
$xmlPackage->send();
?>
Some parts of your code may be unable to conform to your coding standard. For example, you might have to break your standard to integrate with an external library or web service. To stop PHP_CodeSniffer generating errors for this code, you can wrap it in special comments. PHP_CodeSniffer will then hide all errors and warnings that are generated for these lines of code.
Ignoring parts of a file using comments
$xmlPackage = new XMLPackage;
// @codingStandardsIgnoreStart
$xmlPackage['error_code'] = get_default_error_code_value();
// @codingStandardsIgnoreEnd
$xmlPackage->send();
By default, PHP_CodeSniffer will check your code using all sniffs in the specified standard. Sometimes you may want to find all occurrences of an error to eliminate it more quickly or understand the scope of the problem. PHP_CodeSniffer allows you to specify a list of sniffs to limit results to using the --sniffs
command line argument. Sniffs are separated by commas.
Checking files for two specific sniffs only
$ phpcs --standard=PEAR --sniffs=Generic.PHP.LowerCaseConstant,PEAR.WhiteSpace.ScopeIndent /path/to/code
This feature is a message filter and not a quick way to define a custom coding standard. All sniffs specified on the command line in this way must be used in the coding standard you are using to check your files. You can't, for example, limit results to Squiz sniffs while using the PEAR standard as the PEAR standard does not include these sniffs.
To view source codes for error messages, use the -s
command line argument. This will print source codes in the full, summary and source reports.
By default, PHP_CodeSniffer assigns a severity of 5
to all errors and warnings. Standards, especially custom standards, may change the severity of some messages so they are hidden by default or even so that they are raised to indicate greater importance. PHP_CodeSniffer allows you to decide what the minimum severity level must be to show a message in its report using the --severity
command line argument.
Hiding errors and warnings with a severity less than 3
$ phpcs --severity=3 /path/to/code
You can specify different values for errors and warnings using the --error-severity
and --warning-severity
command line arguments.
Showing all errors but only warnings with a severity of 8 or more
$ phpcs --error-severity=1 --warning-severity=8 /path/to/code
Setting the severity of warnings to0
is the same as using the-n
command line argument. If you set the severity of errors to0
PHP_CodeSniffer will not show any errors, which may be useful if you just want to show the warnings.
This feature is particularly useful during manual code reviews. During normal development or an automated build, you may want to only check code formatting issues while during a code review you may wish to show less severe errors and warnings that may need manual peer review.
Most of the sniffs written for PHP_CodeSniffer do not support the usage of tabs for indentation and alignment. You can write your own sniffs that check for tabs instead of spaces, but you can also get PHP_CodeSniffer to convert your tabs into spaces before a file is checked. This allows you to use the existing space-based sniffs on your tab-based files.
In the following example, PHP_CodeSniffer will replace all tabs in the files being checked with between 1 and 4 spaces, depending on the column the tab indents to.
Converting tabs to spaces
$ phpcs --tab-width=4 /path/to/code
Some PHP_CodeSniffer reports output UTF-8 encoded XML, which can cause problems if your files are already UTF-8 encoded. In this case, some content from your files (generally comments) are used within error messages and may be double-encoded. To help PHP_CodeSniffer encode reports correctly, you can specify the encoding of your source files using the --encoding
command line argument.
Specifying UTF-8 encoding
$ phpcs --encoding=utf-8 /path/to/code
The default encoding used by PHP_CodeSniffer is ISO-8859-1.
PHP_CodeSniffer allows you to set temporary php.ini settings during a run using the -d
command line argument. The name of the php.ini setting must be specified on the command line, but the value is optional. If no value is set, the php.ini setting will be given a value of TRUE.
Specifying a memory limit
$ phpcs -d memory_limit=32M /path/to/code
Specifying multiple values
$ phpcs -d memory_limit=32M -d include_path=.:/php/includes /path/to/code
PHP_CodeSniffer has some configuration options that can be set. Individual coding standards may also require configuration options to be set before functionality can be used. View a full list of configuration options.
To set a configuration option, use the --config-set
command line argument.
Setting a configuration option
$ phpcs --config-set <option> <value>
PHP_CodeSniffer allows you to delete any configuration option, reverting it to its default value. View a full list of configuration options.
To delete a configuration option, use the --config-delete
command line argument.
Deleting a configuration option
$ phpcs --config-delete <option>
To view the currently set configuration options, use the --config-show
command line argument.
Viewing configuration options
$ phpcs --config-show
Array
(
[default_standard] => PEAR
[zend_ca_path] => /path/to/ZendCodeAnalyzer
)
This feature is provided for debugging purposes only. Using this feature will dramatically increase screen output and script running time.
PHP_CodeSniffer contains multiple verbosity levels. Level 2 (indicated by the command line argument -vv
) will print all verbosity information for level 1 (file specific token and line counts with running times) as well as verbose tokeniser output.
The output of the PHP_CodeSniffer tokeniser shows the step-by-step creation of the scope map and the level map.
The scope map is best explained with an example. For the following file:
<?php
if ($condition) {
echo 'Condition was true';
}
?>
The scope map output is:
Sample scope map output
*** START SCOPE MAP ***
Start scope map at 1: T_IF => if
Process token 2 []: T_WHITESPACE =>
Process token 3 []: T_OPEN_PARENTHESIS => (
Process token 6 []: T_WHITESPACE =>
Process token 7 []: T_OPEN_CURLY_BRACKET => {
=> Found scope opener for 1 (T_IF)
Process token 8 [opener:7;]: T_WHITESPACE => \n
Process token 9 [opener:7;]: T_WHITESPACE =>
Process token 10 [opener:7;]: T_ECHO => echo
Process token 11 [opener:7;]: T_WHITESPACE =>
Process token 12 [opener:7;]: T_CONSTANT_ENCAPSED_STRING => 'Condition was true'
Process token 13 [opener:7;]: T_SEMICOLON => ;
Process token 14 [opener:7;]: T_WHITESPACE => \n
Process token 15 [opener:7;]: T_CLOSE_CURLY_BRACKET => }
=> Found scope closer for 1 (T_IF)
*** END SCOPE MAP ***
The scope map output above shows the following pieces of information about the file:
A scope token, if
, was found at token 1 (note that token 0 is the open PHP tag).
The opener for the if
statement, the open curly brace, was found at token 7.
The closer for the if
statement, the close curly brace, was found at token 15.
Tokens 8 - 15 are all included in the scope set by the scope opener at token 7, the open curly brace. This indicates that these tokens are all within the if
statement.
The scope map output is most useful when debugging PHP_CodeSniffer's scope map, which is critically important to the successful checking of a file, but is also useful for checking the type of a particular token. For example, if you are unsure of the token type for an opening curly brace, the scope map output shows you that the type is T_OPEN_CURLY_BRACKET and not, for example, T_OPEN_CURLY_BRACE.
The level map is best explained with an example. For the following file:
<?php
if ($condition) {
echo 'Condition was true';
}
?>
The level map output is:
Sample level map output
*** START LEVEL MAP ***
Process token 0 on line 1 [lvl:0;]: T_OPEN_TAG => <?php\n
Process token 1 on line 2 [lvl:0;]: T_IF => if
Process token 2 on line 2 [lvl:0;]: T_WHITESPACE =>
Process token 3 on line 2 [lvl:0;]: T_OPEN_PARENTHESIS => (
Process token 4 on line 2 [lvl:0;]: T_VARIABLE => $condition
Process token 5 on line 2 [lvl:0;]: T_CLOSE_PARENTHESIS => )
Process token 6 on line 2 [lvl:0;]: T_WHITESPACE =>
Process token 7 on line 2 [lvl:0;]: T_OPEN_CURLY_BRACKET => {
=> Found scope opener for 1 (T_IF)
* level increased *
* token 1 (T_IF) added to conditions array *
Process token 8 on line 2 [lvl:1;conds;T_IF;]: T_WHITESPACE => \n
Process token 9 on line 3 [lvl:1;conds;T_IF;]: T_WHITESPACE =>
Process token 10 on line 3 [lvl:1;conds;T_IF;]: T_ECHO => echo
Process token 11 on line 3 [lvl:1;conds;T_IF;]: T_WHITESPACE =>
Process token 12 on line 3 [lvl:1;conds;T_IF;]: T_CONSTANT_ENCAPSED_STRING => 'Condition was true'
Process token 13 on line 3 [lvl:1;conds;T_IF;]: T_SEMICOLON => ;
Process token 14 on line 3 [lvl:1;conds;T_IF;]: T_WHITESPACE => \n
Process token 15 on line 4 [lvl:1;conds;T_IF;]: T_CLOSE_CURLY_BRACKET => }
=> Found scope closer for 7 (T_OPEN_CURLY_BRACKET)
* token T_IF removed from conditions array *
* level decreased *
Process token 16 on line 4 [lvl:0;]: T_WHITESPACE => \n
Process token 17 on line 5 [lvl:0;]: T_CLOSE_TAG => ?>\n
*** END LEVEL MAP ***
The level map output above shows the following pieces of information about the file:
A scope opener, an open curly brace, was found at token 7 and opened the scope for an if
statement, defined at token 1.
Tokens 8 - 15 are all included in the scope set by the scope opener at token 7, the open curly brace. All these tokens are at level 1, indicating that they are enclosed in 1 scope condition, and all these tokens are enclosed in a single condition; an if
statement.
The level map is most commonly used to determine indentation rules (e.g., a token 4 levels deep requires 16 spaces of indentation) or to determine if a particular token is within a particular scope (eg. a function
keyword is within a class scope, making it a method).
This feature is provided for debugging purposes only. Using this feature will dramatically increase screen output and script running time.
PHP_CodeSniffer contains multiple verbosity levels. Level 3 (indicated by the command line argument -vvv
) will print all verbosity information for level 1 (file specific token and line counts with running times), level 2 (tokeniser output) as well as token processing output with sniff running times.
The token processing output is best explained with an example. For the following file:
<?php
if ($condition) {
echo 'Condition was true';
}
?>
The token processing output is:
Sample token processing output
*** START TOKEN PROCESSING ***
Process token 0: T_OPEN_TAG => <?php\n
Processing PEAR_Sniffs_Commenting_FileCommentSniff... DONE in 0.001 seconds
Processing PEAR_Sniffs_Files_LineLengthSniff... DONE in 0.0004 seconds
Processing PEAR_Sniffs_PHP_DisallowShortOpenTagSniff... DONE in 0.0001 seconds
Process token 1: T_IF => if
Processing PEAR_Sniffs_ControlStructures_ControlSignatureSniff... DONE in 0.0008 seconds
Processing PEAR_Sniffs_WhiteSpace_ScopeClosingBraceSniff... DONE in 0.0248 seconds
Processing PEAR_Sniffs_WhiteSpace_ScopeIndentSniff... DONE in 0.0004 seconds
Process token 2: T_WHITESPACE =>
Process token 3: T_OPEN_PARENTHESIS => (
Process token 4: T_VARIABLE => $condition
Process token 5: T_CLOSE_PARENTHESIS => )
Process token 6: T_WHITESPACE =>
Process token 7: T_OPEN_CURLY_BRACKET => {
Process token 8: T_WHITESPACE => \n
Process token 9: T_WHITESPACE =>
Process token 10: T_ECHO => echo
Process token 11: T_WHITESPACE =>
Process token 12: T_CONSTANT_ENCAPSED_STRING => 'Condition was true'
Process token 13: T_SEMICOLON => ;
Process token 14: T_WHITESPACE => \n
Process token 15: T_CLOSE_CURLY_BRACKET => }
Process token 16: T_WHITESPACE => \n
Process token 17: T_CLOSE_TAG => ?>\n
*** END TOKEN PROCESSING ***
Every token processed is shown, along with its ID, type and contents. For each token, all sniffs that were executed on the token are displayed, along with the running time.
For example, the output above shows us that token 1, an if
keyword, had 3 sniffs executed on it; the ControlSignature sniff, the ScopeClosingBrace sniff and the ScopeIndent sniff. Each was executed fairly quickly, but the slowest was the ScopeClosingBrace sniff, taking 0.0248 seconds to process that token.
The other interesting piece of information we get from the output above is that only 2 tokens in the whole file had sniffs executed on them; tokens 0 and 1. This is normal behavior for PHP_CodeSniffer as most sniffs listen for a very specific and rarely used token and then execute on it and a number of tokens following it.
For example, the ScopeIndentSniff executes on the if
statement's token only, but actually checks the indentation of every line within the if
statement. The sniff uses the scope map to find all tokens within the if
statement.
See the main Usage page for basic usage information about these report types, including example output.
Both the full and summary reports can additionally show information about the source of errors and warnings. Source codes can be used with the --sniffs
command line argument to only show messages from a specified list of sources. To include source codes in the report, use the -s
command line argument.
Sample PHP_CodeSniffer full report with source codes
$ phpcs -s /path/to/code/myfile.php
FILE: /path/to/code/myfile.php
--------------------------------------------------------------------------------
FOUND 5 ERROR(S) AND 1 WARNING(S) AFFECTING 5 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment (PEAR.Commenting.FileComment)
20 | ERROR | PHP keywords must be lowercase; expected "false" but found
| | "FALSE" (Generic.PHP.LowerCaseConstant)
47 | ERROR | Line not indented correctly; expected 4 spaces but found 1
| | (PEAR.WhiteSpace.ScopeIndent)
47 | WARNING | Equals sign not aligned with surrounding assignments
| | (Generic.Formatting.MultipleStatementAlignment)
51 | ERROR | Missing function doc comment
| | (PEAR.Commenting.FunctionComment)
88 | ERROR | Line not indented correctly; expected 9 spaces but found 6
| | (PEAR.WhiteSpace.ScopeIndent)
--------------------------------------------------------------------------------
Sample PHP_CodeSniffer summary report with source codes
$ phpcs -s --report=summary /path/to/code
PHP CODE SNIFFER REPORT SUMMARY
--------------------------------------------------------------------------------
FILE ERRORS WARNINGS
--------------------------------------------------------------------------------
/path/to/code/myfile.inc 5 0
/path/to/code/yourfile.inc 1 1
/path/to/code/ourfile.inc 0 2
--------------------------------------------------------------------------------
A TOTAL OF 6 ERROR(S) AND 3 WARNING(S) WERE FOUND IN 3 FILE(S)
--------------------------------------------------------------------------------
PHP CODE SNIFFER VIOLATION SOURCE SUMMARY
--------------------------------------------------------------------------------
SOURCE COUNT
--------------------------------------------------------------------------------
PEAR.WhiteSpace.ScopeIndent 3
PEAR.Commenting.FileComment 2
Generic.PHP.LowerCaseConstant 2
Generic.Formatting.MultipleStatementAlignment 1
PEAR.Commenting.FunctionComment 1
--------------------------------------------------------------------------------
A TOTAL OF 9 SNIFF VIOLATION(S) WERE FOUND IN 5 SOURCE(S)
--------------------------------------------------------------------------------
PHP_CodeSniffer can output a summary report showing you the most common errors detected in your files so you can target specific parts of your coding standard for improvement. To print a source report, use the --report=source
command line argument. The output will look like this:
Sample PHP_CodeSniffer source output
$ phpcs --report=source /path/to/code
PHP CODE SNIFFER VIOLATION SOURCE SUMMARY
--------------------------------------------------------------------------------
STANDARD CATEGORY SNIFF COUNT
--------------------------------------------------------------------------------
Generic PHP Lower case constant 4
PEAR White space Scope indent 3
PEAR Commenting File comment 1
--------------------------------------------------------------------------------
A TOTAL OF 8 SNIFF VIOLATION(S) WERE FOUND IN 3 SOURCE(S)
--------------------------------------------------------------------------------
To show source codes instead of friendly names, use the -s
command line argument.
Sample PHP_CodeSniffer source code output
$ phpcs -s --report=source /path/to/code
PHP CODE SNIFFER VIOLATION SOURCE SUMMARY
--------------------------------------------------------------------------------
SOURCE COUNT
--------------------------------------------------------------------------------
Generic.PHP.LowerCaseConstant 4
PEAR.WhiteSpace.ScopeIndent 3
PEAR.Commenting.FileComment 1
--------------------------------------------------------------------------------
A TOTAL OF 8 SNIFF VIOLATION(S) WERE FOUND IN 3 SOURCE(S)
--------------------------------------------------------------------------------
PHP_CodeSniffer can output an XML report to allow you to parse the output easily and use the results in your own scripts. To print an XML report, use the --report=xml
command line argument. The output will look like this:
Sample PHP_CodeSniffer XML output
$ phpcs --report=xml /path/to/code
<?xml version="1.0" encoding="UTF-8"?>
<phpcs version="1.0.0">
<file name="/path/to/code/myfile.php" errors="4" warnings="1">
<error line="2" column="1" source="PEAR.Commenting.FileComment" severity="5">Missing file doc comment</error>
<error line="20" column="43" source="Generic.PHP.LowerCaseConstant" severity="5">PHP keywords must be lowercase; expected "false" but found "FALSE"</error>
<error line="47" column="1" source="PEAR.WhiteSpace.ScopeIndent" severity="5">Line not indented correctly; expected 4 spaces but found 1</error>
<warning line="47" column="20" source="Generic.Formatting.MultipleStatementAlignment" severity="5">Equals sign not aligned with surrounding assignments</warning>
<error line="51" column="4" source="PEAR.Commenting.FunctionComment" severity="5">Missing function doc comment</error>
</file>
</phpcs>
PHP_CodeSniffer can output an XML report similar to the one produced by Checkstyle, allowing you to use the output in scripts and applications that already support Checkstyle. To print a Checkstyle report, use the --report=checkstyle
command line argument. The output will look like this:
Sample PHP_CodeSniffer Checkstyle output
$ phpcs --report=checkstyle /path/to/code
<?xml version="1.0" encoding="UTF-8"?>
<checkstyle version="1.0.0">
<file name="/path/to/code/myfile.php">
<error line="2" column="1" severity="error" message="Missing file doc comment" source="PEAR.Commenting.FileComment"/>
<error line="20" column="43" severity="error" message="PHP keywords must be lowercase; expected "false" but found "FALSE"" source="Generic.PHP.LowerCaseConstant"/>
<error line="47" column="1" severity="error" message="Line not indented correctly; expected 4 spaces but found 1" source="PEAR.WhiteSpace.ScopeIndent"/>
<error line="47" column="20" severity="warning" message="Equals sign not aligned with surrounding assignments" source="Generic.Formatting.MultipleStatementAlignment"/>
<error line="51" column="4" severity="error" message="Missing function doc comment" source="PEAR.Commenting.FunctionComment"/>
</file>
</checkstyle>
PHP_CodeSniffer can output a CSV report to allow you to parse the output easily and use the results in your own scripts. To print a CSV report, use the --report=csv
command line argument. The output will look like this:
Sample PHP_CodeSniffer CSV output
$ phpcs --report=csv /path/to/code
File,Line,Column,Type,Message,Source,Severity
"/path/to/code/myfile.php",2,1,error,"Missing file doc comment",PEAR.Commenting.FileComment,5
"/path/to/code/myfile.php",20,43,error,"PHP keywords must be lowercase; expected \"false\" but found \"FALSE\"",Generic.PHP.LowerCaseConstant,5
"/path/to/code/myfile.php",47,1,error,"Line not indented correctly; expected 4 spaces but found 1",PEAR.WhiteSpace.ScopeIndent,5
"/path/to/code/myfile.php",47,20,warning,"Equals sign not aligned with surrounding assignments",Generic.Formatting.MultipleStatementAlignment,5
"/path/to/code/myfile.php",51,4,error,"Missing function doc comment",PEAR.Commenting.FunctionComment,5
The first row of the CSV output defines the order of information. When using the CSV output, please parse this header row to determine the order correctly as the format may change over time or new information may be added.
PHP_CodeSniffer can output a report in a format the compiler built into the GNU Emacs text editor can understand. This lets you use the built-in complier to run PHP_CodeSniffer on a file you are editing and navigate between errors and warnings within the file. To print an Emacs report, use the --report=emacs
command line argument. The output will look like this:
Sample PHP_CodeSniffer Emacs output
$ phpcs --report=emacs /path/to/code
/path/to/code/myfile.php:2:1: error - Missing file doc comment
/path/to/code/myfile.php:20:43: error - PHP keywords must be lowercase; expected "false" but found "FALSE"
/path/to/code/myfile.php:47:1: error - Line not indented correctly; expected 4 spaces but found 1
/path/to/code/myfile.php:47:20: warning - Equals sign not aligned with surrounding assignments
/path/to/code/myfile.php:51:4: error - Missing function doc comment
To use PHP_CodeSniffer with Emacs, make sure you have installed PHP mode for Emacs. Then put the following into your .emacs
file, changing PHP_CodeSniffer options as required.
Sample .emacs file
(defun my-php-hook-function ()
(set (make-local-variable 'compile-command) (format "phpcs --report=emacs --standard=PEAR %s" (buffer-file-name))))
(add-hook 'php-mode-hook 'my-php-hook-function)
Now you can use the compile command and associated shortcuts to move between error messages within your file.
PHP_CodeSniffer can make use of the svn blame
command to try and determine who committed each error and warning to an SVN respository. To print an SVN Blame report, use the --report=svnblame
command line argument. The output will look like this:
Sample PHP_CodeSniffer SVN Blame output
$ phpcs --report=svnblame /path/to/code
PHP CODE SNIFFER SVN BLAME SUMMARY
--------------------------------------------------------------------------------
AUTHOR COUNT (%)
--------------------------------------------------------------------------------
jsmith 51 (40.8)
jblogs 44 (30)
pdeveloper 43 (10.33)
jscript 27 (19.84)
--------------------------------------------------------------------------------
A TOTAL OF 165 SNIFF VIOLATION(S) WERE COMMITTED BY 4 AUTHOR(S)
--------------------------------------------------------------------------------
Each author is listed with the number of violations they committed and the percentage of error lines to clean lines. The example report above shows that the developer pdeveloper
has 43 violations but they only make up 10% of all code they have committed, while jblogs
has 44 violations but they make up 30% of all their committed code. So these developers have about the same number of total violations, but pdeveloper
seems to be doing a better job of conforming to the coding standard.
To show a breakdown of the types of violations each author is committing, use the -s
command line argument.
Sample PHP_CodeSniffer SVN Blame output with sources
$ phpcs -s --report=svnblame /path/to/code
PHP CODE SNIFFER SVN BLAME SUMMARY
--------------------------------------------------------------------------------
AUTHOR SOURCE COUNT (%)
--------------------------------------------------------------------------------
jsmith 51 (40.8)
Squiz.Files.LineLength 47
PEAR.Functions.FunctionCallSignature 4
jblogs 44 (30)
Squiz.Files.LineLength 40
Generic.CodeAnalysis.UnusedFunctionParameter 2
Squiz.CodeAnalysis.EmptyStatement 1
Squiz.Formatting.MultipleStatementAlignment 1
--------------------------------------------------------------------------------
A TOTAL OF 95 SNIFF VIOLATION(S) WERE COMMITTED BY 2 AUTHOR(S)
--------------------------------------------------------------------------------
To include authors with no violations, and perhaps shower them with praise, use the -v
command line argument.
Sample PHP_CodeSniffer SVN Blame verbose output
$ phpcs -v --report=svnblame /path/to/code
PHP CODE SNIFFER SVN BLAME SUMMARY
--------------------------------------------------------------------------------
AUTHOR COUNT (%)
--------------------------------------------------------------------------------
jsmith 51 (40.8)
jblogs 44 (30)
pdeveloper 43 (10.33)
jscript 27 (19.84)
toogood 0 (0)
--------------------------------------------------------------------------------
A TOTAL OF 165 SNIFF VIOLATION(S) WERE COMMITTED BY 5 AUTHOR(S)
--------------------------------------------------------------------------------
You need to make sure the location of the svn
command is in your path and that SVN is storing a username and password (if required by your repository). If the command is not in your path, the report will fail to generate. If SVN does not have a username and password stored, you'll need to enter it for each file being checked by PHP_CodeSniffer that contains violations.
Like the SVN Blame report, PHP_CodeSniffer can make use of the git blame
command to try and determine who committed each error and warning to a Git respository. To print a Git Blame report, use the --report=gitblame
command line argument. The output and options are the same as those described in the SVN Blame report above.
You need to make sure the location of the git
command is in your path. If the command is not in your path, the report will fail to generate.
PHP_CodeSniffer can print any combination of the above reports to either the screen or to separate files. To print multiple reports, use the --report-[type]
command line argument instead of the standard --report=[type]
format. You can then specify multiple reports using multiple arguments. The reports will be printed to the screen in the order you specify them on the command line.
Writing a full and summary report to the screen
$ phpcs --report-full --report-summary /path/to/code
You can write the reports to separate files by specifying the path to the output file after each report argument.
Writing a full and summary report to a file
$ phpcs --report-full=/path/to/full.txt --report-summary=/path/to/summary.txt /path/to/code
You can print some reports to the screen and other reports to files.
Writing a full report to a file and a summary report to the screen
$ phpcs --report-full=/path/to/full.txt --report-summary /path/to/code
Instead of producing a single report at the end of a run, PHP_CodeSniffer can run interactively and show reports for files one at a time. When using the interactive mode, PHP_CodeSniffer will show a report for the first file it finds an error or warning in. It will then pause and wait for user input. Once you have corrected the errors, you can press ENTER
to have PHP_CodeSniffer recheck your file and continue if the file is now free of errors. You can also choose to skip the file and move to the next file with errors.
To run PHP_CodeSniffer interactively, use the -a
command line argument.
Running interactively
$ phpcs -a /path/to/code
FILE: /path/to/code/myfile.php
--------------------------------------------------------------------------------
FOUND 5 ERROR(S) AND 1 WARNING(S) AFFECTING 5 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
20 | ERROR | PHP keywords must be lowercase; expected "false" but found
| | "FALSE"
47 | ERROR | Line not indented correctly; expected 4 spaces but found 1
47 | WARNING | Equals sign not aligned with surrounding assignments
51 | ERROR | Missing function doc comment
88 | ERROR | Line not indented correctly; expected 9 spaces but found 6
--------------------------------------------------------------------------------
<ENTER> to recheck, [s] to skip or [q] to quit :
PHP_CodeSniffer will always print the full error report for a file when running in interactive mode. Any report types you specify on the command line will be ignored.
By default, PHP_CodeSniffer will print all screen-based reports 80 characters wide. File paths will be truncated if they don't fit within this limit and error messages will be wrapped across multiple lines. You can increase the report width to show longer file paths and limit the wrapping of error messages using the -report-width
command line argument.
Setting the report width to be 120 characters
$ phpcs --report-width=120 --report=summary /path/to/code/myfile.php
PHP_CodeSniffer always prints the specified report to the screen, but it can also be told to write a copy of the report to a file. When writing to a file, all internal parsing errors and verbose output PHP_CodeSniffer produces will not be included in the file. This feature is particularly useful when using report types such as XML and CSV that are often parsed by scripts or used with continuous integration software.
To write a copy of a report to a file, use the --report-file
command line argument.
Writing a report to a file
$ phpcs --report=xml --report-file=/path/to/file.xml /path/to/code
The report will not be written to the screen when using this option. If you still want to view the report, use the -v
command line argument to print verbose output.
By default, PHP_CodeSniffer will use the PEAR coding standard if no standard is supplied on the command line. You can change the default standard by setting the default_standard
configuration option.
Setting the default standard to be the Squiz coding standard
$ phpcs --config-set default_standard Squiz
By default, PHP_CodeSniffer will use the full report format if no format is supplied on the command line. You can change the default report format by setting the report_format
configuration option.
Setting the default report format to be the summary report
$ phpcs --config-set report_format summary
By default, PHP_CodeSniffer will show both errors and warnings for your code. You can hide warnings for a single script run by using the -n
command line argument, but you can also enable this by default if you prefer. To hide warnings by default, set the show_warnings
configuration option to 0
.
Hiding warnings by default
$ phpcs --config-set show_warnings 0
When warnings are hidden by default, you can use the -w
command line argument to show them for a single script run.
By default, PHP_CodeSniffer will run quietly and only print the report of errors and warnings at the end. If you want to know what is happening you can turn on progress output, but you can also enable this by default if you prefer. To show progress by default, set the show_progress
configuration option to 1
.
Showing progress by default
$ phpcs --config-set show_progress 1
By default, PHP_CodeSniffer will show all errors and warnings with a severity level of 5
or greater. You can change these settings for a single script run by using the --severity
, --error-severity
and --warning-severity
command line arguments, but you can also change the default settings if you prefer.
Changing the default severity level to show all errors and warnings
$ phpcs --config-set severity 1
Changing the default severity levels to show all errors but only some warnings
$ phpcs --config-set error_severity 1
$ phpcs --config-set warning_severity 8
Setting the severity of warnings to0
is the same as using the-n
command line argument. If you set the severity of errors to0
PHP_CodeSniffer will not show any errors, which may be useful if you just want to show the warnings.
By default, PHP_CodeSniffer will print all screen-based reports 80 characters wide. File paths will be truncated if they don't fit within this limit and error messages will be wrapped across multiple lines. You can increase the report width to show longer file paths and limit the wrapping of error messages using the -report-width
command line argument, but you can also change the default report width by setting the report_width
configuration option.
Setting the default report width to be 120 characters
$ phpcs --config-set report_width 120
By default, PHP_CodeSniffer will treat all source files as if they use ISO-8859-1 encoding. This can cause double-encoding problems when generating UTF-8 encoded XML reports. To help PHP_CodeSniffer encode reports correctly, you can specify the encoding of your source files using the --encoding
command line argument, but you can also change the default encoding by setting the encoding
configuration option.
Setting the default encoding to UTF-8
$ phpcs --config-set encoding utf-8
By default, PHP_CodeSniffer will not convert tabs to spaces in checked files. Specifying a tab width will make PHP_CodeSniffer replace tabs with spaces. You can force PHP_CodeSniffer to replace tabs with spaces by default by setting the tab_width
configuration option.
Setting the default tab width to be 4 spaces
$ phpcs --config-set tab_width 4
When the tab width is set by default, the replacement of tabs with spaces can be disabled for a single script run by setting the tab width to zero.Disabling the replacement of tabs with spaces
$ phpcs --tab-width=0 /path/to/code
The Squiz coding standard includes a sniff that will check each JavaScript file using JSLint, a JavaScript program that looks for problems in JavaScript programs. Use the jslint_path
configuration option to tell the JSLint sniff where to find the tool.
Setting the path to JSLint
$ phpcs --config-set jslint_path /path/to/jslint.js
As JSLint is just JavaScript code, you also need to install Rhino to be able to execute it. Use the rhino_path
configuration option to tell the JSLint sniff where to find the tool.
Setting the path to Rhino
$ phpcs --config-set rhino_path /path/to/rhino
The Squiz coding standard includes a sniff that will check each JavaScript file using JavaScript Lint, a tool that checks all your JavaScript source code for common mistakes without actually running the script or opening the web page. Use the jsl_path
configuration option to tell the JavaScript Lint sniff where to find the tool.
Setting the path to JavaScript Lint
$ phpcs --config-set jsl_path /path/to/jsl
The Squiz coding standard includes a sniff that will check each file using the Google Closure Linter, an open source JavaScript style checker from Google. Use the gjslint_path
configuration option to tell the Google Closure Linter sniff where to find the tool.
Setting the path to the Google Closure Linter
$ phpcs --config-set gjslint_path /path/to/gjslint
The Zend coding standard includes a sniff that will check each file using the Zend Code Analyzer, a tool that comes with Zend Studio. Use the zend_ca_path
configuration option to tell the Zend Code Analyzer sniff where to find the tool.
Setting the path to the Zend Code Analyzer
$ phpcs --config-set zend_ca_path /path/to/ZendCodeAnalyzer
In this tutorial, we will create a new coding standard with a single sniff. Our sniff will prohibit the use of Perl style hash comments.
All sniffs in PHP_CodeSniffer must belong to a coding standard. A coding standard is a directory with a specific sub-directory structure and a ruleset.xml file, so we can create one very easily. Let's call our coding standard MyStandard. Run the following commands to create the coding standard directory structure:
$ mkdir MyStandard
$ mkdir MyStandard/Sniffs
As this coding standard directory sits outside the main PHP_CodeSniffer directory structure, PHP_CodeSniffer will not show it as an installed standard when using the-i
command line argument. If you want your standard to be shown as installed, create the MyStandard directory inside the PHP_CodeSniffer install:
$ cd /path/to/PHP_CodeSniffer/CodeSniffer/Standards
$ mkdir MyStandard
$ mkdir MyStandard/Sniffs
The MyStandard
directory represents our coding standard. The Sniffs
sub-directory is used to store all the sniff files for this coding standard.
Now that our directory structure is created, we need to add our ruleset.xml file. This file will allow PHP_CodeSniffer to ask our coding standard for information about itself, and also identify this directory as one that contains code sniffs.
$ cd MyStandard
$ touch ruleset.xml
The content of the ruleset.xml
file should be the following:
<?xml version="1.0"?>
<ruleset name="MyStandard">
<description>A custom coding standard.</description>
</ruleset>
The ruleset.xml can be left quite small, as it is in this example coding standard. For information about the other features that the ruleset.xml provides, see the annotated ruleset.xml.
A sniff requires a single PHP file. It's name should clearly describe the standard that we are enforcing and must end with Sniff.php
. For our sniff, we will name the PHP file DisallowHashCommentsSniff.php
and place it into a Commenting
sub-directory to categorise this sniff as relating to commenting. Run the following commands to create the category and the sniff:
$ cd Sniffs
$ mkdir Commenting
$ touch Commenting/DisallowHashCommentsSniff.php
It does not matter what sub-directories you use for categorising your sniffs. Just make them descriptive enough so you can find your sniffs again later when you want to modify them.
Each sniff must implement the PHP_CodeSniffer_Sniff
interface so that PHP_CodeSniffer knows that it should instantiate the sniff once it's invoked. The PHP_CodeSniffer_Sniff
interface defines two methods that must be implemented; register() and process().
The register() method allows a sniff to subscribe to one or more token types that it wants to process. Once PHP_CodeSniffer encounters one of those tokens, it calls the process() method with the PHP_CodeSniffer_File
object (a representation of the current file being checked) and the position in the stack where the token was found.
For our sniff, we are interested in single line comments. The token_get_all() method that PHP_CodeSniffer uses to acquire the tokens within a file distinguishes doc comments and normal comments as two separate token types. Therefore, we don't have to worry about doc comments interfering with our test. The register() method only needs to return one token type, T_COMMENT.
A sniff can gather more information about a token by acquiring the token stack with a call to the getTokens() method on the PHP_CodeSniffer_File
object. This method returns an array and is indexed by the position where the token occurs in the token stack. Each element in the array represents a token. All tokens have a code
, type
and a content
index in their array. The code
value is a unique integer for the type of token. The type
value is a string representation of the token (e.g., 'T_COMMENT' for comment tokens). The type
has a corresponding globally defined integer with the same name. Finally, the content
value contains the content of the token as it appears in the code.
Some tokens have more indexes than those described above. Have a look in the PHP/CodeSniffer/File.php
class comment for a full list of token indexes.
Once an error is detected, a sniff should indicate that an error has occurred by calling the addError() method on the PHP_CodeSniffer_File
object, passing in an appropriate error message as the first argument, the position in the stack where the error was detected as the second, a code to uniquely identify the error within this sniff and an array of data used inside the error message. Alternatively, if the violation is considered not as critical as an error, the addWarning() method can be used.
We now have to write the content of our sniff. The content of the DisallowHashCommentsSniff.php
file should be the following:
<?php
/**
* This sniff prohibits the use of Perl style hash comments.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Your Name <you@domain.net>
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version SVN: $Id: coding-standard-tutorial.xml,v 1.9 2008-10-09 15:16:47 cweiske Exp $
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* This sniff prohibits the use of Perl style hash comments.
*
* An example of a hash comment is:
*
* <code>
* # This is a hash comment, which is prohibited.
* $hello = 'hello';
* </code>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Your Name <you@domain.net>
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: @package_version@
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class MyStandard_Sniffs_Commenting_DisallowHashCommentsSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns the token types that this sniff is interested in.
*
* @return array(int)
*/
public function register()
{
return array(T_COMMENT);
}//end register()
/**
* Processes the tokens that this sniff is interested in.
*
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where
* the token was found.
*
* @return void
*/
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['content']{0} === '#') {
$error = 'Hash comments are prohibited; found %s';
$data = array(trim($tokens[$stackPtr]['content']));
$phpcsFile->addError($error, $stackPtr, 'Found', $data);
}
}//end process()
}//end class
?>
By default, PHP_CodeSniffer assumes all sniffs are designed to check PHP code only. You can specify a list of tokenizers that your sniff supports, allowing it to be used wth PHP, JavaScript or XML files, or any combination of the three. You do this by setting the$supportedTokenizers
member variable in your sniff. Adding the following code to your sniff will tell PHP_CodeSniffer that it can be used to check both PHP and JavaScript code:
<?php
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
);
?>
Now that we have defined a coding standard, let's validate a file that contains hash comments.
The test file we are using has the following contents:
<?php
# Check for valid contents.
if ($obj->contentsAreValid($array)) {
$value = $obj->getValue();
# Value needs to be an array.
if (is_array($value) === false) {
# Error.
$obj->throwError();
exit();
}
}
?>
When PHP_CodeSniffer is run on the file using our new coding standard, 3 errors will be reported:
$ phpcs --standard=/path/to/MyStandard test.php
FILE: test.php
--------------------------------------------------------------------------------
FOUND 3 ERROR(S) AFFECTING 3 LINE(S)
--------------------------------------------------------------------------------
3 | ERROR | Hash comments are prohibited; found # Check for valid contents.
7 | ERROR | Hash comments are prohibited; found # Value needs to be an array.
9 | ERROR | Hash comments are prohibited; found # Error.
--------------------------------------------------------------------------------
Note that we pass the absolute path to our coding standard directory on the command line because our standard is not installed inside the main PHP_CodeSniffer directory structure. If you have created your standard inside PHP_CodeSniffer, you can simply pass the name of the standard:
$ phpcs --standard=MyStandard Test.php
PHP_CodeSniffer allows developers to design their own coding standards by creating a simple ruleset XML file that both pulls in sniffs from existing standards and customises them for the developer's needs. This XML file can be named anything you like, as long as it has an xml
extension and complies to the ruleset.xml format. The file can be stored anywhere, making it perfect for placing under version control with a project's source code and unit tests.
Once created, a ruleset file can be used with the --standard
command line argument. In the following example, PHP_CodeSniffer will use the coding standard defined in a custom ruleset file called custom_ruleset.xml:
Using a custom ruleset file
$ phpcs --standard=/path/to/custom_ruleset.xml test.php
The following sample file documents the ruleset.xml format and shows you the complete range of features that the format supports. The file is designed for documentation purposes only and is not a working coding standard.
<?xml version="1.0"?> <ruleset name="Custom Standard"> <!-- The name attribute of the ruleset tag is displayed when running PHP_CodeSniffer with the -v command line argument. The description tag below is not displayed anywhere except in this file, so it can contain information for developers who may change this file in the future. --> <description>A custom coding standard</description> <!-- You can hard-code ignore patterns directly into your custom standard so you don't have to specify the patterns on the command line. The following two tags are equivalent to the command line argument: --ignore=*/tests/*,*/data/* --> <exclude-pattern>*/tests/*</exclude-pattern> <exclude-pattern>*/data/*</exclude-pattern> <!-- Include all sniffs in the PEAR standard. Note that the path to the standard does not have to be specified as the PEAR standard exists inside the PHP_CodeSniffer install directory. --> <rule ref="PEAR"/> <!-- Include all sniffs in an external standard directory. Note that we have to specify the full path to the standard's directory because it does not exist inside the PHP_CodeSniffer install directory. --> <rule ref="/home/username/standards/mystandard"/> <!-- Include everything in another ruleset.xml file. This is really handy if you want to customise another developer's custom standard. They just need to distribute their single ruleset file to allow this. --> <rule ref="/home/username/standards/custom.xml"/> <!-- Include all sniffs in the Squiz standard except one. Note that the name of the sniff being excluded is the code that the sniff is given by PHP_CodeSniffer and is based on the file name and path of the sniff class. You can display these codes using the -s command line argument when checking a file. --> <rule ref="Squiz"> <exclude name="Squiz.PHP.CommentedOutCode"/> </rule> <!-- Include some specific sniffs from the Generic standard. Note again that the name of the sniff is the code that PHP_CodeSniffer gives it. --> <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter"/> <rule ref="Generic.Commenting.Todo"/> <rule ref="Generic.ControlStructures.InlineControlStructure"/> <!-- Here we are including a specific sniff but also changing the error message of a specific message inside the sniff. Note that the specific code for the message, which is CommentFound in this case, is defined by the sniff developer. You can display these codes by using the -s command line argument when checking a file. Also note that this message has a variable inside it, which is why it is important that sniffs use a printf style format for their error messages. We also drop the severity of this message from the default value (5) so that it is hidden by default. It can be displayed by setting the minimum severity on the PHP_CodeSniffer command line. This is great if you want to use some messages only in code reviews and not have them block code commits. --> <rule ref="Generic.Commenting.Todo.CommentFound"> <message>Please review this TODO comment: %s</message> <severity>3</severity> </rule> <!-- Here we change two messages from the same sniff. Note how the codes are slightly different because the sniff developer has defined both a MaxExceeded message and a TooLong message. In the case of this sniff, one is used for warnings and one is used for errors. --> <rule ref="Generic.Files.LineLength.MaxExceeded"> <message>Line contains %s chars, which is longer than the max limit of %s</message> </rule> <rule ref="Generic.Files.LineLength.TooLong"> <message>Line longer than %s characters; contains %s characters</message> </rule> <!-- Some sniffs have public member vars that allow you to customise specific elements of the sniff. In the case of the Generic LineLength sniff, you can customise the limit at which the sniff will throw warnings and the limit at which it will throw errors. The rule below includes the LineLength sniff but changes the settings so the sniff will show warnings for any line longer than 90 chars and errors for any line longer than 100 chars. --> <rule ref="Generic.Files.LineLength"> <properties> <property name="lineLimit" value="90"/> <property name="absoluteLineLimit" value="100"/> </properties> </rule> <!-- Another useful example of changing sniff settings is to specify the end of line character that your standard should check for. --> <rule ref="Generic.Files.LineEndings"> <properties> <property name="eolChar" value="\r\n"/> </properties> </rule> <!-- Boolean values should be specified by using the strings "true" and "false" rather than the integers 0 and 1. --> <rule ref="Generic.Formatting.MultipleStatementAlignment"> <properties> <property name="maxPadding" value="8"/> <property name="ignoreMultiLine" value="true"/> <property name="error" value="true"/> </properties> </rule> <!-- If you want to completely disable an error message in a sniff but you don't want to exclude the whole sniff, you can change the severity of the message to 0. In this case, we want the Squiz DoubleQuoteUsage sniff to be included in our standard, but we don't want the ContainsVar error message to ever be displayed. --> <rule ref="Squiz.Strings.DoubleQuoteUsage.ContainsVar"> <severity>0</severity> </rule> <!-- There is a special internal error message produced by PHP_CodeSniffer when it is unable to detect code in a file, possible due to the use of short open tags even though php.ini disables them. You can disable this message in the same way as sniff messages. Again, the code here will be displayed in the PHP_CodeSniffer output when using the -s command line argument while checking a file. --> <rule ref="Internal.NoCodeFound"> <severity>0</severity> </rule> <!-- You can also hard-code ignore patterns for specific sniffs, a feature not available on the command line. The code here will hide all messages from the Squiz DoubleQuoteUsage sniff for files that match either of the two exclude patterns. --> <rule ref="Squiz.Strings.DoubleQuoteUsage"> <exclude-pattern>*/tests/*</exclude-pattern> <exclude-pattern>*/data/*</exclude-pattern> </rule> <!-- You can also be more specific and just exclude some messages. The code here will just hide the ContainsVar error generated by the Squiz DoubleQuoteUsage sniff for files that match either of the two exclude patterns. --> <rule ref="Squiz.Strings.DoubleQuoteUsage.ContainsVar"> <exclude-pattern>*/tests/*</exclude-pattern> <exclude-pattern>*/data/*</exclude-pattern> </rule> </ruleset>
The SVN pre-commit hook has different requirements than the main PHP_CodeSniffer package. See the Requirements page for more information.
A pre-commit hook is a feature available in the Subversion version control system that allows code to be validated before it is committed to the repository. The PHP_CodeSniffer pre-commit hook allows you to check code for coding standard errors and stop the commit process if errors are found. This ensures developers are not able to commit code that violates your coding standard. Instead, they are presented with the list of errors they need to correct before committing.
Sample pre-commit output
$ svn commit -m "Test" temp.php
Sending temp.php
Transmitting file data .svn: Commit failed (details follow):
svn: 'pre-commit' hook failed with error output:
FILE: temp.php
---------------------------------------------------------------
FOUND 1 ERROR(S) AND 0 WARNING(S) AFFECTING 1 LINE(S)
---------------------------------------------------------------
2 | ERROR | Missing file doc comment
--------------------------------------------------------------
Edit /path/to/PHP_CodeSniffer/scripts/phpcs-svn-pre-commit
and replace @php_bin@
in the first line with the path to the PHP CLI. For example, #!@php_bin@
becomes #!/usr/bin/php
.
Now ensure the path to svnlook
is correct by modifying the following line, if required:
Changing the path to svnlook
define('PHP_CODESNIFFER_SVNLOOK', '/usr/bin/svnlook');
Now add the following line to your pre-commit file in the Subversion hooks directory:
Adding the pre-commit hook to the Subversion config file
/path/to/PHP_CodeSniffer/scripts/phpcs-svn-pre-commit "$REPOS" -t "$TXN" >&2 || exit 1
You can also use all the standard phpcs
command line options to do things like set the standard to use, the tab width and the error report format:
Adding the pre-commit hook to the Subversion config file
/path/to/PHP_CodeSniffer/scripts/phpcs-svn-pre-commit --standard=Squiz --tab-width=4 "$REPOS" -t "$TXN" >&2 || exit 1
No. PHP_CodeSniffer is not a tool for testing that your PHP application works correctly. All PHP_CodeSniffer will do is ensure your PHP code meets the standards that you are following.
Maybe you don't, but if you want to ensure you adhere to a set of coding standards, PHP_CodeSniffer is a quick and easy way to do that. PHP_CodeSniffer is a replacement for the more manual task of checking coding standards in code reviews. With PHP_CodeSniffer, you can reserve code reviews for the checking of code correctness.
Coding standards are a good thing. They will make your code easier to read and maintain, especially when multiple developers are working on the same application. Consider using coding standards if you don't already.
No. PHP_CodeSniffer does not actually parse your code, and so cannot accurately tell if your code contains parse errors. PHP_CodeSniffer does know about some parse errors and will warn you if it finds code that it is unable to sniff correctly due to a suspected parse error. However, as there is no actual parsing taking place, PHP_CodeSniffer may return an incorrect number of errors when checking code that does contain parse errors.
You can easily check for parse errors in a file using the PHP command line interface and the
-l
(lowercase L) option.
$ php -l /path/to/code/myfile.inc
No syntax errors detected in /path/to/code/myfile.inc
Yes. At its core, PHP_CodeSniffer is just a framework for enforcing coding standards. We release PHP_CodeSniffer with some sample coding standards to help developers get started on projects where there is no standard defined. If you want to write your own standard, read the tutorial on creating coding standards.
Sometimes, errors mask the existence of other errors, or new errors are created as you fix others. For example, PHP_CodeSniffer might tell you that an inline IF statement needs to be defined with braces. Once you make this change, PHP_CodeSniffer may report that the braces you added are not correctly aligned.
Always run PHP_CodeSniffer until you get a passing result. Once you've made the changes PHP_CodeSniffer recommends, run PHP_CodeSniffer again to ensure no new errors have been added.
As much as we trust PHP_CodeSniffer to check your code for coding standard errors, we don't trust any application to ever change code for us without reviewing it first. Considering you would have to check each change PHP_CodeSniffer made before releasing the source code, why not make the changes manually?
Making the changes manually ensures a couple of positive things happen:
Developers learn the coding standards and make less mistakes in the future.
Developers can ensure that PHP_CodeSniffer is working correctly.
Developers can decide if a coding standard doesn't fit a particular piece of code.
So if you find yourself wishing PHP_CodeSniffer would just go ahead and make those changes for you, maybe you just need to read the coding standards and adhere to them a bit better.
No matter how small of a change you make, always test your code before committing it to your code repository or releasing it. Even changes suggested by PHP_CodeSniffer need to be tested, as small and insignificant as they may seem.
For PHP files, PHP_CodeSniffer uses PHP's inbuilt tokenizer functions to parse your code. It then changes that output to include much more data about the file, such as matching function braces to function keywords.
For all other file types, PHP_CodeSniffer includes a custom tokenizer that either makes use of PHP's inbuilt tokenizer or emulates it. In both cases, the token array must be checked and changed manually before all the standard PHP_CodeSniffer matching rules are applied, making tokenizing a bit slower for these file types.
Provides missing functionality for older versions of PHP.
PHP_Compat provides missing functionality in the form of functions and constants for older versions of PHP.
The replicated functions are designed to be interchangable with their native equivilants. They have the same signature, same return values and throw the same errors. Each function is unit tested to ensure accuracy.
Loading a component with PHP_Compat:
<?php
require_once 'PHP/Compat.php';
PHP_Compat::loadFunction('file_get_contents');
?>
Or, if you don't wish to use the class, you can load it manually.
Loading a component manually:
<?php
require_once 'PHP/Compat/Function/file_put_contents.php';
?>
The function is then ready to use like you would normally.
PHP_Compat is designed for ease of use. It has no dependencies and can be used completely outside the PEAR infrastructure.
The following functions have been replicated:
Function | Since |
---|---|
array_change_key_case() | PHP 4.2.0 |
array_chunk() | PHP 4.2.0 |
array_combine() | PHP 5.0.0 |
array_diff_assoc() | PHP 4.3.0 |
array_diff_key() | PHP 5.0.2 |
array_diff_ukey() | PHP 5.0.2 |
array_intersect_assoc() | PHP 5.0.0 |
array_intersect_key() | PHP 5.0.2 |
array_intersect_uassoc() | PHP 5.0.0 |
array_intersect_ukey() | PHP 5.0.2 |
array_key_exists() | PHP 4.1.0 |
array_product() | PHP 5.1.0 |
array_search() | PHP 4.0.5 |
array_udiff() | PHP 5.0.0 |
array_udiff_assoc() | PHP 5.0.0 |
array_udiff_uassoc() | PHP 5.0.0 |
array_uintersect() | PHP 5.0.0 |
array_uintersect_assoc() | PHP 5.0.0 |
array_uintersect_uassoc() | PHP 5.0.0 |
array_walk_recursive() | PHP 5.0.0 |
call_user_func_array() | PHP 4.0.4 |
bcinvert() | PHP 5.2.0 |
bcpowmod() | PHP 5.0.0 |
clone() | PHP 5.0.0 |
constant() | PHP 4.0.4 |
convert_uudecode() | PHP 5.0.0 |
convert_uuencode() | PHP 5.0.0 |
debug_print_backtrace() | PHP 5.0.0 |
file_get_contents() | PHP 4.3.0 |
file_put_contents() | PHP 5.0.0 |
floatval() | PHP 4.2.0 |
fprintf() | PHP 5.0.0 |
fputcsv() | PHP 5.0.0 |
get_headers() | PHP 5.0.0 |
get_include_path() | PHP 4.3.0 |
html_entity_decode() | PHP 4.3.0 |
htmlspecialchars_decode() | PHP 5.1.0 |
http_build_query() | PHP 5.0.0 |
ibase_timefmt() | PHP 5.0.0 |
idate() | PHP 5.1.0 |
image_type_to_mime_type() | PHP 4.3.0 |
inet_ntop() | PHP 5.1.0 |
inet_pton() | PHP 5.1.0 |
ini_get_all() | PHP 4.2.0 |
is_a() | PHP 4.2.0 |
is_scalar() | PHP 4.0.5 |
md5_file() | PHP 4.2.0 |
mhash() | PHP 4.1.0 |
mime_content_type() | PHP 4.3.0 |
ob_clean() | PHP 4.2.0 |
ob_flush() | PHP 4.2.0 |
ob_get_clean() | PHP 4.3.0 |
ob_get_flush() | PHP 4.3.0 |
php_strip_whitespace() | PHP 5.0.0 |
property_exists() | PHP 5.1.0 |
pg_affected_rows() | PHP 4.2.0 |
pg_escape_bytea() | PHP 4.2.0 |
pg_unescape_bytea() | PHP 4.2.0 |
restore_include_path() | PHP 4.3.0 |
scandir() | PHP 5.0.0 |
set_include_path() | PHP 4.3.0 |
str_ireplace() | PHP 5.0.0 |
str_rot13() | PHP 4.2.0 |
str_shuffle() | PHP 4.3.0 |
str_split() | PHP 5.0.0 |
str_word_count() | PHP 4.3.0 |
stripos() | PHP 5.0.0 |
strpbrk() | PHP 5.0.0 |
strripos() | PHP 5.0.0 |
substr_compare() | PHP 5.0.0 |
time_sleep_until() | PHP 5.1.0 |
var_export() | PHP 4.2.0 |
version_compare() | PHP 4.1.0 |
vprintf() | PHP 4.1.0 |
vsprintf() | PHP 4.1.0 |
The following constants have been replicated:
File | Constants | Since |
---|---|---|
DIRECTORY_SEPARATOR | DIRECTORY_SEPARATOR | PHP 4.0.6 |
E_STRICT |
|
PHP 5 |
FILE |
|
PHP 5 |
PATH_SEPARATOR |
|
PHP 4.3.0 |
PHP_EOL |
|
PHP 5 |
STD |
|
PHP 4.3.0 |
T |
|
PHP 5 |
UPLOAD_ERR |
|
PHP 4.3.0 |
In order to create portable code, it's best to assume all the native functions already exist. Littering a script with calls to PHP_Compat can become messy, and increase the work required to port the code.
To solve this there are a number of approaches. The first would
be adding the load statements to a file which is included at the
top of every document. The second, and preferred method is to make
use of php_auto_append
php.ini
value. This can be set in a .htaccess
file,
and no modifications to the actual code are required.
If phpcompat.php
was the name of the file, an
example entry in a .htaccess
could be
php_value php_auto_append phpcompat.php
.
The phpcompat.php
file would make use of either the
loadVersion or loadFunction/loadConstant methods discussed in the next
page.
mixed PHP_Compat::loadFunction (
mixed $function
)
Loads a function, or an array of functions.
$function
The name, or an array of names, of functions to load
mixed
Loading a function with the class:
<?php
require_once 'PHP/Compat.php';
// load file_put_contents
PHP_Compat::loadFunction('file_put_contents');
// load str_split, array_chunk and file_get_contents
PHP_Compat::loadFunction(array('str_split', 'array_chunk', 'file_get_contents'));
?>
You may also load a function without using the class.
Loading a function manually:
<?php
require_once 'PHP/Compat/Function/file_put_contents.php';
?>
This function should be called statically.
mixed PHP_Compat::loadConstant (
mixed $constant
)
Loads a constant, or an array of constants.
$constant
The name, or an array of names, of constants to load
mixed
Loading a constant with the class:
<?php
require_once 'PHP/Compat.php';
// load E_STRICT
PHP_Compat::loadConstant('E_STRICT');
// load E_STRICT, and PATH_SEPATATOR
PHP_Compat::loadConstant(array('E_STRICT', 'PATH_SEPARATOR'));
// load the STD group of constants (STDIN, STDOUT, STDERR)
PHP_Compat::loadConstant('STD');
?>
You may also load a constant without using the class.
Loading a constant manually:
<?php
require_once 'PHP/Compat/Constant/E_STRICT.php';
?>
This function should be called statically.
array PHP_Compat::loadVersion (
string $version
)
Load all components, or all components until a given version of PHP.
$version
The version of PHP to load components until.
array
Loading all components:
<?php
require_once 'PHP/Compat.php';
$components = PHP_Compat::loadVersion();
// To see which components were loaded
print_r($components);
?>
This example would show a long list of components which were loaded.
Loading up to a specific version:
The components loaded would be those with versions lower than, or equal to the supplied version and greater than the current PHP version.
<?php
require_once 'PHP/Compat.php';
$components = PHP_Compat::loadVersion('4.3.0');
// To see which components were loaded
print_r($components);
?>
This would output an array of components which were loaded. The output would be simular to:
Array
(
[array_diff_assoc] => 1
[file_get_contents] => 1
[get_include_path] => 1
[html_entity_decode] => 1
[image_type_to_mime_type] => 1
[ob_get_clean] => 1
[ob_get_flush] => 1
[restore_include_path] => 1
[set_include_path] => 1
[str_shuffle] => 1
[str_word_count] => 1
[FILE] => 1
[STD] => 1
[UPLOAD_ERR] => 1
)
This function should be called statically.
Find out the minimum version and the extensions required for a piece of code to run
Mandatory resources :
PHP 4.3.10 or newer.
PEAR 1.5.4 or newer.
PEAR::Console_Table 1.0.5 or newer.
PEAR::Console_GetArgs 1.3.3 or newer.
PEAR::File_Find 1.3.0 or newer.
PEAR::Event_Dispatcher 1.0.0 or newer.
pcre extension.
tokenizer extension.
Optional resources :
PHP 5.0.0 or newer.
PHPUnit 3.2.0 or newer.
PEAR::XML_Util 1.1.4 or newer.
PEAR::XML_Beautifier 1.1 or newer.
PEAR::Console_ProgressBar 0.5.2beta or newer.
PEAR::Var_Dump 1.0.3 or newer.
PEAR::HTML_Table 1.8.2 or newer.
You can download and use it for free. But don't delete the copyright notice. You can read terms of the PHP license.
YES if there is no answer in this Guide and if you are ready to share some informations such as : your configuration (platform Win *nix mac, PHP version, PEAR packages installed) and perharps your script.
You can report it with the bug tracker at PEAR.
Report it with the bug tracker at PEAR. If error is confirmed, a new PHP_CompatInfo release will follow.
PEAR (an acronym for PHP Extension and Application Repository) is a framework and distribution system for reusable PHP components.
Don't forget to read also the PEAR Manual and PEAR FAQ.
If you want to ignore all PHP5 code (functions, constants, extensions), you only need to add a line on your parsing (file, directory, string) options: "ignore_versions". In this example all PHP 5.0.0 to 5.2.0 code will be ignored when parsing current directory.
<?php
require_once 'PHP/CompatInfo.php';
$dir = dirname(__FILE__);
$options = array('ignore_versions' => array('5.0.0', '5.2.0'));
$pci = new PHP_CompatInfo();
$res = $pci->parseDir($dir, $options);
var_dump($res);
?>
Even if it's the new behavior of API 1.8.0, you can still consumes all output events with the Null renderer. Give the null value (case insensitive), as first parameter to the class constructor.
<?php
require_once 'PHP/CompatInfo.php';
$pci = new PHP_CompatInfo('null');
$res = $pci->parseData($datasource);
// display results is only produced by the line below
var_dump($res);
?>
For a single file, it's not necessary to have a progress bar or a message wait. But when you parse a directory with many subdirectories and files, it may take some time.
Depending of interface you're running (CLI or Web) you've two ways to display a progress bar or a message wait.
If you specify a progress bar to wait and you don't have PEAR::Console_ProgressBar package installed, default use standard text messages (no error stop the process).
Wait while parsing data source ... Wait while parsing file "C:\wamp\tmp\Services_W3C_CSSValidator-0.1.0\CSSValidator.php"
On CLI with pci command,
give the -p|--progress switch with either
bar
(for a progress bar), or text
(for a simple text message).
pci --summarize --progress bar --dir C:\Temp\beehiveforum082\forum
On CLI without pci command,
use the second parameter (driver specific options) of class constructor.
Give progress key with
bar
(for a progress bar), or
text
(for a simple text message).
And don't forget to de-activate silent mode (default is on for behavior
backward compatibility).
<?php
require_once 'PHP/CompatInfo.php';
$driverType = 'csv';
$driverOptions = array('silent' => false, 'progress' => 'bar');
$pci = new PHP_CompatInfo($driverType, $driverOptions);
?>
It will produce something like:
- 79/419 files [====>-----------------] 18.85% Elapsed Time: 00:28.93
For Web SAPI see the full example available
in distribution into examples directory named
pci180_parsefolder_tohtml.php
. It used the
PEAR::HTML_Progress2 package to produce a progress bar with COMET method
(not AJAX).
If you need PHP3 detection, DO NOT use version 1.5.0 or greater, but the latest release of 1.4 branch (1.4.3)
Initial PEAR public release: occured in March 2003, one month later after package proposal was launched under name PHP_Compatibility
Added the ability to ignore files:
in PHP_CompatInfo::parseFolder()
and PHP_CompatInfo::parseArray()
Added the ability to ignore folders:
in PHP_CompatInfo::parseFolder()
Added the ability to ignore functions: in all public methods.
Added a command line interface: with new class PHP_CompatInfo_Cli
Added a clone function:
to PHP_CompatInfo::parseFolder()
named PHP_CompatInfo::parseDir()
.
parseFolder() became now an alias of parseDir().
Added new option file_ext for parseArray: This option contains an array of file extensions to parse for PHP code. Default: php, php4, inc, phtml
Fixed integration with PEAR_PackageFileManager:
PackageFileManager::detectDependencies()
implement
call to PHP_CompatInfo and allow to detect easily minimum PHP version needed.
Improved PHP5 detection: one year after first PEAR public release, stable version 1.0.0 fixed some bugs and also allow PHP3 script/function detection.
Added support for reporting max PHP version: came with user request 6056
Improved PECL detection: especially with SAPI extension.
Added new option ignore_constants: to
PHP_CompatInfo::parseArray()
,
PHP_CompatInfo::parseDir()
,
PHP_CompatInfo::parseFolder()
,
PHP_CompatInfo::parseFile()
,
PHP_CompatInfo::parseString()
,
Load components list for a PHP version or subset:
added new function PHP_CompatInfo::loadVersion()
to API
Improved command line interface: output is limited to 80 columns for a better render. There is also now a windows launcher (named compatinfo.bat).
package xml 1.0: was stopped to be generated on version 1.3.2 (september 2006)
Inactive leader: Davey Shafik, initial author of this package, became inactive, and I (Laurent Laville) follow his steps as new active package leader.
PHP 4.3.0: is now required as a minimum to use both web and cli interfaces.
License upgrade: from PHP 3.0 to PHP 3.01
Improved PHP5 detection: with some bug fixes
Improved PHP5 detection: with new data source version.xml (revision 1.8) and funclist.txt (revision 1.39).
Dropped support of PHP3 detection: due to content of new data source version.xml (1.8) and funclist.txt (1.39)
Command line interface: under windows (pci.bat) and unix (pci.php) have now unified name. Idea came from PHP_CodeSniffer package that allow to fix lot of errors/warnings of coding standard.
Debug output of CLI gave now origin of extension (from PECL or not) and minimum extension version (rather than PHP). See example docs/examples/checkExtensions.php
Improved PHP5 detection: with data source version.xml (revision 1.9) and funclist.txt (revision 1.39).
More PHP constant are detected natively:
Some Core Predefined Constants ( PHP_EOL, DIRECTORY_SEPARATOR, PATH_SEPARATOR, E_STRICT )
Date Predefined Constants ( DATE_* )
CLI Specific Constants ( STD* )
File Upload Error Specific Constants ( UPLOAD_ERR_* )
Command line interface: allow now to detect version of a simple string (code without using script tags <?php ... ?>. Uses new parameter -s or --string.
Command line interface: allow now to print either an xml or text report (default). Uses new parameter -r or --report.
loadVersion() may return both function or function+constant list.
PHP method chaining is implemented on "Davey Shafik" request #13094.
Fix CLI output render to 80 columns, on main table :
Fix CLI output render to 80 columns, on additionnal tables :
Improved detection on conditionnal code: with new options : ignore_functions_match, ignore_extensions_match, ignore_constants_match.
That allow to implement request 12857 : add the option to locally mask exceptions.
Implement request 13138 : separate constants and tokens in results.
Implement request 13147 : add filter file extension option on parsing directory (CLI)
On CLI, the XML report generation is now xml compliant with a root tag (pci)
XML format is incompatible with version 1.6.x
ability to know conditional code (such as function_exists) used by php scripts. A level (and details) of warning about conditional code found during parsing source code is available for all interface (including command line : with new column C)
On CLI text report is customizable with new --output-level switch. Path/File+Version are always given on minimum report (0), until full details (max:15 - default).
On CLI you may now summarize the result with new --summarize switch. Remove extra lines for all files when parsing a directory.
Rewrites of core API following the MVC pattern came from a user request that wanted to customized its CLI output. Even if output-level switch already exists, there is no easy way with no process-logic dependency.
A word about the new architecture: Parser logic may be found in class PHP_CompatInfo_Parser, while PHP_CompatInfo class is still the main controller, but just a wrapper to parser methods. Each output format (csv, xml, text, html, array, null) is produced by an independant renderer ( PHP_CompatInfo_Renderer_Csv, PHP_CompatInfo_Renderer_Xml, PHP_CompatInfo_Renderer_Text, PHP_CompatInfo_Renderer_Html, PHP_CompatInfo_Renderer_Array, PHP_CompatInfo_Renderer_Null ) with the common interface PHP_CompatInfo_Renderer
To create your own renderer or just customize a bit an exists renderer, please
have a look on example named pci180_parsedir_tohtml.php
Here are the list of news and changes since previous API:
Always display result on standard output. To disable this feature, see the FAQ entry package.php.php-compatinfo.faq.
2 news output format.
With the new renderer system, the Web SAPI may use the xml
render (until now limited to CLI). While there is two news render:
csv
and html
.
Default output format (PHP dump array) use the array
render
while text
is for CLI.
A common method to parse all data sources. The new method PHP_CompatInfo::parseData() allow to parse all data source (array, string, file, directory) with the same interface. All others PHP_CompatInfo::parse* functions become now alias of this new method (to keep backward compatibility).
Event-driven architecture allow multiple renderer and extending core API of PHP_CompatInfo.
Progress bar for long process. Ability to display simple text wait message or a progress bar when parsing directories (or lot of files) from the command line, and even on web interface.
Constant E_RECOVERABLE_ERROR introduced with PHP 5.2.0, is now detected.
With API 1.8.0, there is a limit in class (internal, and end-user defined) detection. This limit is also valuable for lot of constants and PHP extensions.
API 1.8.0 detected PHP4 classes constructors as simple functions, that produce wrong results, especially if class name is also an extension function. Example: HTTP_Request class from PEAR package may be also found into pecl_http extension.
API 1.9.0 introduce a 3 categories dictionary system: class, constant and function.
class dictionary
$GLOBALS['_PHP_COMPATINFO_CLASS'] found into CompatInfo/class_array.php
file
constant dictionary
$GLOBALS['_PHP_COMPATINFO_CONST'] found into CompatInfo/const_array.php
file
function dictionary
$GLOBALS['_PHP_COMPATINFO_FUNCS'] found into CompatInfo/func_array.php
file
Each dictionary can include data from 0 to N extensions depending of your need of detection (and/or platform running).
Extensions Support List (aka ESL) default built include following 30 extensions:
PHP4 users may extend this list by hand and construct their own with other available extensions provided into package distribution.
PHP5 users may also extend this list by hand, but there is a script named
pciconf
(pciconf.bat
for Windows users)
that can simplify the build process.
API 1.9.0 provided also new methods to retrieve easily information from array results, global or for a specific file in results list. These methods are:
getVersion Minimum (and or maximal) PHP Version
getClasses All classes (internal or end-user defined)
getFunctions All functions (internal or end-user defined)
getExtensions All extensions (internal or external)
getConstants All constants (internal or end-user defined)
getTokens All PHP5+ tokens
getConditions All (warning) code conditions
getIgnoredFiles List of files ignored during parsing data source
getIgnoredFunctions List of functions (user or PHP internal) ignored during parsing data source
getIgnoredExtensions List of PHP extensions ignored during parsing data source
getIgnoredConstants List of constants (user or PHP internal) ignored during parsing data source
getSummary Returns only summary information when parsing a directory or multiple data sources
PHP_CompatInfo (alias PCI) should be installed using the PEAR Installer. This installer which provides a distribution system for PHP packages and full application, support channels architecture and custom file tasks. Learn more about new features in PEAR 1.4
Although using the PEAR Installer is the most easy way to install PCI, you can install PCI manually. For manual installation, do the following (steps 11-12 are for version 1.9.0+):
Download the most recent release archive from
http://pear.php.net/get/PHP_CompatInfo/
and extract it to a directory that is listed in the include_path
of your php.ini
configuration file.
Prepare the pci.bat
script:
For windows users only
Rename the compatinfo.bat
script to pci.bat
.
Replace the @php_bin@
string in it with the path
to your PHP command-line interpreter.
Replace also the @bin_dir@
string in it with the directory
where you will put the pci script.
Copy it to a directory that is in your PATH
Prepare the pci
script:
Rename the pci.php
file to pci
.
Replace the @php_bin@
string in it with the path
to your PHP command-line interpreter (usually /usr/bin/php
).
Copy it to a directory that is in your PATH
and
make it executable (chmod +x pci), or for windows users
copy it to the directory corresponding to @bin_dir@
string
(see previous modification).
Prepare the html
renderer script (CompatInfo/Renderer/Html.php):
Replace the @data_dir@
string in it with the path
where you have extracted the downloaded package.
If you do not move the stylesheet pci.css
to another location,
remove the line with . '@package_name@' . DIRECTORY_SEPARATOR
.
Download also, release archive of PEAR::Console_Table package version 1.0.5 (or better), from
http://pear.php.net/package/Console_Table/download
and extract it to a directory that is listed in the include_path
of your php.ini
configuration file.
Download also, release archive of PEAR::Console_GetArgs package version 1.3.3 (or better), from
http://pear.php.net/package/Console_GetArgs/download
and extract it to a directory that is listed in the include_path
of your php.ini
configuration file.
Download also, release archive of PEAR::File_Find package version 1.3.0 (or better), from
http://pear.php.net/package/File_Find/download
and extract it to a directory that is listed in the include_path
of your php.ini
configuration file.
Download also, release archive of PEAR::Event_Dispatcher package version 1.0.0 (or better), from
http://pear.php.net/package/Event_Dispatcher/download
and extract it to a directory that is listed in the include_path
of your php.ini
configuration file.
Depending of what renderer (XML) you will use, you may also need to download
release archive of PEAR::XML_Util package version 1.1.4 (or better), from
http://pear.php.net/package/XML_Util/download
and extract it to a directory that is listed in the include_path
of your php.ini
configuration file.
Depending of what renderer (HTML) you will use, you may also need to download
release archive of PEAR::HTML_Table package version 1.8.2 (or better), from
http://pear.php.net/package/HTML_Table/download
and extract it to a directory that is listed in the include_path
of your php.ini
configuration file.
Prepare the pciconf.bat
script:
For windows users only
Replace the @php_bin@
string in it with the path
to your PHP command-line interpreter.
Replace also the @bin_dir@
string in it with the directory
where you will put the pciconf script.
Copy it to a directory that is in your PATH
Prepare the pciconf
script:
Rename the configure.php
file to pciconf
.
Replace the @php_bin@
string in it with the path
to your PHP command-line interpreter (usually /usr/bin/php
).
Copy it to a directory that is in your PATH
and
make it executable (chmod +x pci), or for windows users
copy it to the directory corresponding to @bin_dir@
string
(see previous modification).
Replace the @php_dir@
string in it with the path
to your PEAR directory installation.
There are two types of interface to run a PHP_CompatInfo (alias PCI) script detection: CLI and web. All PCI features (since 1.7.0) are available on both interface.
It's up to you to choose what is the best usage for you.
PCI may detect without error and with a great precision, any simple PHP scripts
that have no switch conditions such as these ones :
function_exists
or
version_compare
.
<?php
// ...
if (function_exists('debug_backtrace')) {
$backtrace = debug_backtrace();
} else {
$backtrace = false;
}
if (version_compare(phpversion(), '5.0.0', '<')) {
include_once 'PHP/Compat.php';
PHP_Compat::loadFunction('ob_get_clean');
PHP_Compat::loadConstant('PHP_EOL');
}
// ...
?>
Don't be afraid, PCI can still be use, even if your PHP scripts have these conditions or any others, but you should help it, to adjust the parser accuracy with one or more options. See the parser options reference list for details and advanced detection section.
In most case, the basic detection is enough. But sometimes, we will need to
adjust accuracy of parser to give the best result. It is possible with
$option
, the second parameter of each
parser method. See parser options list for details.
Suppose we have to detect which PHP version we need to run this script named "math.php"
<?php
$nb = bcsub(1.234, 5, 4);
if (preg_match('/^-/', $nb)) {
echo 'minus';
}
?>
We will use this very simple detection script.
<?php
require_once 'PHP/CompatInfo.php';
$source = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'math.php';
$info = new PHP_CompatInfo();
$info->parseFile($source);
// you may also use unified method: $info->parseData($source);
?>
Default output used the Array renderer (we will talk about it and other renderers later; don't be afraid if you don't know what is it yet). Here are the raw results we got on standard output :
array ( 'ignored_files' => array ( ), 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.0.0', 'classes' => array ( ), 'extensions' => array ( 0 => 'bcmath', 1 => 'pcre', ), 'constants' => array ( ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 0, ), )
It means that we need at least PHP 4.0.0 to run the "math.php" script. with two PHP extensions
loaded.
Rather than parsing file after file of an application, you my give the root of your application path as the main directory to parse. Default is recursive parsing: that mean each directory children will be also parsed. And only files with extension
will be proceed.
Suppose we have to detect which PHP version we need to run the PEAR::File_Find package release 1.3.0
First begin to download the archive from http://pear.php.net/package/File_Find/download/1.3.0 and extract the full contents to a temporary directory (in our example its '/tmp')
We will use this very simple detection script.
<?php
require_once 'PHP/CompatInfo.php';
$source = '/tmp/File_Find-1.3.0';
$info = new PHP_CompatInfo();
$info->parseDir($source);
// you may also use unified method: $info->parseData($source);
?>
Results displayed:
array ( 'ignored_files' => array ( 0 => '/tmp/File_Find-1.3.0/package.xml', 1 => '/tmp/File_Find-1.3.0/tests/01glob.phpt', 2 => '/tmp/File_Find-1.3.0/tests/02maptree.phpt', 3 => '/tmp/File_Find-1.3.0/tests/03maptreemultiple.phpt', 4 => '/tmp/File_Find-1.3.0/tests/04search.phpt', 5 => '/tmp/File_Find-1.3.0/tests/05search_inside.phpt', 6 => '/tmp/File_Find-1.3.0/tests/06match_shell.phpt', 7 => '/tmp/File_Find-1.3.0/tests/bug2773.phpt', ), 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.3.0', 'classes' => array ( 0 => 'File_Find', ), 'extensions' => array ( 0 => 'pcre', ), 'constants' => array ( 0 => 'FALSE', 1 => 'NULL', 2 => 'PHP_OS', 3 => 'PREG_SPLIT_DELIM_CAPTURE', 4 => 'PREG_SPLIT_NO_EMPTY', 5 => 'TRUE', 6 => '__FILE__', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 4, ), '/tmp/File_Find-1.3.0/Find.php' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.3.0', 'classes' => array ( 0 => 'File_Find', ), 'extensions' => array ( 0 => 'pcre', ), 'constants' => array ( 0 => 'FALSE', 1 => 'NULL', 2 => 'PREG_SPLIT_DELIM_CAPTURE', 3 => 'PREG_SPLIT_NO_EMPTY', 4 => 'TRUE', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 4, ), ), '/tmp/File_Find-1.3.0/tests/setup.php' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.0.0', 'classes' => array ( ), 'extensions' => array ( ), 'constants' => array ( 0 => 'PHP_OS', 1 => '__FILE__', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 0, ), ), )
means that package PEAR::File_Find 1.3.0 need at least PHP 4.3.0 with extension pcre.
cond_cond
offset 0 is set to 4. That means there are conditional code (constant condition) implemented in source code (with php defined function).If you have a look on source code, you will see that all conditions referred to private package constant
FILE_FIND_DEBUG
You may avoid to read the source code to know the constant name, if you specify the
debug
option when parsing the directory.
<?php
require_once 'PHP/CompatInfo.php';
$source = '/tmp/File_Find-1.3.0';
$info = new PHP_CompatInfo();
$info->parseDir($source, array('debug' => true));
?>
And you will see in displayed results, something like :
'cond_code' => array ( 0 => 4, 1 => array ( 0 => array ( ), 1 => array ( ), 2 => array ( 0 => 'FILE_FIND_DEBUG', ), ),
cond_code
offset 1 is an array available only when debug mode
is set to true. In this array :
offset 0, give name of function conditions
offset 1, give name of extension conditions
offset 2, give name of constant conditions
If your file implement code condition that is optional and don't break main goal,
such as, for example :
if function_exists
then I do something, else I do something else.
Solution is very easy: You have to specify what function required should be considered as optional.
Suppose we have to detect which PHP version we need to run this chunk of script
named "errorHandler.php". With standard behavior, PCI returns
PHP 4.3.0 (because debug_backtrace
came with version 4.3.0).
So, if we ignore function debug_backtrace
to find out
the minimum version, we will get the real and true result.
<?php
// ...
if (function_exists('debug_backtrace')) {
$backtrace = debug_backtrace();
} else {
$backtrace = false;
}
// ...
?>
We will use another very simple detection script. Have a look on options array given as second parameter (here is the magic).
<?php
require_once 'PHP/CompatInfo.php';
$source = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'errorHandler.php';
$options = array('ignore_functions' => array('debug_backtrace'));
$info = new PHP_CompatInfo();
$info->parseFile($source, $options);
// you may also use unified method: $info->parseData($source, $options);
?>
And displayed results are :
array ( 'ignored_files' => array ( ), 'ignored_functions' => array ( 0 => 'debug_backtrace', ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.0.0', 'classes' => array ( ), 'extensions' => array ( ), 'constants' => array ( 0 => 'FALSE', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 1, ), )
that means chunk of script named "errorHandler.php" need PHP 4.0.0, have condition code (function condition : cond_code = 1), and php debug_backtrace function was excluded from scope.
Since version 1.7.0, you may catch this situation (more easily), and exclude from scope all functions that are conditionned by a function_exists. See example that follow.
Other alternative is to use ignore_functions_match option.
<?php
require_once 'PHP/CompatInfo.php';
$source = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'errorHandler.php';
$options = array('ignore_functions_match' => array('function_exists', array('/.*/')));
$info = new PHP_CompatInfo();
$info->parseFile($source, $options);
// you may also use unified method: $info->parseData($source, $options);;
?>
The string function_exists as first parameter, tell to ignore function(s) that match only conditionnal code with php function_exists().
The other possibility is string preg_match, that give more freedom, and catch function that match the pattern (without condition).
While array as second parameter, gave a list of pattern (function name) that must be catch and ignored.
Parsing a full directory, recursively or not, is no more difficult than detect PHP version of a single file.
This new example is based on auto detection of HTML_CSS 1.5.1 distribution. As we will see, basic detection is not accurate as it should be. But with an option we can get the real result (PHP minimum = 4.3.0).
First begin to download the archive from http://pear.php.net/package/HTML_CSS/download/1.5.1 and extract the full contents to a temporary directory (in our example its '/tmp')
We will focus on two important files:
CSS.php
and CSS/Error.php
.
So we will indicate to PCI to ignore
examples/
, and
tests/
directories.
Here is the detection script:
<?php
require_once 'PHP/CompatInfo.php';
$source = '/tmp/HTML_CSS-1.5.1';
$options = array('ignore_dirs' => array('examples', 'tests'));
$info = new PHP_CompatInfo();
$info->parseDir($source, $options);
// you may also use unified method: $info->parseData($source, $options);
?>
And displayed results are :
array ( 'ignored_files' => array ( 0 => '/tmp/HTML_CSS-1.5.1/ChangeLog', 1 => '/tmp/HTML_CSS-1.5.1/package.xml', 2 => '/tmp/HTML_CSS-1.5.1/tests/AllTests.php', 3 => '/tmp/HTML_CSS-1.5.1/tests/HTML_CSS_TestSuite_Bugs.php', 4 => '/tmp/HTML_CSS-1.5.1/tests/HTML_CSS_TestSuite_Standard.php', 5 => '/tmp/HTML_CSS-1.5.1/tests/stylesheet.css', 6 => '/tmp/HTML_CSS-1.5.1/examples/CSS_Advanced.php', 7 => '/tmp/HTML_CSS-1.5.1/examples/CSS_DisplayOnline.php', 8 => '/tmp/HTML_CSS-1.5.1/examples/css_errorstack_custom.php', 9 => '/tmp/HTML_CSS-1.5.1/examples/css_errorstack_logger.php', 10 => '/tmp/HTML_CSS-1.5.1/examples/css_error_custom.php', 11 => '/tmp/HTML_CSS-1.5.1/examples/css_error_ignore.php', 12 => '/tmp/HTML_CSS-1.5.1/examples/css_error_logger.php', 13 => '/tmp/HTML_CSS-1.5.1/examples/CSS_grepStyles.php', 14 => '/tmp/HTML_CSS-1.5.1/examples/CSS_InHeader.php', 15 => '/tmp/HTML_CSS-1.5.1/examples/CSS_Inline.php', 16 => '/tmp/HTML_CSS-1.5.1/examples/CSS_Logger.php', 17 => '/tmp/HTML_CSS-1.5.1/examples/CSS_parseData.php', 18 => '/tmp/HTML_CSS-1.5.1/examples/CSS_req12194_atrule_api.php', 19 => '/tmp/HTML_CSS-1.5.1/examples/CSS_req12194_atrule_parser.php', 20 => '/tmp/HTML_CSS-1.5.1/examples/CSS_Stylesheet.php', 21 => '/tmp/HTML_CSS-1.5.1/examples/CSS_validate.php', ), 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '5.0.0', 'classes' => array ( 0 => 'Services_W3C_CSSValidator', ), 'extensions' => array ( 0 => 'date', 1 => 'pcre', ), 'constants' => array ( 0 => 'E_USER_ERROR', 1 => 'E_USER_NOTICE', 2 => 'E_USER_WARNING', 3 => 'FALSE', 4 => 'NULL', 5 => 'PHP_OS', 6 => 'PREG_SET_ORDER', 7 => 'PREG_SPLIT_DELIM_CAPTURE', 8 => 'TRUE', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 1, ), '/tmp/HTML_CSS-1.5.1/CSS.php' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '5.0.0', 'classes' => array ( 0 => 'Services_W3C_CSSValidator', ), 'extensions' => array ( 0 => 'date', 1 => 'pcre', ), 'constants' => array ( 0 => 'FALSE', 1 => 'NULL', 2 => 'PHP_OS', 3 => 'PREG_SET_ORDER', 4 => 'PREG_SPLIT_DELIM_CAPTURE', 5 => 'TRUE', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 1, ), ), '/tmp/HTML_CSS-1.5.1/CSS/Error.php' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.3.0', 'classes' => array ( ), 'extensions' => array ( 0 => 'date', ), 'constants' => array ( 0 => 'E_USER_ERROR', 1 => 'E_USER_NOTICE', 2 => 'E_USER_WARNING', 3 => 'FALSE', 4 => 'NULL', 5 => 'TRUE', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 0, ), ), )
As we can read, PHP 5.0.0 is required to run the package.
Yes, but its the wrong result. HTML_CSS 1.5.1 require only PHP 4.3.0 to run.
If you have
cond_code
offset with a value different than zero, you are almost sure that the version given is wrong.
So why we get such result ?
Package PEAR::HTML_CSS 1.5.1 as many application/extension use conditional code to emulate function that are unavailable for previous PHP versions. Its means that HTML_CSS use the php function function_exists() to implement such alternative.
To illustrate our purpose, we can find in source code (CSS.php) :
<?php
// ...
if (function_exists('file_put_contents')) {
file_put_contents($filename, $this->toString());
} else {
$file = fopen($filename, 'wb');
fwrite($file, $this->toString());
fclose($file);
}
// ...
?>
PHP function file_put_contents() came with version 5.0.0; That is the reason of wrong parsing result. But we can catch such conditional code.
Let's see now how to set the good accuracy with conditional code analysis.
As briefly introduced at end of advanced detection chapter, we will learn now that there are 3 categories of conditional code that could give wrong result, if there are not catched properly.
These categories are :
function (cond_code = 1
)
A quick example:
<?php
if (function_exists('debug_backtrace')) {
$backtrace = debug_backtrace();
} else {
$backtrace = false;
}
?>
extension (cond_code = 2
)
A quick example:
<?php
if (extension_loaded('sqlite') == false) {
$prefix = (PHP_SHLIB_SUFFIX === 'dll') ? 'php_' : '';
dl($prefix . 'sqlite.' . PHP_SHLIB_SUFFIX);
echo 'SQLite version : ' . sqlite_libversion();
}
?>
constant (cond_code = 4
)
A quick example:
<?php
if (defined('DATE_RSS') {
$date_format = DATE_RSS;
} else {
$date_format = 'D, j M Y H:i:s O';
}
// will display something like "Sat, 26 Jul 2008 16:56:24 +0200"
echo date($date_format, mktime(0, 0, 0, 7, 26, 2008));
?>
At the beginning of first version that catch conditional code, there were only
3 options: ignore_functions
, ignore_extensions
and ignore_constants
.
Incovenient with these options, is that you should know the source code to parse, and identify whose functions, extensions or constants to avoid.
Version 1.7.0 of API has introduced the ability to add name patterns to identify
all or part of functions, extensions, constants to ignore from parsing.
You should use now these options: ignore_functions_match
,
ignore_extensions_match
or ignore_constants_match
.
Let's take a look with an example, how it's easy to catch whatever you want to exclude from parsing. We will take again example of PEAR::HTML_CSS 1.5.1 package already seen in advanced directory detection .
<?php
require_once 'PHP/CompatInfo.php';
$datasource = '/tmp/HTML_CSS-1.5.1';
$options = array(
'ignore_dirs' => array('examples', 'tests'),
'ignore_functions_match' => array('function_exists', array('/.*/')),
'ignore_extensions_match' => array('extension_loaded', array('/.*/')),
'ignore_constants_match' => array('defined', array('/.*/')),
);
$pci = new PHP_CompatInfo();
$pci->parseData($datasource, $options);
?>
Here we catch all standard conditional code (function_exists
,
extension_loaded
, defined
) what match
all names (regular expression given by array('/.*/')
).
To catch what ever function you want, use
preg_match
rather thanfunction_exists
.It's also true for
extension_loaded
anddefined
With preg_match you are really free to ignore a single function, a group set or all functions, only by giving the good name pattern.
Example to ignore all functions prefixed by xdebug_ :
<?php
require_once 'PHP/CompatInfo.php';
$datasource = '/tmp/HTML_CSS-1.5.1';
$options = array(
'ignore_functions_match' => array('preg_match', array('/^xdebug_/')),
);
$pci = new PHP_CompatInfo();
$pci->parseData($datasource, $options);
?>
If you use the command-line parser with pci script, the solution is a bit different.
To catch cond_code = 1 (function), you must run the command
pci
-inm functions-match.txt
where functions-match.txt
is a text file, that identify
on each line a new condition.
Each blank line or beginning with ;
will be skipped
(proceed as comment line like in php.ini)
If first non blank character is an equal sign (=), then you can catch what ever function you want with a preg_match condition (see xdebug example in previous section (web interface)
Example of text file contents
;=^xdebug_ ;=alias$ .* ;file_put_contents
Do not confuse a regular expression beginning with equal sign (=), and the same line without =.
In first case you will catch all functions that match the name pattern given found in all source code, while second case try to catch only matches found with
if function_exists('')
condition.It's also true for extensions and constants, we will see them now.
To catch cond_code = 2 (extension), you must run the command
pci
-iem extensions-match.txt
where extensions-match.txt
is a text file, that identify
on each line a new condition.
Each blank line or beginning with ;
will be skipped
(proceed as comment line like in php.ini)
If first non blank character is an equal sign (=), then you can catch what ever extension you want with a preg_match condition.
Example of text file contents
;=xdebug ;sqlite =gd ;=sapi_apache
To catch cond_code = 4 (constant), you must run the command
pci
-icm constants-match.txt
where constants-match.txt
is a text file, that identify
on each line a new condition.
Each blank line or beginning with ;
will be skipped
(proceed as comment line like in php.ini)
If first non blank character is an equal sign (=), then you can catch what ever constant you want with a preg_match condition.
Example of text file contents
=PHP_EOL =DATE_RSS ;FILE_FIND_VERSION
The Command-Line Parser can be invoked through the pci command.
Let's take a look at the command-line parser switches:
Usage: pci [options] -d --dir (optional)value Parse DIR to get its compatibility info () -f --file (optional)value Parse FILE to get its compatibility info () -s --string (optional)value Parse STRING to get its compatibility info () -v --verbose (optional)value Set the verbose level (1) -n --no-recurse Do not recursively parse files when using --dir -if --ignore-files (optional)value Data file name which contains a list of file to ignore (files.txt) -id --ignore-dirs (optional)value Data file name which contains a list of directory to ignore (dirs.txt) -in --ignore-functions (optional)value Data file name which contains a list of php function to ignore (functions.txt) -ic --ignore-constants (optional)value Data file name which contains a list of php constant to ignore (constants.txt) -ie --ignore-extensions (optional)value Data file name which contains a list of php extension to ignore (extensions.txt) -iv --ignore-versions values(optional) PHP versions - functions to exclude when parsing source code (5.0.0) -inm --ignore-functions-match (optional)value Data file name which contains a list of php function pattern to ignore (functions-match.txt) -iem --ignore-extensions-match (optional)value Data file name which contains a list of php extension pattern to ignore (extensions-match.txt) -icm --ignore-constants-match (optional)value Data file name which contains a list of php constant pattern to ignore (constants-match.txt) -fe --file-ext (optional)value A comma separated list of file extensions to parse (only valid if parsing a directory) (php, php4, inc, phtml) -r --report (optional)value Print either "xml" or "csv" report (text) -o --output-level (optional)value Print Path/File + Version with additional data (31) -t --tab (optional)value Columns width (29,12,20) -p --progress (optional)value Show a wait message [text] or a progress bar [bar] (bar) -S --summarize Print only summary when parsing directory -V --version Print version information -h --help Show this help
-d
| --dir
pci -d directory
Runs the parser with all default options, and try to analyze content of files into directory identified by switch -d or --dir
-f
| --file
pci -f file
Runs the parser with all default options, and try to analyze content of a single file identified by switch -f or --file
-s
| --string
pci -s string
Runs the parser with all default options, and try to analyze content of a chunk of code (string) designed by switch -s or --string
-v
| --verbose
pci -v number -d directory
Runs the parser with all default options, and try to analyze content of files into directory identified by switch -d or --dir with the level of detail given by number and switch -v or --verbose
Verbose level goes from 0 (no extra information) to 7 (full extra details).
Level 0 give only parsing results of data source (directory, file, string).
For example: pci -v 0 -d /tmp/Services_W3C_CSSValidator-0.1.0 give
+-----------------------------+---------+---+------------+--------------------+ | Files | Version | C | Extensions | Constants/Tokens | +-----------------------------+---------+---+------------+--------------------+ | ...W3C_CSSValidator-0.1.0/* | 5.1.0 | 4 | dom | ...CTORY_SEPARATOR | | | | | | E_ALL | | | | | | FALSE | | | | | | NULL | | | | | | TRUE | | | | | | __FILE__ | | | | | | instanceof | | | | | | protected | | | | | | public | +-----------------------------+---------+---+------------+--------------------+ | ...r-0.1.0/CSSValidator.php | 5.1.0 | 0 | dom | FALSE | | | | | | NULL | | | | | | TRUE | | | | | | protected | | | | | | public | +-----------------------------+---------+---+------------+--------------------+ | ...0.1.0/tests/AllTests.php | 5.0.0 | 4 | | __FILE__ | | | | | | public | +-----------------------------+---------+---+------------+--------------------+ | ...W3C_CSSValidatorTest.php | 5.0.0 | 4 | | ...CTORY_SEPARATOR | | | | | | __FILE__ | | | | | | protected | | | | | | public | +-----------------------------+---------+---+------------+--------------------+ | ...les/validate_atrules.php | 4.0.0 | 0 | | E_ALL | | | | | | TRUE | +-----------------------------+---------+---+------------+--------------------+ | ...ples/validate_byfile.php | 4.0.0 | 0 | | E_ALL | | | | | | TRUE | +-----------------------------+---------+---+------------+--------------------+ | ...mples/validate_byuri.php | 4.0.0 | 0 | | E_ALL | | | | | | TRUE | +-----------------------------+---------+---+------------+--------------------+ | ...es/validate_fragment.php | 4.0.0 | 0 | | E_ALL | | | | | | TRUE | +-----------------------------+---------+---+------------+--------------------+ | ...0/CSSValidator/Error.php | 5.0.0 | 0 | | public | +-----------------------------+---------+---+------------+--------------------+ | ...CSSValidator/Message.php | 5.0.0 | 0 | | NULL | | | | | | public | +-----------------------------+---------+---+------------+--------------------+ | ...SSValidator/Response.php | 5.0.0 | 0 | | instanceof | | | | | | public | +-----------------------------+---------+---+------------+--------------------+ | ...CSSValidator/Warning.php | 4.0.0 | 0 | | | +-----------------------------+---------+---+------------+--------------------+
Level 1 give same details as level 0, plus command line resume.
For example: pci -v 1 -d /tmp/Services_W3C_CSSValidator-0.1.0 give
Command Line resume : +-------------------------+---------------------------------------------------+ | Option | Value | +-------------------------+---------------------------------------------------+ | summarize | FALSE | | output-level | 31 | | verbose | 1 | | dir | /tmp/Services_W3C_CSSValidator-0.1.0 | +-------------------------+---------------------------------------------------+
Level 2 give same details as level 0, plus parser options used.
For example: pci -v 2 -d /tmp/Services_W3C_CSSValidator-0.1.0 give
Parser options : +-------------------------+---------------------------------------------------+ | Option | Value | +-------------------------+---------------------------------------------------+ | file_ext | php | | | php4 | | | inc | | | phtml | | recurse_dir | TRUE | | debug | FALSE | | is_string | FALSE | | ignore_files | | | ignore_dirs | | +-------------------------+---------------------------------------------------+
Level 3 is equivalent to level 2 + level 1 + level 0.
Level 4 give same details as level 0, plus list of php functions used with their version and source (extension PECL or standard).
For example: pci -v 4 -d /tmp/Services_W3C_CSSValidator-0.1.0 give
Debug: +---------+-----------------+-----------+------+ | Version | Function | Extension | PECL | +---------+-----------------+-----------+------+ | 4.0.0 | in_array | | no | | 4.0.0 | file_exists | | no | | 4.0.0 | is_bool | | no | | 4.0.0 | intval | | no | | 4.0.0 | defined | | no | | 4.0.0 | define | | no | | 4.0.0 | chdir | | no | | 4.0.0 | dirname | | no | | 4.0.0 | realpath | | no | | 4.0.0 | error_reporting | | no | | 4.0.0 | ini_set | | no | | 4.0.0 | var_dump | | no | | 4.0.0 | get_object_vars | | no | | 5.1.0 | property_exists | | no | +---------+-----------------+-----------+------+
Level 5 is equivalent to level 4 + level 1 + level 0.
Level 6 is equivalent to level 4 + level 2.
Level 7 is equivalent to level 4 + level 2 + level 1.
-n
| --no-recurse
pci -n -d directory
Runs the parser and analyze only files in directory identified by -d or --dir. Default behavior will parse all directory childs recursively.
-if
| --ignore-files
Identify the parameter text file that contains on each line the name of each file to ignore when parsing a directory/branch.
Default value used files.txt
file in the same directory
as pci script.
-id
| --ignore-dirs
Identify the parameter text file that contains on each line the name of each sub-directory to ignore when parsing a directory/branch.
Default value used dirs.txt
file in the same directory
as pci script.
-in
| --ignore-functions
Identify the parameter text file that contains on each line the name of each PHP function to ignore when parsing the data source.
Default value used functions.txt
file in the same directory
as pci script.
-ic
| --ignore-constants
Identify the parameter text file that contains on each line the name of each PHP constant to ignore when parsing the data source
Default value used constants.txt
file in the same directory
as pci script.
-ie
| --ignore-extensions
Identify the parameter text file that contains on each line the name of each PHP extension to ignore (all extension.functions) when parsing the data source
-iv
| --ignore-versions
Expect one or two values that identify which PHP version (and all its related functions) to ignore.
For example: ignore all PHP 5 functions (minor releases 0 thru 2), or only PHP 5.0.0 functions. pci -f file -iv 5.0.0 5.2.0 pci -d directory -iv 5.0.0
-inm
| --ignore-functions-match
Identify the parameter text file that contains on each line a pattern (match a regular expression) of PHP function to ignore when parsing the data source.
Default value used functions-match.txt
file in the same directory
as pci script.
Comments start with ";", as in php.ini, and blank lines are allowed.
If you want to use the preg_match compare function, put a "=", to start the line, follow by a regular expression.
-iem
| --ignore-extensions-match
Identify the parameter text file that contains on each line a pattern (match a regular expression) of PHP extension to ignore when parsing the data source.
Default value used extensions-match.txt
file in the same directory
as pci script.
Comments start with ";", as in php.ini, and blank lines are allowed.
If you want to use the preg_match compare function, put a "=", to start the line, follow by a regular expression.
-icm
| --ignore-constants-match
Identify the parameter text file that contains on each line a pattern (match a regular expression) of PHP constant to ignore when parsing a directory, a single file, or a string.
Default value used constants-match.txt
file in the same directory
as pci script.
Comments start with ";", as in php.ini, and blank lines are allowed.
If you want to use the preg_match compare function, put a "=", to start the line, follow by a regular expression.
-fe
| --file-ext
Follow by a comma separated list of file extensions to parse (only valid if parsing a directory). Default is: php,php4,inc,phtml
-r
| --report
Print either a text report (default), or any others render available (csv, xml, ...)
For example: pci -r xml -f /tmp/PHP_CodeSniffer-1.1.0/CodeSniffer.php give these results (when package XML_Beautifier is available)
<?xml version="1.0" encoding="UTF-8"?>
<pci version="1.9.0b2">
<file name="/tmp/PHP_CodeSniffer-1.1.0/CodeSniffer.php">
<version>5.1.2</version>
<conditions level="0" />
<extensions count="5">
<extension>date</extension>
<extension>pcre</extension>
<extension>SPL</extension>
<extension>tokenizer</extension>
<extension>xml</extension>
</extensions>
<constants count="7">
<constant>DIRECTORY_SEPARATOR</constant>
<constant>FALSE</constant>
<constant>NULL</constant>
<constant>PHP_EOL</constant>
<constant>TRUE</constant>
<constant>T_STRING</constant>
<constant>__FILE__</constant>
</constants>
<tokens count="5">
<token>catch</token>
<token>protected</token>
<token>public</token>
<token>throw</token>
<token>try</token>
</tokens>
<ignored>
<files count="0" />
<functions count="0" />
<extensions count="0" />
<constants count="0" />
</ignored>
</pci>
And with little debug option ( verbose level 4 ) pci -r xml -v 4 -f /tmp/PHP_CodeSniffer-1.1.0/CodeSniffer.php results became
<?xml version="1.0" encoding="UTF-8"?>
<pci version="1.9.0b2">
<file>/tmp/PHP_CodeSniffer-1.1.0/CodeSniffer.php</file>
<version>5.1.2</version>
<conditions count="0" level="0" />
<extensions count="5">
<extension>date</extension>
<extension>pcre</extension>
<extension>SPL</extension>
<extension>tokenizer</extension>
<extension>xml</extension>
</extensions>
<constants count="7">
<constant>DIRECTORY_SEPARATOR</constant>
<constant>FALSE</constant>
<constant>NULL</constant>
<constant>PHP_EOL</constant>
<constant>TRUE</constant>
<constant>T_STRING</constant>
<constant>__FILE__</constant>
</constants>
<tokens count="5">
<token>catch</token>
<token>protected</token>
<token>public</token>
<token>throw</token>
<token>try</token>
</tokens>
<ignored>
<files count="0" />
<functions count="0" />
<extensions count="0" />
<constants count="0" />
</ignored>
<functions count="41">
<function version="4.0.0">class_exists</function>
<function version="4.0.0">define</function>
<function version="4.0.0">chdir</function>
<function version="4.0.0">dirname</function>
<function version="4.0.0">substr</function>
<function version="4.0.0">str_replace</function>
<function version="4.0.0">is_file</function>
<function version="4.0.0">is_array</function>
<function version="4.0.0">is_string</function>
<function version="4.0.0">count</function>
<function version="4.0.0">is_dir</function>
<function version="4.0.0">basename</function>
<function version="4.0.0">realpath</function>
<function version="4.0.0">strtolower</function>
<function version="4.0.0">strrpos</function>
<function version="4.0.0">in_array</function>
<function version="4.0.0">explode</function>
<function version="4.0.0">array_pop</function>
<function version="4.0.0">array_merge</function>
<function version="4.0.0">file_exists</function>
<function version="4.0.0">strtr</function>
<function extension="pcre" pecl="false" version="4.0.0">preg_match</function>
<function extension="date" pecl="false" version="4.0.0">time</function>
<function version="4.0.0">ksort</function>
<function version="4.0.0">htmlspecialchars</function>
<function extension="xml" pecl="false" version="4.0.0">utf8_encode</function>
<function version="4.0.0">strlen</function>
<function version="4.0.0">str_repeat</function>
<function version="4.0.0">ord</function>
<function version="4.0.0">strtoupper</function>
<function version="4.0.0">strpos</function>
<function version="4.0.0">rtrim</function>
<function version="4.0.0">is_writable</function>
<function version="4.0.2">wordwrap</function>
<function version="4.0.4">is_null</function>
<function version="4.0.4">constant</function>
<function extension="tokenizer" pecl="false" version="4.2.0">token_name</function>
<function version="4.2.0">var_export</function>
<function version="5.0.0">file_put_contents</function>
<function version="5.0.2">interface_exists</function>
<function extension="SPL" pecl="false" version="5.1.2">spl_autoload_register</function>
</functions>
</pci>
-o
| --output-level
Allow to filter data type (column) you want on standard output (console). From all details (31=default) to only file name (=0)
0: file name
1: conditional code
2: extensions
4: constants
8: tokens
16: version
output_level is binary value. So to have, for example, only file name (always mandatory) with version and extensions, you have to give value 18 (16 + 2).
-t
| --tab
Sets the Files, Etensions and Constants/Tokens columns width. Default values are: 29 char. for Files colum, 12 char. for Extentions column, and 20 char. for Constants/Tokens column.
Here are the best values optimized by output-level for a 80 columns screen width, to almost always see extensions and constants/tokens without name truncated: first value is always for Files column (f), second value is always for Extensions column (e), and third value is always for Constants/Tokens column (c)
If a (f,e,c) value is missing then the corresponding default value (f=29,e=12,c=20) is used.
For example: 56,,20 is equivalent to 56,12,20
output-level 0: 77
output-level 1: 73
output-level 2: 59,17
output-level 3: 55,17
output-level 4, 8, 12: 55,,21
output-level 5, 9, 13: 51,,21
output-level 6, 10, 14: 37,17,21
output-level 7, 11, 15: 33,17,21
output-level 16: 67
output-level 17: 63
output-level 18: 49,17
output-level 19: 45,17
output-level 20, 24: 45,,21
output-level 21, 25, 29: 41,,21
output-level 22, 26, 30: 27,17,21
output-level 23, 27, 31: 23,17,21
output-level 24, 28: 45,17,21
-p
| --progress
[ bar | text ]
Show a progress bar
(if PEAR::Console_ProgressBar is installed)
or a simple text
wait message when parsing a directory.
If you specify --progress bar and PEAR::Console_ProgressBar is not available, then default display will be text wait message (without giving an error), as if you have specified --progress text instead
-S
| --summarize
Print only the summary of parsing result for a directory, rather than full details file by file (default).
-V
| --version
Print only the current version information
-h
| --help
Show help panel (as described in beginning of this section)
Let's take a look now at the result displayed : the main table
+-----------------------------+---------+---+------------+--------------------+ | Files | Version | C | Extensions | Constants/Tokens | +-----------------------------+---------+---+------------+--------------------+ +-----------------------------+---------+---+------------+--------------------+
We have, by default (output-level = 31) 5 columns
For Files, Extensions, Constants/Tokens columns, if content is larger than cell width, then the content if truncated by left, and replaced by
...
Files Identify each file of data source parsed.
Version Give the minimum version to run the file. And if there is a second number, give the max version to run source file.
C Show the level of conditional code found in your source file(s). From 0=none, 1=function_exists, 2=extension_loaded, 4=defined and more (its binary value). This C column is the same information given by cond_code in final hash result of Array Renderer.
Extensions List the PHP standard or PECL extensions required to run the source file.
Constants/Tokens List the PHP standard or user defined constants and PHP5+ tokens (such as: private, public, final, ...)
When verbose mode is set to level 1, you have an additional table displayed, that resume the command line arguments
Command Line resume : +-------------------------+---------------------------------------------------+ | Option | Value | +-------------------------+---------------------------------------------------+ +-------------------------+---------------------------------------------------+
When verbose mode is set to level 2, you have an additional table displayed, that resume the parser options used
Parser options : +-------------------------+---------------------------------------------------+ | Option | Value | +-------------------------+---------------------------------------------------+ +-------------------------+---------------------------------------------------+
When verbose mode is set to level 4, you have an additional table displayed, that show each PHP standard or PECL functions with their version
Debug: +---------+-----------------+-----------+------+ | Version | Function | Extension | PECL | +---------+-----------------+-----------+------+ +---------+-----------------+-----------+------+
PHP_CompatInfo can use different kind of renderers and provides a default one (Array) which print a complete parsable string representation of results.
There are 6 renderers available with PHP_CompatInfo 1.8.0
Array, PHP_CompatInfo_Renderer_Array. It is similar to PHP var_export May be beautify if PEAR::Var_Dump is installed on your system.
Csv, PHP_CompatInfo_Renderer_Csv. Print a comma separate values of results.
Html, PHP_CompatInfo_Renderer_Html. Print a pretty nice table with a customizable style sheet (inline or to a separate file)
Null, PHP_CompatInfo_Renderer_Null. The solution to suppress display on standard output.
Text, PHP_CompatInfo_Renderer_Text. This renderer is usefull only with the command-line parser mode (CLI).
Xml, PHP_CompatInfo_Renderer_Xml. Print an XML representation of results. May be beautify if PEAR::XML_Beautifier is installed on your system.
If none of them match your need, you can also write your own renderers.
The main steps of using any available renderer are quite similar:
<?php
require_once 'PHP/CompatInfo.php';
// data source to parse: file, directory, string, ...
$datasource = '/tmp/HTML_CSS-1.5.1';
// parser options depending of data source
$options = array();
// kind of renderer: array, csv, html, null, text, xml, ...
$driverType = 'array'; // case insensitive
// specific renderer hash options
$driverOptions = array();
$pci = new PHP_CompatInfo($driverType, $driverOptions);
$r = $pci->parseData($datasource, $options);
?>
To keep compatibility with previous versions, you can find the result similar to var_export (Array renderer) in $r (in our example). All parse functions (parseDir, parseFile, parseArray, parseString) have the same behavior.
PHP_CompatInfo class constructor expect to receive two parameters: first one identify the kind of renderer (array, csv, html, null, text, xml), while second is private and specific to each renderer. Allows to customize a bit the renderer without to rewrite a new one (explore all options before beginning to write your own version).
Before to have a look on each driver specific options, take a short pause on common options of all renderers.
silent mode : When parsing more than one source file, PHP_CompatInfo can display status of long process. With simple wait text messages, or a progress bar if PEAR::Console_ProgressBar is available (installed on your system).
The default mode silent On, is turn off when you specify a progress bar or a progress text with --progress or -p switches on CLI with the pci script.
If you run the web interface, you have to turn it on manually. For example:
<?php
require_once 'PHP/CompatInfo.php';
// data source to parse: file, directory, string, ...
$datasource = '/tmp/HTML_CSS-1.5.1';
// parser options depending of data source
$options = array();
// kind of renderer: array, csv, html, null, text, xml, ...
$driverType = 'array'; // case insensitive
// Turn Off the silent mode, and display simple wait text messages
$driverOptions = array('silent' => false, 'progress' => 'text');
$pci = new PHP_CompatInfo($driverType, $driverOptions);
$r = $pci->parseData($datasource, $options);
?>
See also the docs/examples/parseDir_withCustomProgressBar.php
example in the package distribution, to customize a progress bar
for the CLI and pci script usage.
output-level allow to choose what information (condition code level, extensions, constants, tokens, version) will be shown or hidden.
Default value is 31: that means we show all details. See the Command-Line Parser section, and -o | --output-level switch to learn more about values and corresponding behaviors.
summarize mode, is usefull when you parse more than one file, and don't want to see intermediate result of each file, but only the final one.
Default is turn off.
verbose level, give details deep you want to have: from 0 (no extra information) to 7 (full details).
Default level is 0. See the Command-Line Parser section, and -v | --verbose switch to learn more about values and corresponding behaviors.
All renderers can be run in both interfaces (web and cli), this is why we can find a hash named args in the driver array options (second parameter of PHP_CompatInfo class constructor). This hash replace corresponding arguments you should have (otherwise) to specify on the Command-Line.
Array renderer used two drivers to show dump of results.
Default is PHP
(var_export function). The other one is
PEAR::Var_Dump
. You can use this last one only if package
is installed on your system.
While there are no specific options for PHP driver, you can use all options
of PEAR::Var_Dump to beautify the dump of results.
See example docs/examples/pci180_parsefile.php
in the package distribution.
First key of hash (options
) allow to choose the Var_Dump renderer
(display_mode) you want.
Only display_mode = Text is allowed if you run PHP_CompatInfo on CLI.
Second key of hash (rendererOptions
) allow to give
specific options to the Var_Dump renderer identified by display_mode directive.
Var_Dump package exists for two major versions of PHP.
Var_Dump PHP 4 on default PEAR channel (branch 1.0)
Var_Dump PHP 5 on PEAR channel Toolbox (branch 1.2)
Csv renderer can be customized only in one way: change delimiters characters
fields-values-separated-by identify the delimiter of values when there is a list given (extensions, constants, tokens). Default is ,
fields-terminated-by identify the delimiter of fields (those given by the output-level directive). Default is ;
fields-enclosed-by identify the protect field character if content have one or more restrictive character as given by previous options. Default is "
lines-terminated-by identify the end-of-line character. Usefull if you want to generate a file for another platform than those who have produced the results. Default is PHP_EOL constant
Html renderer have only one specific option :
tdwidth that give size of each column (in em css unit), even if there are not shown (see output-level value). Default is 18,4,2,7,13
That means: File is 18 em width, Version is 4 em width, C (cond_code) is 2 em width, Extensions is 7 em width, and Constants/Tokens is 13 em width.
See also the docs/examples/pci180_parsedir_tohtml.php
example in the package distribution, howto define your own html renderer.
Text renderer have only one specific option :
colwidth that give size of file (f), extensions (e), and constants/tokens (c) column (in character unit), even if there are not shown (see output-level value). Default is f = 29, e = 12, c = 20)
See the Command-Line Parser section, and -t | --tab switch to learn more about optimized values depending of output-level
Xml renderer have only two specific options :
use-beautifier tell if we use the PEAR::XML_Beautifier package to beautify XML output. Default is auto (use only if available)
beautifier is a hash for the PEAR::XML_Beautifier package options. Default is empty array (use all XML_Beautifier package default options).
Use
no
value if you have PEAR::XML_Beautifier installed, and you don't want to have XML beautified.
See also the docs/examples/pci180_parsedata_toxml.php
,
and docs/examples/pci180_parsestring_toxml.php
examples in the package distribution.
Since version 1.0.1 and until version 1.9.0, PHP_CompatInfo (aka PCI) used only one monolithic file for all functions, and another one for all constants. These 2 file support both standard (PHP core) and all extensions known (or supposed to be). This system was the first fruits to a dictionary system with the limitation we know now.
Bug report 15011 help me to think again on a new way to improve the dictionary system without any limitation.
As I can't provide all extensions support (already known or future), the new architecture named Extension Support List (aka ESL) will have by default a group of dictionary that know elements of 25 common extensions. But I will let ability to end-users to build their own ESL corresponding to their platform.
Concept was born.
PCI provide an automated system named pciconf (PHP5 CLI script) to build all dictionaries required. Of course, as I didn't want to let down PHP4 users, I will propose two different ways to update your ESL: a first for PHP4 (alternative), and a second for PHP5 (recommanded). But before to explain how to do it, have a look on dictionary structure.
There are 3 main data dictionary knew only by the PCI core of version 1.9.0+. Each one identify a list (alphabetical sorted order) of basic extension component.
Function dictionary
CompatInfo/func_array.php
provides
a global array named $GLOBALS['_PHP_COMPATINFO_FUNCS']
what contains list of all functions implemented by all extensions.
Constant dictionary
CompatInfo/const_array.php
provides
a global array named $GLOBALS['_PHP_COMPATINFO_CONST']
what contains list of all constants implemented by all extensions.
Class dictionary
CompatInfo/class_array.php
provides
a global array named $GLOBALS['_PHP_COMPATINFO_CLASS']
what contains list of all classes implemented by all extensions.
Each extension should have (normally) at least a function dictionary (required), but may also have a class and/or constant dictionary (optional) depending of informations it provided. For example: Take the Libxml that contains 3 types of data dictionary.
Function dictionary
CompatInfo/libxml_func_array.php
provides
a global array named $GLOBALS['_PHP_COMPATINFO_FUNC_LIBXML']
what contains list of all functions implemented by Libxml extension.
Constant dictionary
CompatInfo/libxml_const_array.php
provides
a global array named $GLOBALS['_PHP_COMPATINFO_CONST_LIBXML']
what contains list of all constants implemented by Libxml extension.
Class dictionary
CompatInfo/libxml_class_array.php
provides
a global array named $GLOBALS['_PHP_COMPATINFO_CLASS_LIBXML']
what contains list of all classes implemented by Libxml extension.
All extensions data dictionary is a key-values pair array, with name of function, constant or class, as the key, and specific data as the values.
Specific data is also an array with key-value pairs. Constant and Class dictionary required only 2 keys: init, and name, while Function dictionary required at least 3 keys: init, ext and pecl.
init
the first PHP version which extension element (function, constant, class) came from
end
the last PHP version which extension element (function, constant, class) is available
name
name of extension element (function, constant, class)
ext
name of extension (case sensitive)
ext
tell if extension came from PECL repository or not
This procedure is also named alternative solution, because it's manual. You make changes at your own risks.
If you need only extension included in the default distribution, the solution is easy. Edit by hand the global dictionaries, add resources required and add extension dict to the list as follow : Suppose we need support of XSL extension.
In CompatInfo
directory where PCI
was installed (standard is @php_dir@/PHP, where @php_dir@ identify the
PEAR directory), edit:
class_array.php
add the resource
PHP/CompatInfo/xsl_class_array.php
and merge the global
array $GLOBALS['_PHP_COMPATINFO_CLASS_XSL']
to main
class dictionary named $GLOBALS['_PHP_COMPATINFO_CLASS']
const_array.php
add the resource
PHP/CompatInfo/xsl_const_array.php
and merge the global
array $GLOBALS['_PHP_COMPATINFO_CONST_XSL']
to main
constant dictionary named $GLOBALS['_PHP_COMPATINFO_CONST']
func_array.php
nothing to do because XSL extension
does not provided any function.
That's all. Your PCI system is ready to detect all data sources that used the XSL extension.
This procedure is also named recommanded solution, because there are no manual changes. But for technical reasons, it's only available for PHP5+ users.
PCI provide an automated system named pciconf. This CLI script allow to:
generate by default only all dictionaries corresponding to your platform.
Default behavior is to overwrite default system PCI installation (results into
@php_dir@/PHP/CompatInfo
, where @php_dir@ identify the PEAR directory).
If you don't want to overwrite default system PCI librairies, use the --output switch to write result in a directory accessible of your include_path
Example1: pciconf --output @php_dir@/../includes/PHP/CompatInfo
Example2: pciconf --output /etc/includes/PHP/CompatInfo
generate only a list of extension
Use the --enable switch with a comma separated list of extension name (case sensitive)
Example: pciconf --enable standard,date,gd,xsl
Do not forget the standard extension, otherwise the result will be fake
generate all extensions of your platform without some of them
Use the --disable switch with a comma separated list of extension name (case sensitive)
Example: pciconf --disable xdebug
That's all. Your PCI system is ready to detect all data sources that used extension(s) you have choosen.
You cannot build dictionary for an extension that is not installed on your platform. Unless you used the PHP4 alternative solution.
Since version 1.9.0, PHP_CompatInfo (aka PCI) used a full data dictionary system based on source files (set of versions.xml) used by the PHP Manual generation itself.
Of course, if extensions files reference (of phpdoc) changed, you have to wait a new release of PCI to see bugs/changes fixed.
If you can't wait a new PCI release, or because reference (phpdoc) files are fake and not yet fixed, there is an alternative solution named Exception Rules System (aka ERS).
This procedure is not secure, because you can lose your changes if you install a new PCI release that did not included your fixes.
End-users have to fix the wrong values in data dictionaries themself (by hand).
This procedure is pretty secure, and minimize risk to lose informations.
Version 1.9.0 of PCI used itself the ERS to fix data still missing in phpdoc reference.
Reason: Migration from monolithic
http://cvs.php.net/viewvc.cgi/phpdoc/phpbook/phpbook-xsl/version.xml
to specific extension filehttp://cvs.php.net/viewvc.cgi/phpdoc/en/reference/*/version.xml
is not yet over !
pciconf CLI script build data dictionary from source below :
the phpdoc reference mirror files installed in PCI data directory
@data_dir@/PHP_CompatInfo/data/phpdocref/
,
where @data_dir@ identify your PEAR data directory.
a list of Exception Rules installed in
@php_dir@/scripts/pciconf/
,
where @php_dir@ identify your PEAR directory.
Default Exception Rules are provided by a set of file s named
*_func_exception.php
, *_const_exception.php
,
*_class_exception.php
, where * means for the extension name
(case sensitive).
Exception Rules files have the same structure as data dictionary. Only the array php variable name is different.
$function_exceptions
for the related extension functions list
$constant_exceptions
for the related extension constants list
$class_exceptions
for the related extension classes list
pciconf detect presence of Exception Rules by the
exceptions.conf.php
file.
This file implement a required function
mixed getExceptions (
string $extension
, string $type
)
$extension
identify the extension name,
while second argument $type
identify the kind of exception
(version, class, function, constant) to proceed.
If function return FALSE, that means there are no value to apply/merge with the phpdoc base reference. Otherwise, function should return a compatible data dictionary structure (array) what contains all news/changes to apply.
To use your own ERS, you have to tell it to the pciconf script with the --exception switch.
Example: pciconf --exception /home/users/farell/myERS.php
In summary:
Build your own ERS to be free to fix all errors you found without to wait a PHP Manual/PCI new version.
Use a directory acessible in your include_path to store result of data dictionaries corresponding to your platform, and avoid to overwrite the default PCI installation.
array PHP_CompatInfo::loadVersion (
string $min
, string|boolean $max
= false
, boolean $include_const
= false
, boolean $groupby_vers
= false
)
Load components list for a PHP version or subset
$min
PHP minimal version
$max
(optional) PHP maximal version
$include_const
(optional) include constants list in final result
$groupby_vers
(optional) give initial php version of function or constant
returns An array of php function available in version(s) given
returns A mixed array of php function + php constants available in version(s) given. Available with parameter #3 ($include_const) since API 1.6.0
throws no exceptions thrown
since version 1.2.0 (2006-08-23)
This function can not be called statically.
What's new with PHP version 4.3.10 ?
<?php
require_once 'PHP/CompatInfo.php';
$pci = new PHP_CompatInfo();
$res = $pci->loadVersion('4.3.10', '4.3.10', true);
var_export($res);
?>
Result give: 0 function and 2 constants
array ( 'functions' => array ( ), 'constants' => array ( 0 => 'PHP_EOL', 1 => 'UPLOAD_ERR_NO_TMP_DIR', ), )
What's new since PHP version 5.2.1 ?
<?php
require_once 'PHP/CompatInfo.php';
$pci = new PHP_CompatInfo();
$res = $pci->loadVersion('5.2.1');
var_export($res);
?>
Result give only 4 functions (with default Extension Support List provided by version 1.9.0 of PHP_CompatInfo). Depending of your Extension Support List, this result can be different.
array ( 0 => 'php_ini_loaded_file', 1 => 'stream_is_local', 2 => 'stream_socket_shutdown', 3 => 'sys_get_temp_dir', )
void
PHP_CompatInfo::addListener
(
mixed
$callback
,
string
$nName
= EVENT_DISPATCHER_GLOBAL
)
Registers a new listener with the given criteria
$callback
A PHP callback
$nName
(optional) Expected notification name
returns void
throws no exceptions thrown
since version 1.8.0b3 (2008-06-07)
This function can not be called statically.
Attach a simple function that will listen all PHP_CompatInfo events, which write start and end audit results in a flat file defined by a php constant.
<?php
require_once 'PHP/CompatInfo.php';
define('DEST_PCI_LOG', '/var/log/pciEvents.log');
function debugNotify(&$auditEvent)
{
$notifyName = $auditEvent->getNotificationName();
$notifyInfo = $auditEvent->getNotificationInfo();
if ($notifyName == PHP_COMPATINFO_EVENT_AUDITSTARTED) {
error_log($notifyName.':'. PHP_EOL .
var_export($notifyInfo, true) . PHP_EOL,
3, DEST_PCI_LOG);
} elseif ($notifyName == PHP_COMPATINFO_EVENT_AUDITFINISHED) {
error_log($notifyName.':'. PHP_EOL .
var_export($notifyInfo, true) . PHP_EOL,
3, DEST_PCI_LOG);
}
}
$cbObserver = 'debugNotify';
$pci = new PHP_CompatInfo();
$pci->addListener($cbObserver);
// ...
$source = ''; // <- identify the date source to parse
$options = array(); // <- some parser options
$pci->parseData($source, $options);
$pci->removeListener($cbObserver);
?>
bool
PHP_CompatInfo::removeListener
(
mixed
$callback
,
string
$nName
= EVENT_DISPATCHER_GLOBAL
)
Removes a registered listener that correspond to the given criteria.
$callback
A PHP callback
$nName
(optional) Expected notification name
returns True if listener was removed, false otherwise.
throws no exceptions thrown
since version 1.8.0b3 (2008-06-07)
This function can not be called statically.
see PHP_CompatInfo::addListener() example
array|false PHP_CompatInfo::parseArray (
array $files
, array $options
= array()
)
You can parse an array of Files or Strings. To parse strings, $options['is_string'] must be set to true
$files
Array of file names or code strings
$options
An array of options where:
file_ext Contains an array of file extensions to parse for PHP code. Default: php, php4, inc, phtml
debug Contains a boolean to control whether extra ouput is shown.
ignore_functions Contains an array of functions to ignore when calculating the version needed.
ignore_constants Contains an array of constants to ignore when calculating the version needed.
ignore_files Contains an array of files to ignore. File names are case insensitive.
is_string Contains a boolean which says if the array values are strings or file names.
ignore_extensions Contains an array of php extensions to ignore when calculating the version needed.
ignore_versions Contains an array of php versions to ignore when calculating the version needed.
ignore_functions_match Contains an array of function patterns to ignore when calculating the version needed.
ignore_extensions_match Contains an array of extension patterns to ignore when calculating the version needed.
ignore_constants_match Contains an array of constant patterns to ignore when calculating the version needed.
throws no exceptions thrown
since version 0.7.0 (2004-03-09)
This function can not be called statically.
array - a hash which contains information keys:
ignored_functions
,
ignored_extensions
,
ignored_constants
,
max_version
,
version
,
extensions
,
constants
,
tokens
,
cond_code
<?php
require_once 'PHP/CompatInfo.php';
$pci = new PHP_CompatInfo();
$input = array('/opt/lampp/etc/pear/PEAR.php',
'/opt/lampp/etc/pear/PHP/CompatInfo.php');
$res = $pci->parseArray($input);
#var_export($res); // no need since PCI 1.8.0
?>
We get such result.
array ( 'ignored_files' => array ( ), 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.3.0', 'classes' => array ( 0 => 'PHP_CompatInfo_Parser', ), 'extensions' => array ( 0 => 'pcre', 1 => 'tokenizer', ), 'constants' => array ( 0 => 'E_USER_ERROR', 1 => 'E_USER_NOTICE', 2 => 'E_USER_WARNING', 3 => 'FALSE', 4 => 'NULL', 5 => 'PHP_OS', 6 => 'PHP_VERSION', 7 => 'TRUE', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 5, ), '/opt/lampp/etc/pear/PEAR.php' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.3.0', 'classes' => array ( ), 'extensions' => array ( ), 'constants' => array ( 0 => 'E_USER_ERROR', 1 => 'E_USER_NOTICE', 2 => 'E_USER_WARNING', 3 => 'FALSE', 4 => 'NULL', 5 => 'PHP_OS', 6 => 'PHP_VERSION', 7 => 'TRUE', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 5, ), ), '/opt/lampp/etc/pear/PHP/CompatInfo.php' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.3.0', 'classes' => array ( 0 => 'PHP_CompatInfo_Parser', ), 'extensions' => array ( 0 => 'pcre', 1 => 'tokenizer', ), 'constants' => array ( 0 => 'FALSE', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 0, ), ), )
As for parseDir() we have global result, follow by each $input entry result in details.
Either all $input entries are files, or all are string (chunk of php code). You cannot have both (one or more) string and (one or more) file reference.
array PHP_CompatInfo::parseDir (
string $dir
, array $options
= array()
)
Parse a directory recursively for its compatibility info
$dir
Path of folder to parse
$options
An array of options where:
file_ext Contains an array of file extensions to parse for PHP code. Default: php, php4, inc, phtml
recurse_dir Boolean on whether to recursively find files
debug Contains a boolean to control whether extra ouput is shown.
ignore_functions Contains an array of functions to ignore when calculating the version needed.
ignore_constants Contains an array of constants to ignore when calculating the version needed.
ignore_files Contains an array of files to ignore. File names are case insensitive.
ignore_dirs Contains an array of directories to ignore. Directory names are case insensitive.
ignore_extensions Contains an array of php extensions to ignore when calculating the version needed.
ignore_versions Contains an array of php versions to ignore when calculating the version needed.
ignore_functions_match Contains an array of function patterns to ignore when calculating the version needed.
ignore_extensions_match Contains an array of extension patterns to ignore when calculating the version needed.
ignore_constants_match Contains an array of constant patterns to ignore when calculating the version needed.
throws no exceptions thrown
since version 0.8.0 (2004-04-22)
This function can not be called statically.
array - a hash which contains information keys:
ignored_functions
,
ignored_extensions
,
ignored_constants
,
max_version
,
version
,
extensions
,
constants
,
tokens
,
cond_code
Suppose we have these dirs/files to parse :
PHP_CompatInfo/tests/parseDir/ PHP5/tokens.php5 PHP5/upload_error.php extensions.php phpinfo.php
See package distribution to know in details content of this scripts.
<?php
require_once 'PHP/CompatInfo.php';
$pci = new PHP_CompatInfo();
$input = 'PHP_CompatInfo/tests/parseDir/';
$options = array('recurse_dir' => true,
'file_ext' => array('php', 'php5')
);
$res = $pci->parseDir($input, $options);
#var_export($res); // no need since PCI 1.8.0
?>
We get such result.
array ( 'ignored_files' => array ( ), 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '5.2.0', 'classes' => array ( 0 => 'Exception', ), 'extensions' => array ( ), 'constants' => array ( 0 => 'PHP_SHLIB_SUFFIX', 1 => 'TRUE', 2 => 'UPLOAD_ERR_CANT_WRITE', 3 => 'UPLOAD_ERR_EXTENSION', 4 => 'UPLOAD_ERR_FORM_SIZE', 5 => 'UPLOAD_ERR_INI_SIZE', 6 => 'UPLOAD_ERR_NO_FILE', 7 => 'UPLOAD_ERR_NO_TMP_DIR', 8 => 'UPLOAD_ERR_OK', 9 => 'UPLOAD_ERR_PARTIAL', ), 'tokens' => array ( 0 => 'abstract', 1 => 'catch', 2 => 'clone', 3 => 'final', 4 => 'implements', 5 => 'instanceof', 6 => 'interface', 7 => 'private', 8 => 'protected', 9 => 'public', 10 => 'throw', 11 => 'try', ), 'cond_code' => array ( 0 => 2, ), 'PHP_CompatInfo/tests/parseDir/extensions.php' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.3.2', 'classes' => array ( ), 'extensions' => array ( ), 'constants' => array ( 0 => 'PHP_SHLIB_SUFFIX', 1 => 'TRUE', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 2, ), ), 'PHP_CompatInfo/tests/parseDir/phpinfo.php' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '4.0.0', 'classes' => array ( ), 'extensions' => array ( ), 'constants' => array ( ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 0, ), ), 'PHP_CompatInfo/tests/parseDir/PHP5/tokens.php5' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '5.0.0', 'classes' => array ( 0 => 'Exception', ), 'extensions' => array ( ), 'constants' => array ( ), 'tokens' => array ( 0 => 'abstract', 1 => 'catch', 2 => 'clone', 3 => 'final', 4 => 'implements', 5 => 'instanceof', 6 => 'interface', 7 => 'private', 8 => 'protected', 9 => 'public', 10 => 'throw', 11 => 'try', ), 'cond_code' => array ( 0 => 0, ), ), 'PHP_CompatInfo/tests/parseDir/PHP5/upload_error.php' => array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '5.2.0', 'classes' => array ( 0 => 'Exception', ), 'extensions' => array ( ), 'constants' => array ( 0 => 'UPLOAD_ERR_CANT_WRITE', 1 => 'UPLOAD_ERR_EXTENSION', 2 => 'UPLOAD_ERR_FORM_SIZE', 3 => 'UPLOAD_ERR_INI_SIZE', 4 => 'UPLOAD_ERR_NO_FILE', 5 => 'UPLOAD_ERR_NO_TMP_DIR', 6 => 'UPLOAD_ERR_OK', 7 => 'UPLOAD_ERR_PARTIAL', ), 'tokens' => array ( 0 => 'throw', ), 'cond_code' => array ( 0 => 0, ), ), )
We have global result, then follow by each file parsed in sub-directories.
void PHP_CompatInfo::parseFolder (
string $folder
, array $options
= array()
)
Alias of parseDir function
$folder
Path of folder to parse
$options
An array of options
throws no exceptions thrown
since version 0.7.0 (2004-03-09)
This function can not be called statically.
array - a hash which contains information keys:
ignored_functions
,
ignored_extensions
,
ignored_constants
,
max_version
,
version
,
extensions
,
constants
,
tokens
,
cond_code
See PHP_CompatInfo::parseDir() example.
Array PHP_CompatInfo::parseFile (
string $file
, array $options
= array()
)
Parse a file for its compatibility info
$file
Path of File to parse
$options
An array of options where:
debug Contains a boolean to control whether extra ouput is shown.
ignore_functions Contains an array of functions to ignore when calculating the version needed.
ignore_constants Contains an array of constants to ignore when calculating the version needed.
ignore_extensions Contains an array of php extensions to ignore when calculating the version needed.
ignore_versions Contains an array of php versions to ignore when calculating the version needed.
ignore_functions_match Contains an array of function patterns to ignore when calculating the version needed.
ignore_extensions_match Contains an array of extension patterns to ignore when calculating the version needed.
ignore_constants_match Contains an array of constant patterns to ignore when calculating the version needed.
throws no exceptions thrown
since version 0.7.0 (2004-03-09)
This function can not be called statically.
array - a hash which contains information keys:
ignored_functions
,
ignored_extensions
,
ignored_constants
,
max_version
,
version
,
extensions
,
constants
,
tokens
,
cond_code
Suppose we have to parse source code like this one, named "conditional.php" :
<?php
// PHP 4.0.0 : __FILE__
// PHP 4.0.6 : DIRECTORY_SEPARATOR
// PHP 4.0.7 : version compare
// PHP 4.3.0 : ob_get_clean
// PHP 4.3.0 : debug_backtrace
// PHP 4.3.10 and 5.0.2 : PHP_EOL
// PHP 5.0.0 : simplexml_load_file
// PHP 5.1.1 : DATE_W3C
if (!defined('DIRECTORY_SEPARATOR')) {
define('DIRECTORY_SEPARATOR',
strtoupper(substr(PHP_OS, 0, 3) == 'WIN') ? '\\' : '/'
);
}
if (function_exists('debug_backtrace')) {
$backtrace = debug_backtrace();
} else {
$backtrace = false;
}
if (function_exists('simplexml_load_file')) {
$xml = simplexml_load_file('C:\php\pear\PHP_CompatInfo\scripts\version.xml');
}
if (version_compare(phpversion(), '5.0.0', '<')) {
include_once 'PHP/Compat.php';
PHP_Compat::loadFunction('ob_get_clean');
PHP_Compat::loadConstant('PHP_EOL');
}
echo "Hello World" . PHP_EOL;
$ds = DIRECTORY_SEPARATOR;
$fn = dirname(__FILE__) . $ds . basename(__FILE__);
echo "You have run file : $fn at " . date(DATE_W3C) . PHP_EOL;
?>
<?php
require_once 'PHP/CompatInfo.php';
$pci = new PHP_CompatInfo();
$input = 'conditional.php';
$options = array('ignore_functions' => array('simplexml_load_file'),
'ignore_constants' => array('DATE_W3C')
);
$res = $pci->parseFile($input, $options);
#var_export($res); // no need since PCI 1.8.0
?>
We get such result.
array ( 'ignored_functions' => array ( 0 => 'simplexml_load_file', ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( 0 => 'DATE_W3C', ), 'max_version' => '', 'version' => '4.3.10', 'classes' => array ( ), 'extensions' => array ( 0 => 'date', ), 'constants' => array ( 0 => 'DATE_W3C', 1 => 'DIRECTORY_SEPARATOR', 2 => 'FALSE', 3 => 'PHP_EOL', 4 => 'PHP_OS', 5 => '__FILE__', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 5, ), )
That means php simple_load_file() function and constant DATE_W3C were ignored from scope, and PHP version minimum to run is 4.3.10 (in such condition)
Of course it is impossible to run such script with only PHP 4.3.10 because usage of DATE_W3C constant is not conditionned.
Array PHP_CompatInfo::parseString (
string $string
, array $options
= array()
)
Parse a string for its compatibility info
$string
PHP Code to parses
$options
An array of options where:
debug Contains a boolean to control whether extra ouput is shown.
ignore_functions Contains an array of functions to ignore when calculating the version needed.
ignore_constants Contains an array of constants to ignore when calculating the version needed.
ignore_extensions Contains an array of php extensions to ignore when calculating the version needed.
ignore_versions Contains an array of php versions to ignore when calculating the version needed.
ignore_functions_match Contains an array of function patterns to ignore when calculating the version needed.
ignore_extensions_match Contains an array of extension patterns to ignore when calculating the version needed.
ignore_constants_match Contains an array of constant patterns to ignore when calculating the version needed.
throws no exceptions thrown
since version 0.7.0 (2004-03-09)
This function can not be called statically.
array - a hash which contains information keys:
ignored_functions
,
ignored_extensions
,
ignored_constants
,
max_version
,
version
,
extensions
,
constants
,
tokens
,
cond_code
<?php
require_once 'PHP/CompatInfo.php';
$pci = new PHP_CompatInfo();
$input = '<?php
$nl = "\n";
echo "$nl Rfc3339 = " . DATE_RFC3339;
echo "$nl RSS = " . DATE_RSS;
?>';
$res = $pci->parseString($input);
#var_export($res); // no need since PCI 1.8.0
?>
We get such result.
array ( 'ignored_functions' => array ( ), 'ignored_extensions' => array ( ), 'ignored_constants' => array ( ), 'max_version' => '', 'version' => '5.1.3', 'classes' => array ( ), 'extensions' => array ( ), 'constants' => array ( 0 => 'DATE_RFC3339', 1 => 'DATE_RSS', ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 0, ), )
That means we need PHP 5.1.3 minimum to run this chunk of code, due to usage of DATE_RFC3339 constant.
Array PHP_CompatInfo::parseData (
mixed $dataSource
, array $options
= array()
)
Parse a data source with auto detect ability. This data source, may be one of these follows: a directory, a file, a string (chunk of code), an array of multiple origin.
$dataSource
Identify the data source(s)
$options
An array of common options where:
debug Contains a boolean to control whether extra ouput is shown.
ignore_functions Contains an array of functions to ignore when calculating the version needed.
ignore_constants Contains an array of constants to ignore when calculating the version needed.
ignore_extensions Contains an array of php extensions to ignore when calculating the version needed.
ignore_versions Contains an array of php versions to ignore when calculating the version needed.
ignore_functions_match Contains an array of function patterns to ignore when calculating the version needed.
ignore_extensions_match Contains an array of extension patterns to ignore when calculating the version needed.
ignore_constants_match Contains an array of constant patterns to ignore when calculating the version needed.
An array of specific options, for parseArray parseDir or parseFolder, where:
file_ext Contains an array of file extensions to parse for PHP code. Default: php, php4, inc, phtml
ignore_files Contains an array of files to ignore. File names are case insensitive.
An array of specific options, for parseArray, where:
is_string Contains a boolean which says if the array values are strings or file names.
An array of specific options, for parseDir or parseFolder, where:
recurse_dir Boolean on whether to recursively find files
ignore_dirs Contains an array of directories to ignore. Directory names are case insensitive.
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
array - a hash which contains information keys:
ignored_files
,
ignored_functions
,
ignored_extensions
,
ignored_constants
,
max_version
,
version
,
extensions
,
constants
,
tokens
,
cond_code
or FALSE on error.
<?php
require_once 'PHP/CompatInfo.php';
$source = '/tmp/File_Find-1.3.0/Find.php';
$options = array('debug' => true);
$pci = new PHP_CompatInfo();
$pci->parseData($source, $options);
?>
mixed
PHP_CompatInfo::getVersion
(
mixed $file
= false
, bool $max
= false
)
Returns the latest parse data source version, minimum and/or maximum
$file
(optional) A specific filename or not (FALSE)
$max
(optional) Level with or without contextual data
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b1 (2008-11-30)
This function can not be called statically.
Suppose we have to parse source code like this file, named "conditional.php" :
<?php
// PHP 4.0.0 : __FILE__
// PHP 4.0.6 : DIRECTORY_SEPARATOR
// PHP 4.0.7 : version compare
// PHP 4.3.0 : ob_get_clean
// PHP 4.3.0 : debug_backtrace
// PHP 4.3.10 and 5.0.2 : PHP_EOL
// PHP 5.0.0 : simplexml_load_file
// PHP 5.1.1 : DATE_W3C
if (!defined('DIRECTORY_SEPARATOR')) {
define('DIRECTORY_SEPARATOR',
strtoupper(substr(PHP_OS, 0, 3) == 'WIN') ? '\\' : '/'
);
}
if (function_exists('debug_backtrace')) {
$backtrace = debug_backtrace();
} else {
$backtrace = false;
}
if (function_exists('simplexml_load_file')) {
$xml = simplexml_load_file('C:\php\pear\PHP_CompatInfo\scripts\version.xml');
}
if (version_compare(phpversion(), '5.0.0', '<')) {
include_once 'PHP/Compat.php';
PHP_Compat::loadFunction('ob_get_clean');
PHP_Compat::loadConstant('PHP_EOL');
}
echo "Hello World" . PHP_EOL;
$ds = DIRECTORY_SEPARATOR;
$fn = dirname(__FILE__) . $ds . basename(__FILE__);
echo "You have run file : $fn at " . date(DATE_W3C) . PHP_EOL;
?>
And this second file, named "upload_error.php" :
<?php
$uploadErrors = array(
UPLOAD_ERR_INI_SIZE => "The uploaded file exceeds the upload_max_filesize directive in php.ini.",
UPLOAD_ERR_FORM_SIZE => "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.",
UPLOAD_ERR_PARTIAL => "The uploaded file was only partially uploaded.",
UPLOAD_ERR_NO_FILE => "No file was uploaded.",
UPLOAD_ERR_NO_TMP_DIR => "Missing a temporary folder.",
UPLOAD_ERR_CANT_WRITE => "Failed to write file to disk.",
UPLOAD_ERR_EXTENSION => "File upload stopped by extension.",
);
$errorCode = $_FILES["myUpload"]["error"];
if ($errorCode !== UPLOAD_ERR_OK) {
if (isset($uploadErrors[$errorCode])) {
throw new Exception($uploadErrors[$errorCode]);
} else {
throw new Exception("Unknown error uploading file.");
}
}
?>
Script to parse data source, will look like to:
<?php
require_once 'PHP/CompatInfo.php';
$pci = new PHP_CompatInfo('null');
$dir = dirname(__FILE__) . DIRECTORY_SEPARATOR;
$input = array($dir . 'conditional.php', $dir . 'upload_error.php');
$options = array('ignore_functions' => array('simplexml_load_file'),
'ignore_constants' => array('DATE_W3C')
);
$pci->parseData($input, $options);
$version = $pci->getVersion();
echo 'GLOBAL version = '. $version . PHP_EOL;
$version = $pci->getVersion($input[0]);
echo basename($input[0]) . ' version = '. $version . PHP_EOL;
$classes = $pci->getClasses();
echo 'ALL Classes = '. implode(',', $classes) . PHP_EOL;
$functions = $pci->getFunctions();
echo 'ALL Functions = '. implode(',', $functions) . PHP_EOL;
$extensions = $pci->getExtensions();
echo 'ALL Extensions required = '. implode(',', $extensions) . PHP_EOL;
$constants = $pci->getConstants();
echo 'ALL Constants required = '. implode(',', $constants) . PHP_EOL;
$tokens = $pci->getTokens();
echo 'ALL Tokens required = '. implode(',', $tokens) . PHP_EOL;
$conditions = $pci->getConditions(false, true);
echo 'ALL Code Conditions = '. $conditions . PHP_EOL;
$conditions = $pci->getConditions($input[1], true);
echo basename($input[1]) . ' conditions = '. $conditions . PHP_EOL;
?>
We have just used the NULL renderer because we want to organize output results, as below:
GLOBAL version = 5.2.0 conditional.php version = 4.3.10 ALL Classes = Exception ALL Functions = basename,date,debug_backtrace,define,defined,dirname,function_exists, phpversion,simplexml_load_file,strtoupper,substr,version_compare ALL Extensions required = date ALL Constants required = DATE_W3C,DIRECTORY_SEPARATOR,FALSE,PHP_EOL,PHP_OS, UPLOAD_ERR_CANT_WRITE,UPLOAD_ERR_EXTENSION,UPLOAD_ERR_FORM_SIZE,UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_NO_FILE,UPLOAD_ERR_NO_TMP_DIR,UPLOAD_ERR_OK,UPLOAD_ERR_PARTIAL,__FILE__ ALL Tokens required = throw ALL Code Conditions = 5 upload_error.php conditions = 0
mixed
PHP_CompatInfo::getClasses
(
mixed $file
= false
)
Returns the latest parse data source classes declared (internal or end-user defined)
$file
(optional) A specific filename or not (FALSE)
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b1 (2008-11-30)
This function can not be called statically.
See PHP_CompatInfo::getVersion() example.
mixed
PHP_CompatInfo::getFunctions
(
mixed $file
= false
)
Returns the latest parse data source functions declared (internal or end-user defined)
$file
(optional) A specific filename or not (FALSE)
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b1 (2008-11-30)
This function can not be called statically.
See PHP_CompatInfo::getVersion() example.
mixed
PHP_CompatInfo::getExtensions
(
mixed $file
= false
)
Returns the latest parse data source extensions used
$file
(optional) A specific filename or not (FALSE)
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b1 (2008-11-30)
This function can not be called statically.
See PHP_CompatInfo::getVersion() example.
mixed
PHP_CompatInfo::getConstants
(
mixed $file
= false
)
Returns the latest parse data source constants declared (internal or end-user defined)
$file
(optional) A specific filename or not (FALSE)
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b1 (2008-11-30)
This function can not be called statically.
See PHP_CompatInfo::getVersion() example.
mixed
PHP_CompatInfo::getTokens
(
mixed $file
= false
)
Returns the latest parse data source PHP5+ tokens declared
$file
(optional) A specific filename or not (FALSE)
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b1 (2008-11-30)
This function can not be called statically.
See PHP_CompatInfo::getVersion() example.
mixed
PHP_CompatInfo::getConditions
(
mixed $file
= false
, bool $levelOnly
= false
)
Returns the latest parse data source conditions, with or without contextual data
$file
(optional) A specific filename or not (FALSE)
$levelOnly
(optional) Level with or without contextual data
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b1 (2008-11-30)
This function can not be called statically.
See PHP_CompatInfo::getVersion() example.
array
PHP_CompatInfo::getIgnoredFiles
(
)
Returns list of files ignored while parsing directories
returns or false on error
throws no exceptions thrown
since version 1.9.0b2 (2008-12-19)
This function can not be called statically.
mixed
PHP_CompatInfo::getIgnoredFunctions
(
mixed $file
= false
)
Returns the latest parse data source ignored functions list
$file
(optional) A specific filename or not (FALSE)
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b2 (2008-12-19)
This function can not be called statically.
Suppose we have to parse source code like this file, named "conditional.php" :
<?php
// PHP 4.0.0 : __FILE__
// PHP 4.0.6 : DIRECTORY_SEPARATOR
// PHP 4.0.7 : version compare
// PHP 4.3.0 : ob_get_clean
// PHP 4.3.0 : debug_backtrace
// PHP 4.3.10 and 5.0.2 : PHP_EOL
// PHP 5.0.0 : simplexml_load_file
// PHP 5.1.1 : DATE_W3C
if (!defined('DIRECTORY_SEPARATOR')) {
define('DIRECTORY_SEPARATOR',
strtoupper(substr(PHP_OS, 0, 3) == 'WIN') ? '\\' : '/'
);
}
if (function_exists('debug_backtrace')) {
$backtrace = debug_backtrace();
} else {
$backtrace = false;
}
if (function_exists('simplexml_load_file')) {
$xml = simplexml_load_file('C:\php\pear\PHP_CompatInfo\scripts\version.xml');
}
if (version_compare(phpversion(), '5.0.0', '<')) {
include_once 'PHP/Compat.php';
PHP_Compat::loadFunction('ob_get_clean');
PHP_Compat::loadConstant('PHP_EOL');
}
echo "Hello World" . PHP_EOL;
$ds = DIRECTORY_SEPARATOR;
$fn = dirname(__FILE__) . $ds . basename(__FILE__);
echo "You have run file : $fn at " . date(DATE_W3C) . PHP_EOL;
?>
And this other one, named "ignore_functions_match.php"
<?php
function toFile($filename, $data)
{
if (function_exists('file_put_contents')) {
file_put_contents($filename, $data);
} else {
$file = fopen($filename, 'wb');
fwrite($file, $data);
fclose($file);
}
}
if (function_exists('debug_backtrace')) {
$backtrace = debug_backtrace();
} else {
$backtrace = false;
}
debug_print_backtrace();
?>
Script to parse data source, will look like to:
<?php
require_once 'PHP/CompatInfo.php';
$pci = new PHP_CompatInfo('null');
$dir = dirname(__FILE__) . DIRECTORY_SEPARATOR;
$input = array($dir . 'conditional.php', $dir . 'ignore_functions_match.php');
$options = array('ignore_functions_match' => array('preg_match', array('/^debug/')),
'ignore_constants' => array('DIRECTORY_SEPARATOR')
);
$pci->parseData($input, $options);
$functions = $pci->getIgnoredFunctions();
echo 'ALL Ignored Functions = '. implode(',', $functions) . PHP_EOL;
$functions = $pci->getIgnoredFunctions($input[0]);
echo basename($input[0]) . ' ignored functions = '. implode(',', $functions) . PHP_EOL;
$extensions = $pci->getIgnoredExtensions();
echo 'ALL Ignored Extensions = '. implode(',', $extensions) . PHP_EOL;
$constants = $pci->getIgnoredConstants();
echo 'ALL Ignored Constants = '. implode(',', $constants) . PHP_EOL;
?>
We have just used the NULL renderer because we want to organize output results, as below:
ALL Ignored Functions = debug_backtrace,debug_print_backtrace conditional.php ignored functions = debug_backtrace ALL Ignored Extensions = ALL Ignored Constants = DIRECTORY_SEPARATOR
mixed
PHP_CompatInfo::getIgnoredExtensions
(
mixed $file
= false
)
Returns the latest parse data source ignored extensions list
$file
(optional) A specific filename or not (FALSE)
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b2 (2008-12-19)
This function can not be called statically.
See PHP_CompatInfo::getIgnoredFunctions() example.
mixed
PHP_CompatInfo::getIgnoredConstants
(
mixed $file
= false
)
Returns the latest parse data source ignored constants list
$file
(optional) A specific filename or not (FALSE)
returns Null on error or if there were no previous data parsing
throws no exceptions thrown
since version 1.9.0b2 (2008-12-19)
This function can not be called statically.
See PHP_CompatInfo::getIgnoredFunctions() example.
array
PHP_CompatInfo::getSummary
(
)
Returns only summary when parsing a directory or multiple data sources
returns Array of summary results
throws no exceptions thrown
since version 1.9.0 (2009-01-19)
This function can not be called statically.
void constructor
PHP_CompatInfo_Parser::PHP_CompatInfo_Parser
(
)
Parser Class constructor
returns instance of object PHP_CompatInfo_Parser
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void
PHP_CompatInfo_Parser::setOutputDriver
(
string
$type
,
array
$conf
= array()
)
Set up driver to be used, dependant on specified type.
$type
Name the type of driver (html, text...)
$conf
A hash containing any additional configuration
returns void
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void
PHP_CompatInfo_Parser::addListener
(
mixed
$callback
,
string
$nName
= EVENT_DISPATCHER_GLOBAL
)
Registers a new listener with the given criteria.
$callback
A PHP callback
$nName
(optional) Expected notification name
returns void
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
see PHP_CompatInfo::addListener() example
bool
PHP_CompatInfo_Parser::removeListener
(
mixed
$callback
,
string
$nName
= EVENT_DISPATCHER_GLOBAL
)
Removes a registered listener that correspond to the given criteria.
$callback
A PHP callback
$nName
(optional) Expected notification name
returns True if listener was removed, false otherwise.
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
see PHP_CompatInfo::addListener() example
void
PHP_CompatInfo_Parser::notifyListeners
(
string
$event
,
array
$info
= array()
)
This notification occured only if a dispatcher exists. That means if at least one listener was registered.
$event
Name of the notification handler
$info
(optional) Additional information about the notification
returns void
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
array
PHP_CompatInfo_Parser::loadVersion
(
string
$min
,
string|boolean
$max
= false
,
boolean
$include_const
= false
,
boolean
$groupby_vers
= false
)
Load components list for a PHP version or subset
$min
PHP minimal version
$max
(optional) PHP maximal version
$include_const
(optional) include constants list in final result
$groupby_vers
(optional) give initial php version of function or constant
returns An array of php function/constant names history
throws no exceptions thrown
since version 1.2.0 (2006-08-23)
This function can not be called statically.
array
PHP_CompatInfo_Parser::getDirlist
(
mixed
$dir
,
array
$options
)
Returns list of directory parsed, depending of restrictive parser options.
$dir
The directory name
$options
An array of parser options. See parseData() method.
returns array - list of directories that should be parsed
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
array
PHP_CompatInfo_Parser::getFilelist
(
mixed
$dir
,
array
$options
)
Returns list of files parsed, depending of restrictive parser options.
$dir
The directory name where to look files
$options
An array of parser options. See parseData() method.
returns array - list of files that should be parsed
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
array
PHP_CompatInfo_Parser::getIgnoredFiles
(
)
Returns list of files ignored while parsing directories
returns array or false on error
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
array
PHP_CompatInfo_Parser::parseData
(
mixed
$dataSource
,
array
$options
= array()
)
Parse a data source with auto detect ability. This data source, may be one of these follows: a directory, a file, a string (chunk of code), an array of multiple origin.
Each of five parsing functions support common and specifics options.
* Common options :
'debug' Contains a boolean to control whether extra ouput is shown.
'ignore_functions' Contains an array of functions to ignore when calculating the version needed.
'ignore_constants' Contains an array of constants to ignore when calculating the version needed.
'ignore_extensions' Contains an array of php extensions to ignore when calculating the version needed.
'ignore_versions' Contains an array of php versions to ignore when calculating the version needed.
'ignore_functions_match' Contains an array of function patterns to ignore when calculating the version needed.
'ignore_extensions_match' Contains an array of extension patterns to ignore when calculating the version needed.
'ignore_constants_match' Contains an array of constant patterns to ignore when calculating the version needed.
* parseArray, parseDir|parseFolder, specific options :
'file_ext' Contains an array of file extensions to parse for PHP code. Default: php, php4, inc, phtml
'ignore_files' Contains an array of files to ignore. File names are case insensitive.
* parseArray specific options :
'is_string' Contains a boolean which says if the array values are strings or file names.
* parseDir|parseFolder specific options :
'recurse_dir' Boolean on whether to recursively find files
'ignore_dirs' Contains an array of directories to ignore. Directory names are case insensitive.
$dataSource
The data source (may be file, dir, string, or array)
$options
An array of options. See above.
returns array or false on error
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
see PHP_CompatInfo::parseData() example
void constructor
PHP_CompatInfo_Renderer::PHP_CompatInfo_Renderer
(
object
&$parser
,
array
$conf
)
Base Renderer Class constructor
&$parser
Instance of the parser (model of MVC pattern)
$conf
A hash containing any additional configuration
returns instance of object PHP_CompatInfo_Renderer
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
object PHP_CompatInfo_Renderer
&
PHP_CompatInfo_Renderer::factory
(
object
&$parser
,
string
$type
= 'array'
,
array
$conf
= array()
)
Creates a concrete instance of the renderer depending of $type
&$parser
A concrete instance of the parser
$type
(optional) Type of instance required, case insensitive
$conf
(optional) A hash containing any additional configuration information that a subclass might need.
returns A concrete PHP_CompatInfo_Renderer instance, or null on error.
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void
PHP_CompatInfo_Renderer::update
(
object
&$auditEvent
)
Interface to update the view with current information. Listen events produced by Event_Dispatcher and the PHP_CompatInfo_Parser
&$auditEvent
Instance of Event_Dispatcher
returns void
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void
PHP_CompatInfo_Renderer::startWaitProgress
(
integer
$maxEntries
)
Initialize the wait process, with a simple message or a progress bar.
$maxEntries
Number of source to parse
returns void
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void
PHP_CompatInfo_Renderer::stillWaitProgress
(
string
$source
,
string
$index
)
Update the wait message, or status of the progress bar
$source
Source (file, string) currently parsing
$index
Position of the $source in the data source list to parse
returns void
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void
PHP_CompatInfo_Renderer::endWaitProgress
(
)
Finish the wait process, by erasing the progress bar
returns void
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
boolean
PHP_CompatInfo_Renderer::isIncludable
(
string
$file
)
Returns whether or not a file is in the include path
$file
Path to filename to check if includable
returns True if the file is in the include path, false otherwise
throws no exceptions thrown
since version 1.7.0b4 (2008-04-03)
This function can not be called statically.
void constructor
PHP_CompatInfo_Renderer_Array::PHP_CompatInfo_Renderer_Array
(
object
&$parser
,
array
$conf
)
Array Renderer Class constructor
&$parser
Instance of the parser (model of MVC pattern)
$conf
A hash containing any additional configuration
returns instance of object PHP_CompatInfo_Renderer_Array
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void
PHP_CompatInfo_Renderer_Array::display
(
)
Display final results, when data source parsing is over.
returns void
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void constructor
PHP_CompatInfo_Renderer_Csv::PHP_CompatInfo_Renderer_Csv
(
object
&$parser
,
array
$conf
)
Csv Renderer Class constructor
&$parser
Instance of the parser (model of MVC pattern)
$conf
A hash containing any additional configuration
returns instance of object PHP_CompatInfo_Renderer_Csv
throws no exceptions thrown
since version 1.8.0b4 (2008-06-18)
This function can not be called statically.
void
PHP_CompatInfo_Renderer_Csv::display
(
)
Display final results, when data source parsing is over.
returns void
throws no exceptions thrown
since version 1.8.0b4 (2008-06-18)
This function can not be called statically.
void constructor
PHP_CompatInfo_Renderer_Html::PHP_CompatInfo_Renderer_Html
(
object
&$parser
,
array
$conf
)
Html Renderer Class constructor
&$parser
Instance of the parser (model of MVC pattern)
$conf
A hash containing any additional configuration
returns instance of object PHP_CompatInfo_Renderer_Html
throws no exceptions thrown
since version 1.8.0b4 (2008-06-18)
This function can not be called statically.
void
PHP_CompatInfo_Renderer_Html::display
(
)
Display final results, when data source parsing is over.
returns void
throws no exceptions thrown
since version 1.8.0b4 (2008-06-18)
This function can not be called statically.
mixed
PHP_CompatInfo_Renderer_Html::getStyleSheet
(
int
$destination
= 1
,
mixed
$extra
= null
)
Returns the custom style sheet to use for layout
$destination
(optional) Destination of css content
$extra
(optional) Additional data depending of destination
returns mixed
throws no exceptions thrown
since version 1.8.0b4 (2008-06-18)
This function can not be called statically.
bool
PHP_CompatInfo_Renderer_Html::setStyleSheet
(
string
$css
= null
)
Set a custom style sheet to use your own styles
$css
(optional) File to read user-defined styles from
returns True if custom styles, false if default styles applied
throws no exceptions thrown
since version 1.8.0b4 (2008-06-18)
This function can not be called statically.
string
PHP_CompatInfo_Renderer_Html::toHtml
(
object
$obj
)
Returns HTML code of parsing result
$obj
instance of HTML_Table
returns string - html code
throws no exceptions thrown
since version 1.8.0b4 (2008-06-18)
This function can not be called statically.
void constructor
PHP_CompatInfo_Renderer_Null::PHP_CompatInfo_Renderer_Null
(
object
&$parser
,
array
$conf
)
Null Renderer Class constructor
&$parser
Instance of the parser (model of MVC pattern)
$conf
A hash containing any additional configuration
returns instance of object PHP_CompatInfo_Renderer_Null
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void
PHP_CompatInfo_Renderer_Null::display
(
)
Consumes all output events
returns void
throws no exceptions thrown
since version 1.8.0RC1 (2008-07-01)
This function can not be called statically.
void constructor
PHP_CompatInfo_Renderer_Text::PHP_CompatInfo_Renderer_Text
(
object
&$parser
,
array
$conf
)
Text Renderer Class constructor
&$parser
Instance of the parser (model of MVC pattern)
$conf
A hash containing any additional configuration
returns instance of object PHP_CompatInfo_Renderer_Text
throws no exceptions thrown
since version 1.8.0b3 (2008-06-07)
This function can not be called statically.
void
PHP_CompatInfo_Renderer_Text::display
(
)
Display final results, when data source parsing is over.
returns void
throws no exceptions thrown
since version 1.8.0b3 (2008-06-07)
This function can not be called statically.
void constructor
PHP_CompatInfo_Renderer_Xml::PHP_CompatInfo_Renderer_Xml
(
object
&$parser
,
array
$conf
)
Xml Renderer Class constructor
&$parser
Instance of the parser (model of MVC pattern)
$conf
A hash containing any additional configuration
returns instance of object PHP_CompatInfo_Renderer_Xml
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void
PHP_CompatInfo_Renderer_Xml::display
(
)
Display final results, when data source parsing is over.
returns void
throws no exceptions thrown
since version 1.8.0b2 (2008-06-03)
This function can not be called statically.
void constructor
PHP_CompatInfo_Cli::PHP_CompatInfo_Cli
(
)
Command-Line Class constructor
returns instance of object PHP_CompatInfo_Cli
throws no exceptions thrown
since version 0.8.0 (2004-04-22)
This function can not be called statically.
void
PHP_CompatInfo_Cli::run
(
)
Run the CLI version of PHP_CompatInfo
returns void
throws no exceptions thrown
since version 0.8.0 (2004-04-22)
This function can not be called statically.
Creates a function calls debug trace. Functions arguments, returned parameters and watched variables are reported in the same section for each function call. The trace is available as an array, or can be displayed or written in a file. Traced variables can be processed by provided user functions for displaying purposes.
Functions arguments, returned parameters and watched variables are reported in the same section for each function call. The trace is available as an array, or can be displayed or written in a file. Traced variables can be processed by provided user functions for displaying purposes.
This package is not a replacement for full fledged PHP debuggers. It is useful for (1) remote debugging, (2) to debug a complex sequence of function calls, (3) to display non text variables in a user readable format.
traceArguments() is used to trace the function arguments. There is no need to pass the function arguments to traceArguments() to trace them. Note that traceArguments() may not be used, if the function does not call any other function or if the function calls functions that are not being traced.
traceVariables() is used to trace variables within the function. The variables to trace must be passed as arguments to traceVariables(). traceVariables() may be called as many times as required to watch variables value change.
traceReturn() is used to trace the variables returned by the function. The variables to trace must be passed as arguments to traceReturn().
putTrace() is used to display the trace to the standard output or to write it into a file.
In this example, package.php
contains two classes whose methods are traced.
The package is used by an application trace.php
.
The trace is stored into trace.txt
.
To generate the trace, run: #php trace.php.
The package package.php
Note that testing if PHP_FunctionCallTracer is loaded: class_exists('PHP_FunctionCallTracer', false), is optional. It is useful only if the tracing methods are meant to be left in the code. They will be called only if PHP_FunctionCallTracer is loaded.
<?php
class math {
/**
* tracing the arguments and the returned parameter
*
* note that traceReturn() calls traceArguments() by default which is fine here
* since this method does call other methods to trace
*/
public static function prod($x, $y)
{
// class_exists('PHP_FunctionCallTracer', false)
// and PHP_FunctionCallTracer::traceArguments();
$p = $x * $y;
class_exists('PHP_FunctionCallTracer', false) and
PHP_FunctionCallTracer::traceReturn($p);
return $p;
}
/**
* tracing the arguments and the returned parameter
*
* traceArguments() must be called here since this method calls other methods
* that may be traced, so that traced calls are displayed in the right order
*/
public static function square($x)
{
class_exists('PHP_FunctionCallTracer', false) and
PHP_FunctionCallTracer::traceArguments();
$x2 = self::prod($x, $x);
class_exists('PHP_FunctionCallTracer', false) and
PHP_FunctionCallTracer::traceReturn($x2);
return $x2;
}
}
class geometry {
private $pi = 3.14;
/**
* tracing the arguments and the returned parameter
* another variable is traced along with the returned parameter
*/
public function circle($r)
{
class_exists('PHP_FunctionCallTracer', false) and
PHP_FunctionCallTracer::traceArguments();
$pi2 = 2 * $this->pi;
$c = math::prod($r, $pi2);
class_exists('PHP_FunctionCallTracer', false) and
PHP_FunctionCallTracer::traceReturn($c, $pi2);
return $c;
}
/**
* tracing the arguments, some variables and the returned parameter
*/
public function disk($r)
{
class_exists('PHP_FunctionCallTracer', false) and
PHP_FunctionCallTracer::traceArguments();
$r2 = math::square($r);
class_exists('PHP_FunctionCallTracer', false) and
PHP_FunctionCallTracer::traceVariables($r2, $this->pi);
$d = math::prod($r2, $this->pi);
class_exists('PHP_FunctionCallTracer', false) and
PHP_FunctionCallTracer::traceReturn($d);
return $d;
}
}
?>
The application trace.php
<?php
/**
* adds the path of the package if this is a raw install
* includes the package (example) to debug and the tracer package
*/
file_exists("../../../../PHP/") and set_include_path('../../../..' . PATH_SEPARATOR . get_include_path());
require_once 'package.php';
require_once 'PHP/FunctionCallTracer.php';
/**
* creates an instance of the class to debug and calls a few methods
* writes the trace in a file
*/
$geometry = new geometry;
$c = $geometry->circle(2);
$d = $geometry->disk(3);
$file = dirname(__FILE__) . '/trace.txt';
PHP_FunctionCallTracer::putTrace($file, false);
?>
The trace trace.txt
Array ( [php_uname] => Windows NT mybox 5.1 build 2600 [date] => Friday, 03-Aug-07 09:17:30 UTC [calls] => Array ( [0] => Array ( [call] => Array ( [file] => trace.php [line] => 20 [function] => geometry->circle ) [in] => Array ( [file] => package.php [line] => 55 [args] => Array ( [0] => 2 ) ) [out] => Array ( [file] => package.php [line] => 61 [args] => Array ( [0] => 12.56 [1] => 6.28 ) ) ) [1] => Array ( [call] => Array ( [file] => package.php [line] => 58 [function] => math::prod ) [in] => Array ( [file] => package.php [line] => 22 [args] => Array ( [0] => 2 [1] => 6.28 ) ) [out] => Array ( [file] => package.php [line] => 22 [args] => Array ( [0] => 12.56 ) ) ) [2] => Array ( [call] => Array ( [file] => trace.php [line] => 21 [function] => geometry->disk ) [in] => Array ( [file] => package.php [line] => 71 [args] => Array ( [0] => 3 ) ) [watches] => Array ( [0] => Array ( [file] => package.php [line] => 75 [args] => Array ( [0] => 9 [1] => 3.14 ) ) ) [out] => Array ( [file] => package.php [line] => 80 [args] => Array ( [0] => 28.26 ) ) ) [3] => Array ( [call] => Array ( [file] => package.php [line] => 73 [function] => math::square ) [in] => Array ( [file] => package.php [line] => 35 [args] => Array ( [0] => 3 ) ) [out] => Array ( [file] => package.php [line] => 40 [args] => Array ( [0] => 9 ) ) ) [4] => Array ( [call] => Array ( [file] => package.php [line] => 37 [function] => math::prod ) [in] => Array ( [file] => package.php [line] => 22 [args] => Array ( [0] => 3 [1] => 3 ) ) [out] => Array ( [file] => package.php [line] => 22 [args] => Array ( [0] => 9 ) ) ) [5] => Array ( [call] => Array ( [file] => package.php [line] => 77 [function] => math::prod ) [in] => Array ( [file] => package.php [line] => 22 [args] => Array ( [0] => 9 [1] => 3.14 ) ) [out] => Array ( [file] => package.php [line] => 22 [args] => Array ( [0] => 28.26 ) ) ) ) [objects] => Array ( [0] => geometry Object ( [pi:private] => 3.14 ) [2] => same as #0 ) )
A PHP parser, an XMI generator, and an API documentation tool
PHP_UML converts programming data from one format (PHP or XMI) to another (PHP, XMI or HTML). In other words, PHP_UML is a reverse-engineering tool, and an API documentation tool.
It can parse PHP files, and generate:
From its version 1.5, PHP_UML can also parse procedural code. The export format called "htmlnew" benefits from this new capability, and turns PHP_UML into a kind of competitor of PhpDocumentor (although it does not offer all of its features). See here an example of an API documentation as generated by PHP_UML.
PHP_UML is able to parse the following PHP elements: namespaces, classes, interfaces, properties, and functions. It can also retrieve information from the inline comments, via the annotations: @package
, @var
, @param
@param
and @var
learn PHP_UML about the expected types of a parameter or a property (when type hinting is not present).
@package
learns PHP_UML about the namespace of an element (even if, from PHP 5.3, it is recommended to use the PHP namespacing instructions instead)
So the more documented your PHP code is, the more precise your XMI file, or your API documentation, will be.
You can use PHP_UML either from command line (the simplest solution), or by writing a piece of code that relies on PHP_UML.
Parsing a single file test.php
, and generating its XMI file:
<?php
require_once 'PHP/UML.php';
$uml = new PHP_UML();
$uml->setInput('tests'); // this defines which files/folders to parse (here, the folder "tests")
$uml->parse('myApp'); // this starts the parser, and gives the name "myApp" to the generated metamodel
$uml->export('xmi', 'myApp.xmi'); // this serializes the metamodel in XMI code, and saves it to a file "myApp.xmi"
?>
At the current time, the UML/XMI standards exist in two distinct families of versions, 1.x and 2.x. PHP_UML can generate XMI in version 1.4, as well as in version 2.1. Be warned, though, that some UML tools might not interpret accurately the data contained in your XMI file. For example, the link between a UML artifact (a source file) and the classes defined inside that artifact is only available from version 2 of UML.
PHP_UML can also convert existing UML/XMI data from version 1.4 to version 2. An inline converter (using PHP_UML behind the scene) is available here.
The executable phpuml
relies on Console::CommandLine.
If you have followed the normal PEAR installation process to install PHP_UML, that dependency should have been resolved, and you should be able to run phpuml
directly from the command line.
PHP_UML converts data into another kind of data. This can be summarized with:
$ phpuml [INPUT] -o [OUTPUT LOCATION] -f [OUTPUT FORMAT]
In other words, to specify the files/directories to scan, pass them as main arguments:
$ phpuml /var/www/foo
Separate the different elements by a space:
$ phpuml /var/www/file1.php file2.php
By default, phpuml
will recursively parse the specified files/folders, and will echo the XMI code on the screen (UML/XMI version 2).
Renaming the UML model name
By default, the root package of a UML model is named default. To rename it, use the switch -n
:
$ phpuml /var/www/foo -n MyProject
Saving to a file
To save the XMI data in a particular place, instead of printing it on the screen, use -o
:
$ phpuml /var/www/foo -n MyProject -o /var/tmp/
This will parse /var/www/foo
, and save the XMI data to /var/tmp/MyProject.xmi
.
Use the dot to save to the current directory: -o .
The option -o
also accepts a file name, instead of a directory path.
Generating an API's documentation in HTML, or some PHP code
In addition to xmi, 3 output formats are also available: html, htmlnew, and php.
Use the option -f
to specify which format you want phpuml
to generate.
$ phpuml /var/www/foo -f html -o /var/tmp/
This will scan /var/www/foo
, and create the API documentation in /var/tmp/
.
If you need to provide your own XMI file (instead of parsing existing PHP files), simply pass it as argument.
$ phpuml myFile.xmi -f php -o /var/tmp/
This will read the XMI code contained in myFile.xmi
, and generate the PHP code templates in /var/tmp/
.
Note that with the command line tool, you cannot both read an XMI file and parse PHP files: you will have to use the API if you need to build
a UML model by merging inputs from XMI and PHP simultaneously.
Selecting the UML/XMI version
To select which version of the UML/XMI standards you want your XMI to be written in, use the option -x
:
$ phpuml /var/www/foo -x 1 -o /var/tmp/
Note that Argouml accepts XMI code only in version 1, while the Eclipse plugins (Ecore standard) only in version 2.
Converting from UML/XMI version 1 to 2
phpuml
can automatically convert UML/XMI data from version 1.4 to version 2.1.
$ phpuml foo1.xmi -o foo2.xmi
This will read foo1.xmi
, and, if its XMI content is in version 1.x, converts it to version 2, and stores it in foo2.xmi
.
Note that this is an ad hoc conversion, inspired on what Rationale Rose, Argouml and Umbrello generate. It does not convert all of the UML entities, and it does not interpret all of the various XMI dialects.
Filtering the files to parse
By default, phpuml
will parse only files with the extension .php
. To modify this file pattern, use the - m
selector:
$ phpuml /var/www/foo -m *.php *.txt
This will parse all php
and txt
files.
Ignoring some files and folders
Use the switch -i
:
$ phpuml /var/www/foo -i tests *.php4
This will parse all files, except the ones in the folder "tests", and the ones with an extension "php4".
Removing the dollar sign ($) in the documentation
Use the switch --no-dollar
:
$ phpuml /var/www/foo --no-dollar -o foo.xmi
Other options are available. You will discover them by asking for help, like this:
$ phpuml -h
Form more information about how PHP_UML interprets your PHP code, read this section.
PHP_UML relies on the PHP extension XSL for certain output formats. Please modify your php.ini to enable this extension.
You can ignore this warning. If you really don't want to see it, add a "date.timezone" configuration line in your php.ini
Some modeling tools expect XMI in version 1. By default, PHP_UML generates XMI in version 2. So use the switch -x to specify the version 1, as described in Command line interface
Even though PHP is not a "strong typed language", PHP_UML relies a set of predefined types (integer, float, string, mixed, etc.) and tries to use them as much as it can guess. By inspecting the default values, the type hints in the functions parameters (when they are present), as well as the docblocks @param, PHP_UML can detect the types of the parameters, constants and properties. When it cannot guess, it assumes that it is the mixed type.
PHP_UML is also aware of a couple of internal PHP classifiers, such as Exception or Iterator (that's why you might see them appear in the API documentation).
What happens if the parser has not been able to resolve a type/class/interface? (for example, when a class implements an interface whose source code has not been provided)
Even if packages don't exist by themselves in PHP, PHP_UML reconstitutes them by using the PHP namespaces (from PHP 5.3), or by using the docblock @package (if the source code has some). As for the "top package", it is nothing else than the "global namespace" of PHP.
Although this is not very UML style, PHP_UML can parse procedural code. The output format htmlnew is currently the only format to benefit from this new capability (the XMI format cannot, since it is a strict object-oriented XML vocabulary).
In the API documentation, the procedural functions will appear under Functions and the procedural constants under Properties, inside the package that matches the namespace that these elements belong to.
By default, the parser will ignore all the elements that have a docblock @internal. If you want to have them parsed anyway, use the switch --show-internal
Similarly, the switch --only-api forces PHP_UML to parse only the elements that are annotated with an @api.
With the switch --no-dollar, the parser removes the $ at the beginning of the property names (this character can cause problems with some UML tools).
Set the error reporting level to 2 with "--error-level 2" to display a list of the types/classes/interfaces that PHP_UML has not been able to resolve.
Packages do not exist in PHP, like they do in Java. There are two possible ways to mimic them:
@package
in the comment of a class (or of a file)
Note that the namespace
and use
instructions will be parsed only if you run PHP_UML with PHP from version 5.3.
In UML 1.4:
In UML 2.1:
PHP_UML
is structured in 4 packages:
PHP_UML
) and some utility classes
Input
package, where the PHP and XMI parser reside (they are the Importer
objects).
Metamodel
package, which contains the data structures that PHP_UML
is using to modelize the code parsed
Output
package, which contains all the objects (called Exporter
) to transform the UML model stored into memory into an output format (like XMI, HTML, or PHP)
If the default settings suit your needs, the only single class you need to know about is PHP_UML
.
For more advanced operations, you must use the Importer
and Exporter
hierarchies of objects,
whose roles, respectively, are to import
data into the UML model, and export data out from the UML model.
Parsing of a single file test.php
, and generation of its XMI file:
<?php
require_once 'PHP/UML.php';
$uml = new PHP_UML();
$uml->setInput('test.php');
$uml->parse('foo'); // parses, and sets the name of the root package
$uml->export('xmi', 'test.xmi'); // first param is the format (html, php, htmlnew or xmi), second param is the output folder
?>
Parsing of two directories, ignoring the CSV
folders, and generation of HTML documentation
<?php
require_once 'PHP/UML.php';
$uml = new PHP_UML();
$uml->setInput(array('C:\Inetpub\foo', 'C:\Inetpub\libraries'));
$uml->parse();
$uml->export('html', 'C:\Inetpub\api');
?>
Import of an XMI file, and generation of PHP code templates
<?php
require_once 'PHP/UML.php';
$uml = new PHP_UML();
$uml->setInput('foo.xmi');
$uml->setImporter(new PHP_UML_Input_XMI_FileScanner()); // by default, PHP_UML uses a PHP_UML_Input_PHP_FileScanner
$uml->parse();
$exporter = new PHP_UML_Output_Php_Exporter();
$exporter->setModel($uml->getModel());
$exporter->export('./');
?>
Parsing of a PHP directory, followed by an XMI generation, without relying on a PHP_UML object
<?php
require_once 'PHP/UML.php';
$importer = new PHP_UML_Input_PHP_FileScanner();
$importer->setDirectories(array('somewhere/'));
$importer->import();
$exporter = new PHP_UML_Output_Xmi_Exporter();
$exporter->setModel($importer->getModel());
$exporter->setXmiVersion(1);
$exporter->setEncoding('utf-8');
$exporter->setDeploymentView(true);
$exporter->setComponentView(true);
$exporter->export('somewhere/else/');
?>
Note how the model is transfered from the importer object to the exporter object, with the
methods getModel()
and setModel()
.
You can use the factory method PHP_UML_Output_Exporter::getInstance($format)
to get an exporter object given a format name,
instead of instantiating the objects by yourself.
As said in the Command Line section, PHP_UML converts programming data from one format to another format. Between the two, it builds a UML model.
At the current moment, PHP_UML can read XMI and PHP, and can generate XMI, PHP and HTML.
If you want to write your own input format, you
can program your own implementation
of PHP_UML_Input_ImporterFileScanner
.
If you want to write your own output format, you
can program your own implementation
of PHP_UML_Output_Exporter
(either by traversing the model, like the "HtmlNew" implementation does, either by XSLT applied on XMI, like the implementation "Html" does).
For a better understanding of the program's guts, have a look at its class diagram, available in the docs folder (PHP_UML_simplified_class_diagram.png).
If you are interested in the PHP_UML project, and want to participate, do not hesitate to contact me.
Provides a simple framework for creating a test application to automate testing of functions and classes.
PHPUnit provides a simple framework for creating a test suite to automate testing of functions and classes. PHPUnit is inspired by JUnit which was created by Kent Beck and Erich Gamma as a tool for eXtreme Programming. One of the rules of XP is to test small software components as often and early as possible, this way you will not have to fix bugs and errors in the API while setting up and testing larger applications which depend on the class. While unit testing is one of the fundimental rules in XP, you don't have to switch to XP to benefit from PHPUnit. PHPUnit stands alone as a good tool for testing classes or a set of functions and will ease your development cycle and help you to avoid endless debug sessions.
Normally, you would write a class, do some unsystematic tests using echo() or var_dump(). After this, you use the class in your application and hope everything is ok. To benefit from PHPUnit you should rethink the flow. The best way is to do this:
1. design your class/API
2. create a test suite
3. implement the class/API
4. run the test suite
5. fix failures or errors and go to #4 again
It may seem that this will require a lot of time, but this impression is wrong. Creating the test suite using PHPUnit needs only a few minutes and running the test suite only seconds.
Let's start with a small example: a string class. First we create a bunch of functions declarations to work on a string:
---- string.php ----
<?php
class String
{
//contains the internal data
var $data;
// constructor
function String($data) {
$this->data = $data;
}
// creates a deep copy of the string object
function copy() {
}
// adds another string object to this class
function add($string) {
}
// returns the formated string
function toString($format) {
}
}
?>
Now we can create a test suite, which checks every function of your string class. A test suite is normal PHP class inherited from PHPUnit_TestCase containing test functions, identified by a leading 'test' in the function name. In the test function an expected value has to be compared with the result of the function to test. The result of this compare must delegate to a function of the assert*()-family, which decides if a function passes or fails the test.
---- testcase.php ----
<?php
require_once 'string.php';
require_once 'PHPUnit.php';
class StringTest extends PHPUnit_TestCase
{
// contains the object handle of the string class
var $abc;
// constructor of the test suite
function StringTest($name) {
$this->PHPUnit_TestCase($name);
}
// called before the test functions will be executed
// this function is defined in PHPUnit_TestCase and overwritten
// here
function setUp() {
// create a new instance of String with the
// string 'abc'
$this->abc = new String("abc");
}
// called after the test functions are executed
// this function is defined in PHPUnit_TestCase and overwritten
// here
function tearDown() {
// delete your instance
unset($this->abc);
}
// test the toString function
function testToString() {
$result = $this->abc->toString('contains %s');
$expected = 'contains abc';
$this->assertTrue($result == $expected);
}
// test the copy function
function testCopy() {
$abc2 = $this->abc->copy();
$this->assertEquals($abc2, $this->abc);
}
// test the add function
function testAdd() {
$abc2 = new String('123');
$this->abc->add($abc2);
$result = $this->abc->toString("%s");
$expected = "abc123";
$this->assertTrue($result == $expected);
}
}
?>
Now, we can run a first test. Make sure that all the paths are correct and then execute this PHP program.
---- stringtest.php ----
<?php
require_once 'testcase.php';
require_once 'PHPUnit.php';
$suite = new PHPUnit_TestSuite("StringTest");
$result = PHPUnit::run($suite);
echo $result -> toString();
?>
If you call this script from the commandline, you will get the following output:
Every function fails the test, because your string functions didn't returned what we defined as the expected value.
If you want to call the script through your browser, you have to put the script in a correct html page and call $result->toHTML () instead of $result->toString().
Ok, let's start with implementation of the our string class.
---- string.php ----
<?php
class String
{
//contains the internal data
var $data;
// constructor
function String($data) {
$this->data = $data;
}
// creates a deep copy of the string object
function copy() {
$ret = new String($this->data);
return $ret;
}
// adds another string object to this class
function add($string) {
$this->data = $this->data.$string->toString("%ss");
}
// returns the formated string
function toString($format) {
$ret = sprintf($format, $this->data);
return $ret;
}
}
?>
The implementation is complete and we can run the test again:
~>php -f stringtest.php
TestCase stringtest->testtostring() passed TestCase stringtest->testcopy() passed TestCase stringtest->testadd() failed: expected true, actual false
D'oh! the last test failed! We made a typing mistake. Change
line 16 in string.php
to
<?php
$this->data = $this->data.$string->toString("%s");
?>
and run the test again:
~>php -f stringtest.php
TestCase stringtest->testtostring() passed TestCase stringtest->testcopy() passed TestCase stringtest->testadd() passed
Everything is now OK!
Does it seem like a lot of work for testing three simple functions? Don't forget, this is a small example. Think about bigger, more complex API's like database abstraction or basket classes in a shop application. PHPUnit is an excellent tool to detect errors in the implementation of your class.
Often you will want to reimplement or refactor a large class which is used in several different applications. Without a test suite the likeliness of you breaking something in one of the applications that depends on your class is very high. Thanks to unit tests, you can create a test suite for your class, and then reimplement your class with the security of knowing that as long as the new class passes the tests, applications that depend on the class will work.
object PHPUnit::run (
object $suite
)
Executes the given test suite
object $suite
- an object of PHPUnit_TestSuite
&PHPUnit_Result
- A reference to a
PHPUnit_Result.
Using PHPUnit::run()
<?php
require_once "PHPUnit.php";
$suite = New PHPUnit_TestSuite('Mathtest');
$result = PHPUnit::run($suite);
echo $result->toHtml();
?>
1) Implement a subclass of PHPUnit_TestCase.
2) Define instance variables that store the state of the fixture.
3) Initialize the fixture state by overriding setUp().
4) Clean-up after a test by overriding tearDown().
Each test runs in its own fixture so there can be no side effects among test runs.
<?php
class MathTest extends PHPUnit_TestCase {
var $fValue1;
var $fValue2;
function MathTest($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->fValue1 = 2;
$this->fValue2 = 3;
}
}
?>
For each test implement a method which interacts with the fixture. Verify the expected results with assertions specified by calling assert with a boolean.
<?php
function testPass() {
$this->assertTrue($this->fValue1 + $this->fValue2 == 5);
}
?>
PHPUnit is a family of PEAR packages (PHPUnit2 for PHP 5, PHPUnit for PHP 4) that supports the development of object-oriented PHP applications using the concepts and methods of Agile Software Development, Extreme Programming, Test-Driven Development and Design-by-Contract Development by providing an elegant and robust framework for the creation, execution and analysis of Unit Tests.
You can find more information about this package at the following url PHPUnit wiki
Tools to ensure the quality of something.
Documentation coverage analysis for the PEAR packages documentation.
Daily updated pear documentation stats can be found at pear.cweiske.de/coverage/.
A great library is almost useless without documentation. PEAR has over 300 packages, automatically generated API documentation and a manual. The PEAR manual (the one you are reading) shall provide an overview on the packages and instructions about how to use them, and give an understanding about the classes in a package, and their methods. Further information about them can looked up in the API docs.
With the amount of packages PEAR provides, it's hard to keep track of the state of documentation of the packages - a tool to track this was needed. QA_Peardoc_Coverage is its incarnation.
QA_Peardoc_Coverage uses a SVN checkout of PEAR and PEARDOC to find out which packages are documented, which classes and method are mentioned in the docs, and which developers do a good job in documenting their code.
The documentation analysis report generation is split into two processes:
Collect data and save it
Generate different reports from saved data
This was chosen because the data collection step takes a long time (~ 1 minute), but it's needed for every single report. So if you want to generate the simple doc coverage, and the extended one, you need the same base data - you just display it in a different way.
generateCoverage() in QA_Peardoc_Coverage generates and returns the array with the computed coverage data. It can be serialized and saved, since all the Renderer classes use it as input.
Classes implementing the QA_Peardoc_Coverage_Renderer interface provide a render() method that takes the data returned by generateCoverage() and return the analysis, most times a string containing HTML code.
Make sure you have a fresh checkout of pear
and peardoc
directories from
cvs.php.net.
Configure peardoc
to generate the
manual.xml
in there (
autoconf, ./configure).
Call genData.php
with the path to
peardoc's manual.xml
as first, and
path to the pear cvs directory as second parameter.
A file called doc.dat
will be generated
in the current working directory.
Once the coverage data is generated and serialized, you can generate some HTML reports. For this, the packages provides four Renderer classes that all implement the QA_Peardoc_Coverage_Renderer interface:
QA_Peardoc_Coverage_Renderer_DeveloperList lists all developers with the number of packages they have, the number of undocumented packages and lists the undocumented ones. The list is sorted by percentage of documented packages.
QA_Peardoc_Coverage_Renderer_SimplePackageList lists all categories in PEAR, the packages in them and shows which of them have docs and which not. A final summary with the total number of existing and documented packages, and a percentage number is also given.
QA_Peardoc_Coverage_Renderer_ExtendetPackageList is similar to the simple list, but also shows the package's classes, their documentation state and their methods and their doc state. This list can be used to check which methods aren't yet covered by your documentation.
QA_Peardoc_Coverage_Renderer_MissingDocsPerDeveloper generates an array of developer email => array(package names) assignments that can be used as base data to send reminder emails.
All the renderers have a examples/gen*.php
file that
uses the doc.dat
file written by
examples/genData.php
. They echo the html to the console,
so you should redirect it into a file.
Simple little tool that walks over a checkout of
PEAR's SVN directory (pear
)
and reports the following issues:
Version of package.xml
If the version in package.xml
is missing
Package stability
Last release date
Time since the last release
After listing this stats for each single package, a summary of percentages is given.
Daily updated package stats can be found at pear.cweiske.de/packagestatus/.
Provides Packages for streams API. For more information on stream wrappers see the PHP Manual.
Allows stream based access to variables.
Stream_Var supplies a class that can be used as a wrapper for PHP's stream functions. This allows you to access variables with fopen(), fclose(), fwrite(), fread(), opendir() and all other filesystem functions.
You may use stream_wrapper_register() to register Stream_Var as a wrapper.
Scalar variables (strings, integers, floats) are treated as files, while arrays represent directories.
Please see the PHP Manual or the example for more information on how to register streams.
Stream_Var could be used in various scenarios. Imagine you are using a class that reads data from a file but you are creating the data on-the-fly and do not want to save the data before it can be processed by the class.
This is where Stream_Var can be used. Just register it as a wrapper
for fopen() and pass var://GLOBAL/yourVar
as a parameter to the class.
It will read from the variable as if it was a file.
The following example shows you how to register Stream_Var as a wrapper for the stream functions.
Registering Stream_Var
<?php
require_once "Stream/Var.php";
stream_wrapper_register( "var", "Stream_Var" );
?>
The following example show you how to access scalar variables with fopen(), fread(), frwite() and fclose().
Accessing scalar variables
<?php
require_once "Stream/Var.php";
stream_wrapper_register( "var", "Stream_Var" );
$foo = "I really like tomatoes.";
echo "Content of foo: $foo<br />";
$fp = fopen('var://GLOBALS/foo','r+');
$data = fread($fp, 9);
echo "Read from stream: $data<br />";
fwrite($fp,"hate");
fclose($fp);
echo "Content of foo: $foo<br />";
?>
The following example shows how to use opendir() to access an array.
Accessing an array
<?php
require_once "Stream/Var.php";
stream_wrapper_register( "var", "Stream_Var" );
$dirname = 'var://_SERVER';
$dir = opendir($dirname);
echo "<strong>opening directory '$dirname'</strong><br><br>";
while ($entry = readdir($dir)) {
echo "opening file $dirname/$entry<br />";
if (!$fp = @fopen($dirname."/".$entry,"r")) {
echo "seems to be a directory<br /><br />";
continue;
}
echo "reading from $entry<br />";
while (!feof($fp)) {
echo fread($fp, 16);
}
fclose($fp);
echo "<br /><br />";
}
closedir($dir);
?>
If you want to take a look at some more examples, just install the package and they will be installed in the docs directory.
Provides structures-related Packages
Parsing BibTex Data to an array and exporting to BibTex and RTF.
This package provides methods to access information stored in a BibTex file. During parsing it is possible to let the data be validated. In addition. the creation of BibTex Strings as well as RTF Strings is also supported.
Loading a BibTex File and printing the parsed array
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$ret = $bibtex->loadFile('foo.bib');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
$bibtex->parse();
echo '<pre>';
print_r($bibtex->data);
echo '</pre>';
?>
Options can be set either in the constructor or with the method setOption(). When setting in the constructor the options are given in an associative array. The options are:
stripDelimiter (default: true)
Stripping the delimiter surrounding the entries.
validate (default: true)
Validation while parsing.
unwrap (default: false)
Unwrapping entries while parsing.
wordWrapWidth (default: false)
If set to a number higher one that the entries are wrapped after that amount of characters.
wordWrapBreak (default: \n)
String used to break the line (attached to the line).
wordWrapCut (default: 0)
If set to zero the line will we wrapped at the next possible space, if set to one the line will be wrapped exactly after the given amount of characters.
removeCurlyBraces (default: false)
If set to true Curly Braces will be removed.
Example of setting options in the constructor:
Setting options in the constructor
<?php
$bibtex = new Structures_BibTex(array('validate'=>false, 'unwrap'=>true));
?>
Example of setting options using the method setOption():
Setting options using setOption
<?php
$bibtex = new Structures_BibTex();
$bibtex->setOption('validate', false);
$bibtex->setOption('unwrap', true);
?>
The data is stored in the class variable data
. This is a
a list where each entry is a hash table representing one bibtex-entry. The keys
of the hash table correspond to the keys used in bibtex and the values are the
corresponding values. Some of these keys are:
cite
- The key used in a LaTeX source to do the citing.
entryType
- The type of the entry, like techreport, book and so on.
author
- One or more authors of the entry. This entry is also a list with hash tables representing the authors as entries. The author has table is explained later.
title
- Title of the entry.
As described before the authors are stored in a list. Every entry representing one author as a has table. The hash table consits of four keys: first, von, last and jr. The keys are explained in the following list:
first
- The first name of the author.
von
- Some names have a 'von' part in their name. This is usually a sign of nobleness.
last
- The last name of the author.
jr
- Sometimes a author is the son of his father and has the same name, then the value would be jr. The same is true for the value sen but vice versa.
To add an entry simply create a hash table with the needed keys and values and call the method addEntry().
Adding an entry
<?php
$bibtex = new Structures_BibTex();
$addarray = array();
$addarray['entryType'] = 'Article';
$addarray['cite'] = 'art2';
$addarray['title'] = 'Titel of the Article';
$addarray['author'][0]['first'] = 'John';
$addarray['author'][0]['last'] = 'Doe';
$addarray['author'][1]['first'] = 'Jane';
$addarray['author'][1]['last'] = 'Doe';
$bibtex->addEntry($addarray);
?>
The class Structures_BibTex introduces a system to collect warnings that may happen during parsing. Warnings are things in the BibTex source which are not correct but do not cause the parser to fail. One example would be a double cite entry. These warnings should help to improve the quality of your BibTex code. Whether warnings are generated or not is controlled by the option validate. Per default warnings are generated. If you want to not generate warnings you should use the setOption() method like this.
Switching off creation of warnings
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$bibtex->setOption('validate', false);
?>
The warnings are stored in an array called warnings which is public accessible. To check is a warning exists you can use the method hasWarning(). This method returns true if there are warnings and false otherwise.
Checking for warnings
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$ret = $bibtex->loadFile('foo.bib');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
$bibtex->parse();
if ($bibtex->hasWarning()) {
print 'There are warnings!<br />';
}
?>
Every warning itself is hash table with the following keys:
warning
- Type of the warning
entry
- The line that caused the warning
wholeentry
- The whole entry in which the warning occurred
To print every warning with type and line that caused the warning you could do something like this:
Checking for warnings
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$ret = $bibtex->loadFile('foo.bib');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
$bibtex->parse();
if ($bibtex->hasWarning()) {
foreach ($bibtex->warnings as $warning) {
echo 'Warning: '.$warning['warning'].'<br />';
echo 'Line: '.$warning['entry'].'<hr />';
}
}
?>
Finally if you want to clear all warnings you can use the method clearWarnings().
The following Warnings are known:
WARNING_MISSING_END_BRACE
- This warning is generated when the end brace in an entry is missing. Take this warning seriously!
WARNING_AT_IN_BRACES
- A Value is delimited by Braces. Then inside a @ is not allowed.
WARNING_ESCAPED_DOUBLE_QUOTE_INSIDE_DOUBLE_QUOTES
- A Value is delimited by double quotes. Then inside no escaped double quote is allowed.
WARNING_UNBALANCED_AMOUNT_OF_BRACES
- The amount of braces inside a value is not equal (opening and closing). The parses fails if in the complete entry this amount is not correct. But if only on an entry it is not correct then this is only a warning. As a matter of fact of the parser does not fail but there is an unbalanced amount of braces in an entry this warning has to be generated something times two.
WARNING_MULTIPLE_ENTRIES
- Every entry is identified by a unique string. This warning is created if there are at least two entries with the same identification.
WARNING_LINE_WAS_NOT_CONVERTED
- This warning is created if during exporting (for example in RTF or HTML) one entry was ignored because of no matching data in the entry. At least one of the following entries have to exist in the entry to get converted: title, journal, year or authors.
STRING_ENTRY_NOT_YET_SUPPORTED
- BibTex defines some special entry types, String is one of them and is used to define abbreviations. This is not yet supported by Structres_BibTex.
PREAMBLE_ENTRY_NOT_YET_SUPPORTED
- BibTex defines some special entry types, Preamble is one of them and is used to define formatted code. This is not yet supported by Structres_BibTex.
WARNING_NOT_ALLOWED_ENTRY_TYPE
- BibTex allows to define own types. This warning is genereated when a type was detected which is not part of the standard types. The allowed or standard type are defined in the class variable allowedTypes as array.
The class Structures_BibTex provides some methods to export the data stored in the class. Currently the class exports the data in the following formats:
BibTeX
- The whole data in BibTeX
RTF
- The data with enough matching data as a short list in RTF format
HTML
- The data with enough matching data as a short list in HTML format
The default is to export the name of the author in this format: "VON LAST, JR, FIRST". The corresponding placeholder will be substituted. The placeholders have to be uppercase. The format string is defined in the class variable authorstring. Changing the ouput of the author in the entries to this format "FIRST LAST" can be done like this:
Changing author format
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$bibtex->authorstring = 'FIRST LAST';
?>
One of the basic features is of course the export in BibTeX format. This is simply done by invoking the bibTex() method.
Exporting in BibTeX format
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$ret = $bibtex->loadFile('foo.bib');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
$bibtex->parse();
echo 'The data in BibTeX format:<br />';
echo $bibtex->bibTex();
?>
This feature was introduced to enable some kind of import into Word. Word (of course also Open Office or kword) understands the RTF format. It is simply possible to save the output as 'somefile.rtf' and be opened in Word. This will satisfy the Windows users. To use it simply call the method rtf().
Exporting in RTF format
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$ret = $bibtex->loadFile('foo.bib');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
$bibtex->parse();
echo 'The data in RTF format:<br />';
echo $bibtex->rtf();
?>
The default format for every entry is first the authors, then the title bold and in double quotes, then the journal italic and finally the year. To change the default format you should override the class variable rtfstring. The default rtfstring looks like this: 'AUTHORS, "{\b TITLE}", {\i JOURNAL}, YEAR'. The string AUTHORS, TITLE, JOURNAL and YEAR are substituted with the corresponding values.
Exporting in RTF format with different RTF string
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$ret = $bibtex->loadFile('foo.bib');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
$bibtex->parse();
$bibtex->rtfstring = 'AUTHORS, "TITLE", JOURNAL, YEAR';
echo 'The data in RTF format (but this time plain):<br />';
echo $bibtex->rtf();
?>
This feature is just a simple HTML generation. The default formatting is the same as in rtf. To use it call the method html().
Exporting in HTML format
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$ret = $bibtex->loadFile('foo.bib');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
$bibtex->parse();
echo 'The data in HTML format:<br />';
echo $bibtex->html();
?>
As with the RTF export it is possible to override the default HTML string.The default string is stored in the class variable htmlstring and looks like this: AUTHORS, "<strong>TITLE</strong>", <em>JOURNAL</em>, YEAR<br />.
Exporting in HTML format with different HTML string
<?php
require_once 'Structures/BibTex.php';
$bibtex = new Structures_BibTex();
$ret = $bibtex->loadFile('foo.bib');
if (PEAR::isError($ret)) {
die($ret->getMessage());
}
$bibtex->parse();
$bibtex->htmlstring = 'AUTHORS, "TITLE", JOURNAL, YEAR';
echo 'The data in HTML format (but this time plain):<br />';
echo $bibtex->html();
?>
On this page miscellaneous methods of the class Structures_BibTex are described. These methods include:
amount
- The amount of BibTex entries
getStatistic
- Statistics about BibTex entries
This method simply returns the amount of BibTex entries which are currently stored in the class. The value is returned as Integer value.
This methods returns an array with a statistic about all error types together with the amount. The keys are the types and the values the amount in integer.
Structures_DataGrid is a class for building, manipulating and rendering a tabular structure of data. It has the ability to allow you to render a datagrid in HTML format as well as many other formats such as an XML Document, an Excel Spreadsheet, an XUL Document and more.
It also offers paging and sorting functionality to limit the data that is presented. This concept is based on the .NET Framework DataGrid control and works very well with database and XML result sets.
Structures_DataGrid is a package with the purpose
of rendering a data set into a tabular structure in a specific output
format. Possible output formats are (X)HTML,
XML, XUL, Excel
.xls
spreadsheets, CSV, or
console tables.
The input data format is independent of the underlying data storage layer: It does not matter if the data has been selected from a database, has been harvested from plain text files or converted from a web service call. The data can be sorted and paged, and each cell of the table can have a custom look by using CSS for the HTML output.
So to begin, you will want to find a datasource, commonly you might use a Database like MySQL or an XML document. You can then easily bind this datasource to the datagrid, to do so you have 3 options. The first, is to fetch your data into a 2 dimension array and pass it into the bind method. The second way is to bind a record set that is not already an array, such as a DB_DataObject or a DB_Result object. To do so, you can use the bindDataSource method by creating a DataSource object. The third way is to loop through your record set and add each record individually, this practice is the least efficient.
If you bind the datagrid to a datasource that is an object such as a DB_DataObject, you do not need to fetch the data as the DataGrid will handle that for you.
Once your datagrid has been populated with your records, you have many options on how to manipulate what is to be shown. You can sort the data, choose to show only a certain amount of data per page and also choose what format the data should be rendered into. You can render the datagrid in many formats including HTML.
This document is based on questions asked on PEAR general mailing list and other mailing lists and forums.
The setRequestPrefix() method is the solution for this problem. Each DataGrid for the page needs such a prefix that is internally used before the GET parameters for sorting and paging. An example of the usage:
<?php
require_once 'Structures/DataGrid.php';
$datagrid1 = new Structures_DataGrid();
$datagrid2 = new Structures_DataGrid();
$datagrid1->setRequestPrefix('trade_');
$datagrid2->setRequestPrefix('stock_');
$datagrid1->bind('SELECT * FROM trade', array('dsn' => DSN));
$datagrid2->bind('SELECT * FROM stock', array('dsn' => DSN));
$datagrid1->render();
$datagrid2->render();
?>
You need to call setRequestPrefix() before calling bind().
Currently there are five DataSource drivers that are recommended in the sense of efficiency:
DB_DataObject
DB_Table
DBQuery
MDB2
PDO
These four drivers will only fetch the needed records from the database. For example, if you have a row limit of 15 records per page, they will only fetch (up to) 15 records.
All other DataSource drivers can, of course, also be used. But there is no logic implemented (better said: implementable) to avoid fetching (or keeping in memory) unneeded records.
You need a formatter for the new column that should hold the row number. The first parameter that is passed to such a formatter function contains a currRow value with the row number per page. For calculating the row number relative to the whole table, you need to take also the getCurrentRecordNumberStart() method into account.
The following code snippet shows you how to define the formatter function and how to add the column (with # as the column label and right aligned values):
<?php
function formatRowNumber($params, $recordNumberStart)
{
return $params['currRow'] + $recordNumberStart;
}
$datagrid->addColumn(
new Structures_DataGrid_Column(
'#',
null,
null,
array('style' => 'text-align: right;'),
null,
'formatRowNumber',
$datagrid->getCurrentRecordNumberStart()
));
?>
Instead of using an encoding like ISO-8859-15, you need to use Windows-1252.
Streaming support in Structures_DataGrid is intended to be used with large datasets. But it can also be used with very small datasets without loss of performance.
As always, there is an exception to this rule: When you're using one of the DataSource drivers that fetch data from a database and you have queries that need a lot of time for computation of the results, you should not use streaming, as running such a complex query multiple times will need even more time, of course.
If you just want to output the HTML code, use the following line of code:
<?php
$datagrid->render()
?>
If you want to get the generated HTML code, e.g. for using it within a template, use the following line of code:
<?php
$html = $datagrid->getOutput();
?>
Don't forget to pass the number of rows per page to the constructor:
<?php
$datagrid =& new Structures_DataGrid(10); // 10 rows per page
?>
This is caused by MDB2's portability settings that are enabled by default. The MDB2_PORTABILITY_FIX_CASE setting is set to CASE_LOWER, resulting in lowercased letters for all column names. Disable this or all portability settings of MDB2 to avoid sorting problem with Structures_DataGrid.
If you are not familiar with PEAR, we recommend that you first read the PEAR general installation instructions
Structures_DataGrid uses drivers that are provided separately. It means that installing the core package named Structures_DataGrid is not enough to get something working.
You will at least need one DataSource driver, for handling the input, and one Rendering driver (or Renderer), for the output.
The following commands will install the core package, as well as the MDB2 DataSource (for binding SQL queries), HTML_Table Renderer and the Pager renderer (for paging links). That should cover your most common needs.
$ pear install -o Structures_DataGrid $ pear install -o Structures_DataGrid_DataSource_MDB2 $ pear install -o Structures_DataGrid_Renderer_HTMLTable $ pear install -o Structures_DataGrid_Renderer_Pager
If you get an error message like Failed to download pear/Structures_DataGrid within preferred state "stable", latest release is [...], you can simply append
-beta
to the package name, for example:$ pear install -o Structures_DataGrid-beta
Of course, you may want to use other input/output formats, such as XML, MS Excel, DB_DataObject, XUL, etc... Just install the corresponding drivers.
It is possible to install all DataSource drivers or all Renderer drivers at once. The following two commands will install Structures_DataGrid and either all DataSource or all Renderer drivers at once.
$ pear install Structures_DataGrid-beta#datasources $ pear install Structures_DataGrid-beta#renderers
Please note that the individual drivers will be installed only if the requirements are fulfilled. That means that the MDB2 DataSource won't be installed if you don't have MDB2 installed. The same holds for the Excel Renderer if you don't have Spreadsheet_Excel_Writer installed.
Of course, the same holds for the uninstallation. For example:
$ pear uninstall Structures_DataGrid-beta#datasources $ pear uninstall Structures_DataGrid-beta#renderers
A DataSource has a rather self-explanatory name; however, a DataSource driver in context to the DataGrid can become a very essential key to your software. A DataSource driver will interact with your data source directly, such as a DB_DataObject or a MDB2 query and handle all of the paging and sorting code for you, resulting in very few lines of code that you will need to write.
There are two methods intended to be used for binding a DataSource driver: bind() and bindDataSource().
The bind() method is able to autodetect the right driver
in many cases. For example, you can pass DB_Table
or DB_DataObject
instances to it. Strings can't be autodetected because they could contain
CSV or XML data, for example. In such cases, you can specify the type as
the third parameter in the bind() method call (e.g.
'CSV'
or 'XML'
).
If you are building your own custom DataSource driver, using bindDataSource() is the method of choice. Just instantiate your DataSource class and pass this instance to the bindDataSource() method.
A list with the currently available DataSources can be found on the overview page.
Please also note the following FAQ entry: Which DataSource drivers are recommended?
The DataGrid offers the ability to build out your data in a grid format in an HTML table, which is the most common method due to the nature of PHP. However, the DataGrid offers many other ways of outputting the tabular data structure such as an Excel spreadsheet or an XML document.
To use a different renderer other than the HTML_Table renderer, you can
specify the name of the renderer (e.g. 'CSV'
,
'Pager'
or 'XML'
) as the first
parameter of the fill(),
getOutput(),
and render()
methods. Another possibility is to use setRenderer()
method. The old way of using the third parameter of the constructor to
define the renderer is deprecated since version 0.7.0.
A list with the currently available renderers can be found on the overview page.
Large recordsets can exceed PHP's memory limit. Structures_DataGrid offers streaming support with many DataSource drivers and some Renderer drivers to avoid problems with the memory limit.
Streaming can be enabled by calling the enableStreaming() method of the Structures_DataGrid object. An optimal parameter allows to set the number of records that should be read and written on each stream iteration. The default buffer size is 500 records.
Once streaming is enabled, only up to the number of records specified in the buffer size records will be fetched and rendered. This will be repeated until this was done for all records.
Enabling streaming with buffer size of 1,000 records
<?php
require 'Structures/DataGrid.php';
// Instantiate the DataGrid
$datagrid =& new Structures_DataGrid();
// Enable streaming and set buffer size to 1,000 records
$datagrid->enableStreaming(1000);
// ... e.g. bind(), render() calls as usual
?>
Although streaming can be used with every DataSource driver, only the following drivers support streaming in a meaningful way: DataObject, DBQuery, DB_Table and MDB2. Streaming support is planned to be added also to the CSV and XML DataSources.
Streaming support is also compatible with every Renderer driver but can currently be used only with CSV and XML Renderers in a meaningful way. Other Renderers might be rewritten in the future for streaming support.
The column formatter method can be a very powerful solution to a very common need. The need is to customize the output for a cell in the grid such as a link for a form element. This can be easily done by specifying a "callback" function. This function will then return the string that is needed to be printed.
Using the column formatter
<?php
require_once 'Structures/DataGrid.php';
$dg =& new Structures_DataGrid();
$result = $dg->bind('http://pear.php.net/feeds/pkg_structures_datagrid.rss');
if (PEAR::isError($result)) {
die('An error occured while fetching the RSS information.');
}
$dg->addColumn(
new Structures_DataGrid_Column('Release', 'title', 'title',
null, null, 'printLink')
);
$dg->addColumn(
new Structures_DataGrid_Column('Description', 'description',
'description', null, null,
'printDesc', array('length' => 15))
);
$dg->addColumn(
new Structures_DataGrid_Column('Date', 'dc:date', 'dc:date')
);
$dg->render();
function printLink($params, $args = array())
{
extract($params);
extract($args);
return '<a href="' . $record['link'] . '">' . $record['title'] . '</a>';
}
function printDesc($params, $args = array())
{
extract($params);
extract($args);
if (strlen($record[$fieldName]) > $length) {
return nl2br(substr($record[$fieldName], 0, $length)) . '...';
} else {
return nl2br($record[$fieldName]);
}
}
?>
Each callback function needs to accept at least one parameter
($params
in the example above). This parameter
will contain various information:
'record': An array containing the complete current record
'fieldName': The field name of the current column
'columnName': The column name of the current column
'orderBy': The 'orderBy' argument of the current column
'attribs': The attributes of the current column
'currRow': The number of the current row
'currCol': The number of the current column
An optional second parameter ($args
in the example)
allows you to pass additional information to the callback functions. In the
example above, an array with length information is passed when the callback
function printDesc() is called for the description
column. Please note that this second parameter is only passed when you
specify something in the column constructor. Therefore, it is a good
practice to use $args = array()
in the parameter list
of your callback functions.
To retrieve the data that the datagrid will display you can begin by passing a simple SQL statement to the bind() method.
Using an SQL query as datasource
<?php
require 'Structures/DataGrid.php';
// Instantiate the DataGrid
$datagrid =& new Structures_DataGrid();
// Setup your database connection
$options = array('dsn' => 'mysql://user:password@host/db_name');
// Bind a basic SQL statement as datasource
$test = $datagrid->bind('SELECT * FROM my_table', $options);
// Print binding error if any
if (PEAR::isError($test)) {
echo $test->getMessage();
}
// Print the DataGrid with the default renderer (HTML Table)
$test = $datagrid->render();
// Print rendering error if any
if (PEAR::isError($test)) {
echo $test->getMessage();
}
?>
If you are familiar with the SQL language you'll certainly find many ways to adapt the above example to your needs, using more complex queries.
You can also page through your dataset with the automatic paging feature as shown below. This feature transparently adds LIMIT clauses to your SQL statement, providing optimized database access.
Automatic paging
<?php
require 'Structures/DataGrid.php';
// 10 records per page
$datagrid =& new Structures_DataGrid(10);
// Setup your datasource
$options = array('dsn' => 'mysql://user:password@host/db_name');
$test = $datagrid->bind("SELECT * FROM my_table", $options);
if (PEAR::isError($test)) {
echo $test->getMessage();
}
// Print the DataGrid with the default renderer (HTML Table)
$test = $datagrid->render();
if (PEAR::isError($test)) {
echo $test->getMessage();
}
// Print the HTML paging links
$test = $datagrid->render(DATAGRID_RENDER_PAGER);
if (PEAR::isError($test)) {
echo $test->getMessage();
}
?>
This example will show you how to create an interface to a User Management System using the DB_DataObject package to handle the database aspects of this example.
User Management System Example
<?php
require_once 'Structures/DataGrid.php';
require_once 'HTML/Table.php';
require_once 'myclasses/User.php';
// Instantiate the DataObject; that's our DataSource container
$user = new User_DataObject();
// Create the DataGrid
$datagrid =& new Structures_DataGrid(20); // Display 20 records per page
// Specify how the DataGrid should be sorted by default
$datagrid->setDefaultSort(array('lname' => 'ASC'));
// Bind the DataSource container
$test = $datagrid->bind($user);
if (PEAR::isError($test)) {
echo $test->getMessage();
}
// Define columns
$datagrid->addColumn(new Structures_DataGrid_Column(null, null, null, array('width' => '10'), null, 'printCheckbox()'));
$datagrid->addColumn(new Structures_DataGrid_Column('Name', null, 'lname', array('width' => '40%'), null, 'printFullName()'));
$datagrid->addColumn(new Structures_DataGrid_Column('Username', 'username', 'username', array('width' => '20%')));
$datagrid->addColumn(new Structures_DataGrid_Column('Role', null, null, array('width' => '20%'), null, 'printRoleSelector()'));
$datagrid->addColumn(new Structures_DataGrid_Column('Edit', null, null, array('width' => '20%'), null, 'printEditLink()'));
// Define the Look and Feel
$tableAttribs = array(
'width' => '100%',
'cellspacing' => '0',
'cellpadding' => '4',
'class' => 'datagrid'
);
$headerAttribs = array(
'bgcolor' => '#CCCCCC'
);
$evenRowAttribs = array(
'bgcolor' => '#FFFFFF'
);
$oddRowAttribs = array(
'bgcolor' => '#EEEEEE'
);
$rendererOptions = array(
'sortIconASC' => '⇑',
'sortIconDESC' => '⇓'
);
// Create a HTML_Table
$table = new HTML_Table($tableAttribs);
$tableHeader =& $table->getHeader();
$tableBody =& $table->getBody();
// Ask the DataGrid to fill the HTML_Table with data, using rendering options
$test = $datagrid->fill($table, $rendererOptions);
if (PEAR::isError($test)) {
echo $test->getMessage();
}
// Set attributes for the header row
$tableHeader->setRowAttributes(0, $headerAttribs);
// Set alternating row attributes
$tableBody->altRowAttributes(0, $evenRowAttribs, $oddRowAttribs, true);
// Output table and paging links
echo $table->toHtml();
// Display paging links
$test = $datagrid->render(DATAGRID_RENDER_PAGER);
if (PEAR::isError($test)) {
echo $test->getMessage();
}
function printCheckbox($params)
{
extract($params);
return '<input type="checkbox" name="idList[]" value="' . $record['id'] . '">';
}
function printFullName($params)
{
extract($params);
return $record['fname'] . ' ' . $record['lname'];
}
function printRoleSelector($params)
{
global $roleList;
extract($params);
$html = '<select name="role_id">';
foreach ($roleList as $roleId => $roleName) {
$html .= "<option value=\"$roleId\">$roleName</option>\n";
}
$html .= '</select>';
return $html;
}
function printEditLink($params)
{
extract($params);
return '<a href="edit.php?id=' . $record['id'] . '">Edit</a>';
}
?>
Writing your own DataSource driver is the way to go when none of the existing driver suit your needs. It is actually pretty easy, and allows for great flexibility.
Of course, if you're trying to fetch data from an exotic source, writing your own driver is required. But, sometimes it's also the best way to achieve the best optimization, especially (but not only) with databases.
This document will present you the DataSource interface, and how to implement it.
A DataSource driver is a descendent of the Structures_DataGrid_DataSource class, which implements the DataSource interface.
DataSource is a synomym for DataSource driver.
The DataSource interface consists in a set of methods that drivers must or may overload and protected properties that drivers can use, as well as recommended practices.
A DataSource container is a constant or a variable of any type (string, array, object, etc...) that either contains data or describes how to retrieve data.
Every DataSource driver is specific to, and knows how to handle, a given DataSource container type.
array $_options
- Data binding options as an
associative array. You can read the content of this property but you
shouldn't change it directly.
constructor (
void
)
object bind (
mixed container
, array options
)
container
into the driver, according to some
binding options
. This method is optional.
It must return a PEAR_Error object in case
of failure.
object count (
void
)
object sort (
mixed sortSpec
, array sortDir
)
sortSpec
and the optional
sortDir
. This method is required, and is
always called before fetch(). It must return
a PEAR_Error object in case of failure.
mixed fetch (
integer offset
, integer len
)
offset
, containing
len
records..This method is required
It must return a PEAR_Error object in case of failure.
void _addDefaultOptions (
array options
)
void setOptions (
array options
)
Let's start with a very simple driver. It is rather readable and you shouldn't have much trouble understanding it. It is not extremely useful to write a custom driver for a such simple SQL query, but it should get you started.
A simple SQL adaptor
<?php
require 'Structures/DataGrid/DataSource.php';
require_once 'DB.php';
class MyDataSource extends Structures_DataGrid_DataSource {
var $db;
var $orderBy = '';
function MyDataSource() {
$dsn = 'mysql://someuser:apasswd@localhost/thedb';
$this->db =& DB::connect($dsn);
}
function count() {
$query = "SELECT COUNT(*) FROM animals WHERE species='cat'";
return $this->db->getOne($query);
}
function sort($sortSpec, $sortDir = 'ASC') {
$this->orderBy = "ORDER BY $sortSpec $sortDir";
}
function fetch($offset = 0, $len = null) {
$limit = is_null($len) ? "LIMIT $offset,18446744073709551615"
: "LIMIT $offset,$len";
$query = "SELECT * FROM animals WHERE species='cat' ";
$query .= $this->_orderBy . " $limit";
return $this->db->getAll($query);
}
}
?>
Before going live, it is very recommended to test your driver with the dump() method.
Testing with dump()
<?php
$datasource = new MyDataSource();
$count = $datasource->count();
echo "There are $count cats in the farm\n\n";
$datasource->sort('weight');
echo "Here are the 5 lightest ones: \n";
// dump() accepts the same $offset and $len argument as fetch()
$datasource->dump(0,5);
?>
That should output a nicely formated ascii table like:
There are 23 cats in the farm.
Here are the 5 lightest ones:
+---------+---------+-----------+--------+
| name | species | birthDate | weight |
+---------+---------+-----------+--------+
| sarge | cat | 20021220 | 1.8 |
| etch | cat | 20000509 | 2.5 |
| potato | cat | 19980128 | 3.8 |
| sid | cat | 20011101 | 4.1 |
| woody | cat | 19970712 | 6.0 |
+---------+---------+-----------+--------+
Okay, so you have written a driver that's tailored to your needs, and tested it. It is now time to connect it to Structures_DataGrid.
For this purpose we're going to use the bindDataSource() method.
Binding a custom datasource
<?php
$datagrid =& new Structures_DataGrid();
$datasource = new MyDataSource();
$datagrid->bindDataSource($datasource);
$datagrid->render();
?>
That should output a sortable HTML table.
Of course, the usual features of Structures_DataGrid are now available to you: paging, other output formats as XML, MS-Excel, etc...
Writing your own Renderer allows you to fine tune every aspect of Structures_DataGrid output.
You may need to output the datagrid in a completely unsupported format. Then writing your own Renderer is the only option
But you may also need a unsupported variant of an existing renderer, say, for example, a very specific HTML output. In this case, you generally have two options : either writing a Rendering driver from scratch, or customizing (subclassing) an existing one.
This document will present you the Rendering driver interface, and how to implement it. You will certainly see how flexible Structures_DataGrid can become with custom Renderers, but also how easy we have to tried to make this process.
A Rendering driver is a descendent of the Structures_DataGrid_Renderer class, which implements the Renderer interface.
Renderer is a synomym for Rendering driver.
The Renderer interface consists in a set of methods that drivers must or may overload and protected properties that drivers can use, as well as recommended practices.
A Rendering container is either an object that contain a sort of rendering engine, or a variable of any type (string, array, object, etc...) that can contain the output of a Renderer.
A Renderer with Container Support driver is specific to, and knows how to handle, a given type of Rendering container.
A driver is said to provide Container Support when it is able to handle a given type of Rendering container. Drivers with Container Support must implement the setContainer() and getContainer() public methods. They must be able to use a Rendering container provided by the user with setContainer(). They must also be able to create and intialize a Rendering container if the user does not provide any.
A driver is said to provide Output Buffering when it buffers the output that it generates, implements the flatten() method and is able to return the generated output from flatten().
A Direct Rendering driver is the simplest form of Renderer. It directs all of its output to standard output. By definition, a such driver neither provide Container support nor Output Buffering
All of the following properties are read-only : drivers can access their content but must not change it directly.
array $_options
-Common and driver-specific
options
array $_columns
- Columns fields names and
labels
int $_columnsNum
- Number of columns
array $_records
- Records content
int $_recordsNum
- Number of records
in the current page
int $_totalRecordsNum
- Total number of
records as reported by the datasource
int $_firstRecord
- First record number (starting from 1), in the current page
int $_lastRecord
- Last record number (starting from 1), in the current page
int $_page
- Current page number (starting
from 1)
int $_pageLimit
- Number of records per page
int $_pagesNum
- Number of pages
string $_requestPrefix
- GET/POST/Cookie
parameters prefix
array $_currentSort
- Fields/directions
the data is currently sorted by
array $_sortableFields
- Which fields the
datagrid may be sorted by
All of the following methods are optional.
constructor (
void
)
object setContainer (
mixed container
)
container
provided by the user. It must
return a PEAR_Error object in case of failure.
object getContainer (
void
)
void init (
void
)
void buildHeader (
array columns
)
$columns
is a convenient reference to the
$_columns
property.
void _addDefaultOptions (
array options
)
void setOptions (
array options
)
Soon
Soon
Soon
void constructor
Structures_DataGrid::Structures_DataGrid
(
string
$limit
= null
,
int
$page
= null
,
string
$rendererType
= null
)
Builds the DataGrid class. The Core functionality and Renderer are separated for maintainability and to keep cohesion high.
$limit
The number of records to display per page.
$page
The current page viewed. In most cases, this is useless. Note: if you specify this, the "page" GET variable will be ignored.
$rendererType
The type of renderer to use. You may prefer to use the $type argument of render() , fill() or getOutput()
throws no exceptions thrown
Instantiation
<?php
// Don't forget the ampersand
$datagrid =& new Structures_DataGrid();
?>
This function can not be called statically.
mixed
Structures_DataGrid::addColumn
(
&$column
,
string
$position = 'last'
,
string
$relativeTo
= null
,
object
$column
)
This package is not documented yet.
&$column
$position
One of: "last", "first", "after" or "before" (default: "last")
$relativeTo
The name (label) or field name of the relative column, if $position is "after" or "before"
$column
The Structures_DataGrid_Column object (reference to)
returns PEAR_Error on failure, void otherwise
throws no exceptions thrown
Adding a simple column
<?php
$datagrid =& new Structures_DataGrid();
$column = new Structures_DataGrid_Column('Title', 'title', 'title', array('align' => 'center'), 'N/A', null);
$datagrid->addColumn($column);
?>
This function can not be called statically.
mixed&
Structures_DataGrid::attachRenderer
(
&$renderer
,
object
$renderer
)
This package is not documented yet.
&$renderer
$renderer
Driver object, subclassing Structures_DataGrid_Renderer
returns Renderer instance or a PEAR_Error object
throws no exceptions thrown
This function can not be called statically.
bool
Structures_DataGrid::bind
(
mixed
$container
,
array
$options = array()
,
string
$type
= null
)
This package is not documented yet.
$container
The record set in any of the supported data source types
$options
Optional. The options to be used for the data source
$type
Optional. The data source type
returns True if successful, otherwise PEAR_Error.
throws no exceptions thrown
Bind an SQL query
<?php
// Setup your database connection
$options = array('dsn' => 'mysql://user:password@host/db_name');
// Bind a basic SQL statement as datasource
// Note: ORDER BY and LIMIT clause are automatically added
$test = $datagrid->bind('SELECT * FROM my_table', $options);
// Print binding error if any
if (PEAR::isError($test)) {
echo $test->getMessage();
}
?>
Bind a DB_DataObject
<?php
$person = new DataObjects_Person;
$person->hair = 'red';
$person->has_glasses = 1;
$datagrid->bind($person);
?>
This function can not be called statically.
mixed
Structures_DataGrid::bindDataSource
(
mixed
&$source
)
This package is not documented yet.
&$source
The data source driver object
returns True if successful, otherwise PEAR_Error
throws no exceptions thrown
This function can not be called statically.
mixed
Structures_DataGrid::build
(
)
This package is not documented yet.
returns Either true or a PEAR_Error object
throws no exceptions thrown
This function can not be called statically.
Structures_DataGrid_DataSource|PEAR_Error&
Structures_DataGrid::dataSourceFactory
(
mixed
$source
,
array
$options = array()
,
string
$type
= null
)
A clever method which loads and instantiate data source drivers.
Can be called in various ways:
Detect the source type and load the appropriate driver with default options:
<?php
$driver =& Structures_DataGrid::dataSourceFactory($source);
?>
Detect the source type and load the appropriate driver with custom options:
<?php
$driver =& Structures_DataGrid::dataSourceFactory($source, $options);
?>
Load a driver for an explicit type (faster, bypasses detection routine):
<?php
$driver =& Structures_DataGrid::dataSourceFactory($source, $options, $type);
?>
$source
The data source respective to the driver
$options
An associative array of the form: array(optionName => optionValue, ...)
$type
The data source type constant (of the form DATAGRID_SOURCE_*)
returns driver object or PEAR_Error on failure
throws no exceptions thrown
see Structures_DataGrid::_detectSourceType()
This function can not be called statically.
void
Structures_DataGrid::dump
(
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid::enableStreaming
(
integer
$bufferSize = 500
)
This package is not documented yet.
$bufferSize
Number of records that should be buffered
throws no exceptions thrown
This function can not be called statically.
mixed
Structures_DataGrid::fill
(
object
&$container
,
array
$options = array()
,
string
$type
= null
)
This package is not documented yet.
&$container
A rendering container of any of the supported types (example: an HTML_Table object, a Spreadsheet_Excel_Writer object, etc...)
$options
Options for the corresponding rendering driver
$type
Explicit type in case the container type can't be detected
returns Either true or a PEAR_Error object
throws no exceptions thrown
Filling a Pager object
<?php
require_once 'Pager/Pager.php';
// Create a Pager object with your own options
$pager =& Pager::factory($options);
// fill() sets the $pager object up, according to your data and settings
$datagrid->fill($pager);
// Render the paging links
echo $pager->links;
// Or a select field if you like that
echo $pager->getpageselectbox();
?>
Fill a form with sort fields
<?php
require_once 'HTML/QuickForm.php';
// Create an empty form with your settings
$form = new HTML_QuickForm('myForm', 'POST');
// Customize it, add a header, text field, etc..
$form->addElement('header', null, 'Search & Sort Form Example');
$form->addElement('text', 'my_search', 'Search for:');
// Let the datagrid add sort fields, radio style
$options = array('directionStyle' => 'radio');
$datagrid->fill($form, $options, 'HTMLSortForm');
// You must add a submit button. fill() never does this
$form->addElement('submit', null, 'Submit');
// Use the native HTML_QuickForm::display() to print your form
$form->display();
?>
This function can not be called statically.
void
Structures_DataGrid::generateColumns
(
array
$fields = array()
)
This is a shortcut for adding simple columns easily, instead of creating them manually and calling addColumn() for each.
The generated columns are appended to the current column set.
$fields
Fields and labels. Array of the form: array(field => label, ...) The default is an empty array, which means: all fields fetched from the datasource
throws no exceptions thrown
This function can not be called statically.
object Either&
Structures_DataGrid::getColumnByField
(
string
$fieldName
)
This package is not documented yet.
$fieldName
The field name of the column to look for
returns the column object (reference to) or false if there is no such column
throws no exceptions thrown
This function can not be called statically.
object Either&
Structures_DataGrid::getColumnByName
(
string
$name
)
This package is not documented yet.
$name
The name (label) of the column to look for
returns the column object (reference to) or false if there is no such column
throws no exceptions thrown
This function can not be called statically.
int
Structures_DataGrid::getColumnCount
(
)
This package is not documented yet.
returns the number of columns
throws no exceptions thrown
This function can not be called statically.
array
Structures_DataGrid::getColumns
(
)
This package is not documented yet.
returns Structures_DataGrid_Column objects (references to). This is a numerically indexed array (starting from 0).
throws no exceptions thrown
This function can not be called statically.
int
Structures_DataGrid::getCurrentPage
(
)
This package is not documented yet.
returns the current page number
throws no exceptions thrown
This function can not be called statically.
int
Structures_DataGrid::getCurrentRecordNumberEnd
(
)
This package is not documented yet.
returns the number of the last record currently shown
throws no exceptions thrown
This function can not be called statically.
int
Structures_DataGrid::getCurrentRecordNumberStart
(
)
This package is not documented yet.
returns the number of the first record currently shown, or: 0 if there are no records, 1 if there is no row limit
throws no exceptions thrown
This function can not be called statically.
object DataSource&
Structures_DataGrid::getDataSource
(
)
Retrieves the DataSource object as a reference
returns object reference or null if no driver is loaded
throws no exceptions thrown
This function can not be called statically.
mixed
Structures_DataGrid::getOutput
(
int
$type
= null
,
array
$options = array()
)
This package is not documented yet.
$type
Renderer type (optional)
$options
An associative array of the form: array(optionName => optionValue, ...)
returns The datagrid output (Usually a string: HTML, CSV, etc...) or a PEAR_Error
throws no exceptions thrown
This function can not be called statically.
int
Structures_DataGrid::getPageCount
(
)
This package is not documented yet.
returns the total number of pages or 1 if there are no records or if there is no row limit
throws no exceptions thrown
This function can not be called statically.
int
Structures_DataGrid::getRecordCount
(
)
This package is not documented yet.
returns the total number of records
throws no exceptions thrown
This function can not be called statically.
object Renderer&
Structures_DataGrid::getRenderer
(
)
Retrieves the renderer object as a reference
returns object reference
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid::removeColumn
(
&$column
,
object
$column
)
This package is not documented yet.
&$column
$column
The Structures_DataGrid_Column object (reference to)
throws no exceptions thrown
Remove an unneeded column
<?php
$datagrid =& new Structures_DataGrid();
// Replace this with your database access informations:
$bindOptions['dsn'] = "mysql://foo:bar@host/world";
// The City table contains 5 fields: ID, Name, CountryCode, District and Population
$datagrid->bind("SELECT * FROM City ORDER BY Population", $bindOptions);
// We want to remove the ID field, so we retrieve a reference to the Column:
$column =& $datagrid->getColumnByField('ID');
// And we drop that column:
$datagrid->removeColumn($column);
// This will only render 4 fields: Name, CountryCode, District and Population:
$datagrid->render();
?>
This function can not be called statically.
mixed
Structures_DataGrid::render
(
mixed
$renderer
= null
,
array
$options = array()
)
You can call this method several times with different renderers.
$renderer
Renderer type or instance (optional)
$options
An associative array of the form: array(optionName => optionValue, ...)
returns True or PEAR_Error
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid::setCurrentPage
(
mixed
$page
)
This method is used when paging is implemented
$page
The current page number (as string or int).
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid::setDataSourceOption
(
string
$name
,
mixed
$value
)
This package is not documented yet.
$name
Option name
$value
Option value
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid::setDataSourceOptions
(
array
$options
)
This package is not documented yet.
$options
An associative array of the form: array("option_name" => "option_value",...)
throws no exceptions thrown
This function can not be called statically.
mixed
Structures_DataGrid::setDefaultSort
(
array
$sortSpec
)
If there is no sorting query in the HTTP request, and if the sortRecordSet() method is not called, then the specification passed to setDefaultSort() will be used.
This is especially useful if you want the data to already be sorted when a user first see the datagrid.
This method needs to be called before bind().
$sortSpec
Sorting specification Structure: array(fieldName => direction, ...)
returns Either true or a PEAR_Error object
throws no exceptions thrown
This function can not be called statically.
mixed&
Structures_DataGrid::setRenderer
(
string
$type
,
array
$options = array()
)
Defines which renderer to be used by the DataGrid based on given $type and $options. To attach an existing renderer instance, use attachRenderer() instead.
$type
The defined renderer string
$options
Rendering options
returns Renderer instance or PEAR_Error
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid::setRendererOption
(
string
$name
,
mixed
$value
,
bool
$common
= false
)
This package is not documented yet.
$name
Option name
$value
Option value
$common
Whether to use this option for all renderers (TRUE) or only for the current one (FALSE)
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid::setRendererOptions
(
array
$options
,
bool
$common
= false
)
This package is not documented yet.
$options
An associative array of the form: array("option_name" => "option_value",...)
$common
Whether to use these options for all renderers (TRUE) or only for the current one (FALSE)
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid::setRequestPrefix
(
string
$prefix
)
If you need to change the request variables, you can define a prefix. This is extra useful when using multiple datagrids.
This method needs to be called before bind().
$prefix
The prefix to use on request variables;
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid::setUrlFormat
(
mixed
$format
,
string
$prefix
= null
,
string
$scriptname
= null
)
If this is set, it will be parsed instead of GET/POST. This is only supported on PHP5, as it depends on Net_URL_Mapper.
There are three possible placeholders, :pager, :orderBy and :direction. :page or (:orderBy and :direction) can be used alone.
It is possible to use multipe DataGrid instances on one page with different prefixes.
Instead of a format string you might also pass a Net_URL_Mapper instance to this method, in which case $prefix and $scriptname will be ignored. This instance must be properly set up, connected to url patterns, etc... This is especially useful when you've already configured URL mapping globally for your application and want Structures_DataGrid to integrate.
$format
The URL format string or a Net_URL_Mapper instance
$prefix
Sets the url prefix
$scriptname
Set the scriptname if mod_rewrite not available
throws Net_URL_Mapper_InvalidException
configure a url format
<?php
// identical, for example /page/5/foo/ASC
$datagrid->setUrlFormat('/page/:page/:orderBy/:direction');
$datagrid->setUrlFormat('/:page/:orderBy/:direction', 'page');
// without /page, for example /5/foo/ASC
$datagrid->setUrlFormat('/:page/:orderBy/:direction');
// without paging, for example /sort/foo/ASC
$datagrid->setUrlFormat('/:orderBy/:direction', 'sort');
// with scriptname, for example /index.php/5/foo/ASC
$datagrid->setUrlFormat('/:page/:orderBy/:direction', 'page', 'index.php');
?>
This function can not be called statically.
void
Structures_DataGrid::sortRecordSet
(
array
$sortSpec
,
string
$direction = 'ASC'
)
Do not use this method if data is coming from a database as sorting is much faster coming directly from the database itself.
$sortSpec
Sorting specification Structure: array(fieldName => direction, ...)
$direction
Deprecated. Put the direction(s) into $sortSpec
throws no exceptions thrown
This function can not be called statically.
This class represents a single column for the DataGrid.
Structures_DataGrid_Column
void constructor
Structures_DataGrid_Column::Structures_DataGrid_Column
(
string
$label
,
string
$field
= null
,
string
$orderBy
= null
,
array
$attributes = array()
,
string
$autoFillValue
= null
,
mixed
$formatter
= null
,
array
$formatterArgs = array()
)
Creates default table style settings
$label
The label of the column to be printed
$field
The name of the field for the column to be mapped to
$orderBy
The field or expression to order the data by
$attributes
The attributes for the XML or HTML TD tag; form: array(name => value, ...)
$autoFillValue
The value to use for the autoFill
$formatter
Formatter callback. See setFormatter()
$formatterArgs
Associative array of arguments passed as second argument to the formatter callback
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid_Column::format
(
$type
,
mixed
$type,...
)
EXPERIMENTAL: the behaviour of this method may change in future releases.
This method allows to associate an "automatic" predefined formatter to the column, for common needs as formatting dates, numbers, ...
The currently supported predefined formatters are :
dateFromTimestamp: format a UNIX timestamp according to the date()-like format string passed as second argument
dateFromMysql : format a MySQL DATE, DATETIME, or TIMESTAMP MySQL according to the date() like format string passed as second argument
number: format a number, according to the same optional 2nd, 3rd and 4th arguments that the number_format() PHP function accepts.
printf: format using the printf expression passed as 2nd argument.
printfURL: url-encode and format using the printf expression passed as 2nd argument
$type
$type,...
Predefined formatter name, followed by formatter-specific parameters
throws no exceptions thrown
Common formats
<?php
// Format UNIX timestamps as english dates:
$column->format('dateFromTimestamp', 'm/d/y');
// Format MySQL DATE, DATETIME or TIMESTAMP strings as french dates:
$column->format('dateFromMysql', 'd/m/y');
// Format numbers with 3 decimals and no thousands separator:
$column->format('number', 3, '.', '');
// Format numbers with 2 decimals:
$column->format('number', 2);
// Format using a printf expression:
$column->format('printf', 'Number of potatoes: %d');
// Format an HTML link using a printf expression with url-encoding:
$column->format('printfURL', '<a href="edit.php?key=%s">Edit</a>');
?>
This function can not be called statically.
void
Structures_DataGrid_Column::formatter
(
$record
,
$row
,
$col
)
This method is not meant to be called by user-space code.
Calls a predefined function to develop custom output for the column. The defined function can accept parameters so that each cell in the column can be unique based on the record. The function will also automatically receive the record array as a parameter. All parameters passed into the function will be in one array.
$record
$row
$col
throws no exceptions thrown
This function can not be called statically.
array
Structures_DataGrid_Column::getAttributes
(
)
Return the attributes applied to all cells in this column. This only makes sense for HTML or XML rendering
returns Attributes; form: array(name => value, ...)
throws no exceptions thrown
This function can not be called statically.
string
Structures_DataGrid_Column::getAutoFillValue
(
)
Returns the value to be printed if a cell in the column is null.
throws no exceptions thrown
This function can not be called statically.
string
Structures_DataGrid_Column::getDefaultDirection
(
$str
)
This package is not documented yet.
$str
returns "ASC" or "DESC"
throws no exceptions thrown
This function can not be called statically.
string
Structures_DataGrid_Column::getField
(
)
Returns the name of the field for the column to be mapped to
throws no exceptions thrown
This function can not be called statically.
string
Structures_DataGrid_Column::getLabel
(
)
The label is the text rendered into the column header.
throws no exceptions thrown
This function can not be called statically.
string
Structures_DataGrid_Column::getOrderBy
(
)
This package is not documented yet.
returns field name
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid_Column::setAttributes
(
array
$attributes
)
Set the attributes to be applied to all cells in this column. This only makes sense for HTML or XML rendering
$attributes
form: array(name => value, ...)
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid_Column::setAutoFillValue
(
string
$str
)
Defines a value to be printed if a cell in the column is null.
$str
The value to use for the autoFill
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid_Column::setDefaultDirection
(
string
$str
)
This package is not documented yet.
$str
"ASC" or "DESC"
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid_Column::setField
(
string
$str
)
Defines the name of the field for the column to be mapped to
$str
The name of the field for the column to be mapped to
throws no exceptions thrown
This function can not be called statically.
mixed
Structures_DataGrid_Column::setFormatter
(
mixed
$formatter
,
array
$arguments = array()
)
Define a formatting callback function with optional arguments for this column.
The callback function receives the following array as its first argument:
<?php
array(
'record' => associative array of all fields values for this record,
'fieldName' => the field name of this column,
'columnName' => the label (header) of this column,
'orderBy' => the field name to sort this column by,
'attribs' => this column's attributes,
'currRow' => zero-based row index,
'currCol' => zero-based column index,
);
?>
If you pass the optional $arguments parameter to setFormatter(), the callback function will receive it as its second argument.
$formatter
Callback PHP pseudo-type (Array or String)
$arguments
Associative array of parameters passed to as second argument to the callback function
returns PEAR_Error on failure
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid_Column::setLabel
(
string
$str
)
The label is the text rendered into the column header.
$str
Column label
throws no exceptions thrown
This function can not be called statically.
void
Structures_DataGrid_Column::setOrderBy
(
string
$str
)
This package is not documented yet.
$str
field name
throws no exceptions thrown
This function can not be called statically.
This class is a data source driver for a 2D array
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | no |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
natsort | boolean | Whether the array should be sorted naturally (e.g. example1, Example2, test1, Test2) or not (e.g. Example2, Test2, example1, test1; i.e. capital letters will come first). | false |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
This driver expects an array of the following form:
$data = array(0 => array('col0' => 'val00', 'col1' => 'val01', ...), 1 => array('col0' => 'val10', 'col1' => 'val11', ...), ... );
The first level of this array contains one entry for each row. For every row entry an array with the data for this row is expected. Such an array contains the field names as the keys. For example, 'val01' is the value of the column with the field name 'col1' in the first row. Row numbers start with 0, not with 1.
This class is a data source driver for a CSV File. It will also support any other delimiter.
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | no |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
delimiter | string | Field delimiter | ',' |
enclosure | string | Field enclosure | '"' |
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
header | bool | Whether the CSV file (or string) contains a header row | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
natsort | boolean | Whether the array should be sorted naturally (e.g. example1, Example2, test1, Test2) or not (e.g. Example2, Test2, example1, test1; i.e. capital letters will come first). | false |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
This class is a data source driver for a PEAR::DB::DB_DataObject object
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | yes |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
fields_order_property | string | The name of a property that you can set within your DataObject. It will be used to set the order in which fields are displayed, as long as you're not configuring this by adding/generating columns. Also requires the fields_property to be set. | null |
fields_property | string | The name of a property that you can set within your DataObject. This property is expected to contain the same kind of information as the 'fields' option. If the 'fields' option is set, this one will not be used. | 'fb_fieldsToRender' |
formbuilder_integration | bool | DEPRECATED: use link_level and fields_order_property instead. For BC, Setting this to true is equivalent to setting link_level to 3 and fields_order_property to 'fb_preDefOrder'. | false |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
labels_property | string | The name of a property that you can set within your DataObject. This property should contain the same kind of information as the 'labels' option. If the 'labels' option is set, this one will not be used. | 'fb_fieldLabels' |
link_keep_key | bool | Set this to true when you want to keep the original values (usually foreign keys) of fields which are being replaced by their linked values. The record will then contain additional keys with "__key" prepended. This option only makes sense with link_level higher than 0. Example: if the country_code original value is 'FR' and this is replaced by "France" from the linked country table, then setting link_keep_key to true will keep the "FR" value in country_code__key. | false |
link_level | int | The maximum link display level. If equal to 0 the links will not be followed. | 0 |
link_property | string | The name of a property you can set within a linked DataObject. This property should contain a array of field names that will be used to display a string out of this linked DataObject. Has no effect when link_level is 0. | 'fb_linkDisplayFields' |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
raw_count | bool | If true: query all the records in order to count them. This is needed when records are grouped (GROUP BY, DISTINCT, etc..), but might be heavy. If false: perform a smart count query with DB_DataObject::count(). | false |
return_objects | bool | If true, the returned records will consists of clones of the dataobject instead of associative arrays. This is especially useful when used in conjunction with the smarty renderer for example, to directly access the dataobject properties and methods from your templates. | false |
sort_property | string | The name of a property that you can set within your DataObject. This property should contain an array of the form: array("field1", "field1 DESC", ...) If the data is already being sorted then this this property's content will be appended to the current ordering. | 'fb_linkOrderFields' |
Bind a DB_DataObject to Structures_DataGrid
<?php
$person = new DataObjects_Person;
$person->hair = 'red';
$person->has_glasses = 1;
$datagrid->bind($person);
?>
This class is a data source driver for the PEAR::DB::DB_Result object
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | no |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
natsort | boolean | Whether the array should be sorted naturally (e.g. example1, Example2, test1, Test2) or not (e.g. Example2, Test2, example1, test1; i.e. capital letters will come first). | false |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
This class is a data source driver for the PEAR::DB object
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | yes |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
count_query | string | Query that calculates the number of rows. See below for more information about when such a count query is needed. | '' |
db_options | array | Options for the created database object. This option is only used when the 'dsn' option is given. | array() |
dbc | object | A PEAR::DB instance that will be used by this driver. Either this or the 'dsn' option is required. | null |
dsn | string | A PEAR::DB dsn string. The DB connection will be established by this driver. Either this or the 'dbc' option is required. | null |
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
You need to specify either a DB instance or a DB compatible dsn string as an option to use this driver.
If you use complex queries (e.g. with complex joins or with aliases), $datagrid->getRecordCount() might return a wrong result. For the case of GROUP BY, UNION, or DISTINCT in your queries, and for the case of subqueries, this driver already has special handling. However, if you observe wrong record counts, you need to specify a special query that returns only the number of records (e.g. 'SELECT COUNT(*) FROM ...') as an additional option 'count_query' to the bind() call.
You can specify an ORDER BY statement in your query. Please be aware that this sorting statement is then used in *every* query before the sorting options that come from a renderer (e.g. by clicking on the column header when using the HTML_Table renderer which is sent in the HTTP request). If you want to give a default sorting statement that is only used if there is no sorting query in the HTTP request, then use $datagrid->setDefaultSort().
This class is a data source driver for the PEAR::DB_Table object
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | yes |
Insert, update and delete records | yes |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
params | array | Placeholder parameters for prepare/execute | array() |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
view | string | The view from $sql array in your DB_Table object. This option is required. | null |
where | string | A where clause for the SQL query. | null |
If you use aliases in the select part of your view, the count() method from DB_Table and, therefore, $datagrid->getRecordCount() might return a wrong result. To avoid this, DB_Table uses a special query for counting if it is given via a view that needs to be named as '__count_' followed by the name of the view that this counting view belongs to. (For example: if you have a view named 'all', the counting view needs to be named as '__count_all'.)
To use update() and delete() methods, it is required that the indexes are properly defined in the $idx array in your DB_Table subclass. If you have, for example, created your database table yourself and did not setup the $idx array, you can use the 'primaryKey' option to define the primary key field.
Bind a DB_Table class to Structures_DataGrid
<?php
// basic guestbook class that extends DB_Table
class GuestBook_Table extends DB_Table
{
var $col = array(
// unique row ID
'id' => array(
'type' => 'integer',
'require' => true
),
// first name
'fname' => array(
'type' => 'varchar',
'size' => 32
),
// last name
'lname' => array(
'type' => 'varchar',
'size' => 64
),
// email address
'email' => array(
'type' => 'varchar',
'size' => 128,
'require' => true
),
// date signed
'signdate' => array(
'type' => 'date',
'require' => true
)
);
var $idx = array(); // indices don't matter here
var $sql = array(
// multiple rows for a list
'list' => array(
'select' => 'id, signdate, CONCAT(fname, " ", lname) AS fullname',
'order' => 'signdate DESC'
)
);
}
// instantiate the extended DB_Table class
// (using an existing database connection and the table name 'guestbook')
$guestbook =& new GuestBook_Table($db, 'guestbook');
// Options for the bind() call
// (using the predefined query 'list' from the $sql array and a where
// condition)
$options = array('view' => 'list', 'where' => 'YEAR(signdate) = 2100');
// bind the guestbook object
// (if you don't generate any column yourself before rendering, three
// columns will be generated: id, signdate, fullname)
$test = $datagrid->bind($guestbook, $options);
// print binding error if any
if (PEAR::isError($test)) {
echo $test->getMessage();
}
?>
This class is a data source driver for an Excel spreadsheet.
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | no |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
header | bool | Whether the Excel file contains a header row | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
natsort | boolean | Whether the array should be sorted naturally (e.g. example1, Example2, test1, Test2) or not (e.g. Example2, Test2, example1, test1; i.e. capital letters will come first). | false |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
Spreadsheet_Excel_Reader ("PHP-ExcelReader") is not available as a PEAR package. It is available on SourceForge.net: http://sourceforge.net/projects/phpexcelreader/
This class expects the file reader.php in the directory Spreadsheet/Excel/.
Please note that the current version (2i) of Spreadsheet_Excel_Reader contains a die() statement in the read() method in reader.php (line 171). This makes a reasonable PEAR error handling for the "file not found" error impossible.
It is therefore recommended that you replace the die() statement by something like this:
return PEAR::raiseError('The filename ' . $sFileName . ' is not readable');
This class is optimized for the changed code (but will work also with the die() in the reader class, of course), and provides then a reasonable error handling.
This class is a data source driver for the PEAR::MDB2 object
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | yes |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
count_query | string | Query that calculates the number of rows. See below for more information about when such a count query is needed. | '' |
db_options | array | Options for the created database object. This option is only used when the 'dsn' option is given. | array() |
dbc | object | A PEAR::MDB2 instance that will be used by this driver. Either this or the 'dsn' option is required. | null |
dsn | string | A PEAR::MDB2 dsn string. The MDB2 connection will be established by this driver. Either this or the 'dbc' option is required. | null |
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
You need to specify either a MDB2 instance or a MDB2 compatible dsn string as an option to use this driver.
If you use complex queries (e.g. with complex joins or with aliases), $datagrid->getRecordCount() might return a wrong result. For the case of GROUP BY, UNION, or DISTINCT in your queries, and for the case of subqueries, this driver already has special handling. However, if you observe wrong record counts, you need to specify a special query that returns only the number of records (e.g. 'SELECT COUNT(*) FROM ...') as an additional option 'count_query' to the bind() call.
You can specify an ORDER BY statement in your query. Please be aware that this sorting statement is then used in *every* query before the sorting options that come from a renderer (e.g. by clicking on the column header when using the HTML_Table renderer which is sent in the HTTP request). If you want to give a default sorting statement that is only used if there is no sorting query in the HTTP request, then use $datagrid->setDefaultSort().
This class is a data source driver for PHP Data Objects
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | yes |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
count_query | string | Query that calculates the number of rows. See below for more information about when such a count query is needed. | '' |
db_options | array | Options for the created database object. This option is only used when the 'dsn' option is given. | array() |
dbc | object | A PDO instance that will be used by this driver. Either this or the 'dsn' option is required. | null |
dsn | string | A PDO dsn string. The PDO connection will be established by this driver. Either this or the 'dbc' option is required. | null |
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
password | string | Password for the crated PDO connection. Only needed in conjunction with 'dsn' option. | null |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
username | string | Username for the created PDO connection. Only needed in conjunction with 'dsn' option. | null |
You need to specify either a PDO instance or a PDO compatible dsn string as an option to use this driver.
If you use complex queries (e.g. with complex joins or with aliases), $datagrid->getRecordCount() might return a wrong result. For the case of GROUP BY, UNION, or DISTINCT in your queries, and for the case of subqueries, this driver already has special handling. However, if you observe wrong record counts, you need to specify a special query that returns only the number of records (e.g. 'SELECT COUNT(*) FROM ...') as an additional option 'count_query' to the bind() call.
You can specify an ORDER BY statement in your query. Please be aware that this sorting statement is then used in *every* query before the sorting options that come from a renderer (e.g. by clicking on the column header when using the HTML_Table renderer which is sent in the HTTP request). If you want to give a default sorting statement that is only used if there is no sorting query in the HTTP request, then use $datagrid->setDefaultSort().
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | no |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
natsort | boolean | Whether the array should be sorted naturally (e.g. example1, Example2, test1, Test2) or not (e.g. Example2, Test2, example1, test1; i.e. capital letters will come first). | false |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
This class is a DataSource driver for XML data. It accepts strings and filenames. An XPath expression can be specified to extract a subset from the given XML data.
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Multiple field sorting | no |
Insert, update and delete records | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
fieldAttribute | string | Which attribute of the XML source should be used as column field name (only used if the XML source has attributes). | null |
fields | array | Which data fields to fetch from the datasource. An empty array means: all fields. Form: array(field1, field2, ...) | array() |
generate_columns | bool | Generate Structures_DataGrid_Column objects with labels. See the 'labels' option. DEPRECATED: use Structures_DataGrid::generateColumns() instead | false |
labelAttribute | string | Which attribute of the XML source should be used as column label (only used if 'generate_columns' is true and the XML source has attributes). | null |
labels | array | Data field to column label mapping. Only used when 'generate_columns' is true. Form: array(field => label, ...) DEPRECATED: use Structures_DataGrid::generateColumns() instead | array() |
natsort | boolean | Whether the array should be sorted naturally (e.g. example1, Example2, test1, Test2) or not (e.g. Example2, Test2, example1, test1; i.e. capital letters will come first). | false |
primaryKey | array | Name(s), or numerical index(es) of the field(s) which contain a unique record identifier (only use several fields in case of a multiple-fields primary key) | null |
xpath | string | XPath to a subset of the XML data. | '' |
Bind a simple XML string
<?php
$xml = <<<XML
<records>
<record>
<firstname>Olivier</firstname>
<lastname>Guilyardi</lastname>
<city>Paris</city>
<country>France</country>
</record>
<record>
<firstname>Mark</firstname>
<lastname>Wiesemann</lastname>
<city>Aachen</city>
<country>Germany</country>
</record>
</records>
XML;
// Options for the bind() call (empty in this example)
$options = array();
// Bind the XML string
$test = $datagrid->bind($xml, $options, 'XML');
// Print binding error if any
if (PEAR::isError($test)) {
echo $test->getMessage();
}
?>
Bind a more complex XML string (using 'xpath' option)
<?php
$xml = <<<XML
<response>
<date>today</date>
<server>localhost</server>
<records>
<record>
<firstname>Olivier</firstname>
<lastname>Guilyardi</lastname>
<city>Paris</city>
<country>France</country>
</record>
<record>
<firstname>Mark</firstname>
<lastname>Wiesemann</lastname>
<city>Aachen</city>
<country>Germany</country>
</record>
</records>
</response>
XML;
// Options for the bind() call
$options = array('xpath' => '/response/records');
// Bind the XML string
$test = $datagrid->bind($xml, $options, 'XML');
// Print binding error if any
if (PEAR::isError($test)) {
echo $test->getMessage();
}
?>
This driver is experimental and has not been officially released yet. It is only available from SVN.
Driver for rendering the DataGrid as an HTML table with a checkbox for each row
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | yes |
Output Buffering | yes |
Direct Rendering | no |
Streaming | no |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
classASC | string | A CSS class name for TH elements to define that sorting is currently ascending. | '' |
classDESC | string | A CSS class name for TH elements to define that sorting is currently descending. | '' |
columnAttributes | array | Column cells attributes. This is an array of the form: array(fieldName => array(attribute => value, ...) ...) This option is only used by XML/HTML based drivers. | array() |
convertEntities | bool | Whether or not to convert html entities. This calls htmlspecialchars(). | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
emptyRowAttributes | array | An associative array containing the attributes for empty rows. | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
evenRowAttributes | array | An associative array containing each attribute of the even rows. | array() |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
form | object | Instance of a HTML_QuickForm object. | null |
formRenderer | object | Instance of a HTML_QuickForm_Renderer_QuickHtml object. | null |
headerAttributes | array | Attributes for the header row. This is an array of the form: array(attribute => value, ...) | array() |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
inputName | string | The HTML_QuickForm element name for the checkboxes. | 'checkedItems' |
numberAlign | bool | Whether to right-align numeric values. | true |
oddRowAttributes | array | An associative array containing each attribute of the odd rows. | array() |
onMove | string | Name of a Javascript function to call on onclick/onsubmit events when the user is either paging or sorting the data. This function receives a single object argument of the form: { page: <page>, sort: [{field: <field>, direction: <direction>}, ...], data: <user_data> }. Remark: setting this option doesn't remove the href attribute, you should return false from your handler function to void it (eg: for AJAX, etc..). | null |
onMoveData | string | User data passed in the "data" member of the object argument passed to onMove. No JSON serialization is performed, this is assigned as a raw string to the "data" attribute. It's up to you to add quotes, slashes, etc... | '' |
primaryKey | string | The name of the primary key. This value is used for the checkboxes. | 'id' |
selfPath | string | The complete path for sorting and paging links. | $_SERVER['PHP_SELF'] |
sortIconASC | string | The icon to define that sorting is currently ascending. Can be text or HTML to define an image. | '' |
sortIconDESC | string | The icon to define that sorting is currently descending. Can be text or HTML to define an image. | '' |
sortingResetsPaging | bool | Whether sorting HTTP queries reset paging. | true |
This driver puts a checkbox for each row of the table into the first column. By default, a new column with the value of the 'inputName' option is added for the checkboxes. If you want to customize this column, you can add a column yourself as it is shown in the example.
Basic usage
<?php
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/Renderer/QuickHtml.php';
require_once 'Structures/DataGrid.php';
// prepare the form and the QuickHtml renderer
$form =& new HTML_QuickForm();
$renderer =& new HTML_QuickForm_Renderer_QuickHtml();
// add action selectbox and submit button to the form
$form->addElement('select', 'action', 'choose',
array('delete' => 'Delete',
'move' => 'Move to archive'));
$form->addElement('submit', 'submit', 'Save');
// prepare the DataGrid
$dg =& new Structures_DataGrid();
if (PEAR::isError($dg)) {
die($dg->getMessage() . '<br />' . $dg->getDebugInfo());
}
// bind some data (e.g. via a SQL query and MDB2)
$error = $dg->bind('SELECT * FROM news',
array('dsn' => 'mysql://user:password@server/database'));
if (PEAR::isError($error)) {
die($error->getMessage() . '<br />' . $error->getDebugInfo());
}
// the renderer adds an auto-generated column for the checkbox by default;
// it is also possible to add a column yourself, for example like in the
// following four lines:
$column = new Structures_DataGrid_Column('checkboxes', 'idList', null,
array('width' => '10'));
$dg->addColumn($column);
$dg->generateColumns();
$rendererOptions = array('form' => $form,
'formRenderer' => $renderer,
'inputName' => 'idList',
'primaryKey' => 'id'
);
// use a template string for the form
$tpl = '';
// generate the HTML table and add it to the template string
$tpl .= $dg->getOutput('CheckableHTMLTable', $rendererOptions);
if (PEAR::isError($tpl)) {
die($tpl->getMessage() . '<br />' . $tpl->getDebugInfo());
}
// add the HTML code of the action selectbox and the submit button to the
// template string
$tpl .= $renderer->elementToHtml('action');
$tpl .= $renderer->elementToHtml('submit');
// we're now ready to output the form (toHtml() adds the <form> / </form>
// pair to the template)
echo $renderer->toHtml($tpl);
// if the form was submitted and the data is valid, show the submitted data
if ($form->isSubmitted() && $form->validate()) {
var_dump($form->getSubmitValues());
}
?>
This renderer generates nicely formatted and padded ASCII tables.
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | yes |
Output Buffering | yes |
Direct Rendering | no |
Streaming | no |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
numberAlign | bool | Whether to right-align numeric values. | true |
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | no |
Output Buffering | yes |
Direct Rendering | yes |
Streaming | yes |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
delimiter | string | Field delimiter | ',' |
enclosure | string | Field enclosure | '"' |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
filename | string | Filename of the generated CSV file; boolean false means that no filename will be sent | false |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
lineBreak | string | The character(s) to use for line breaks | '\n' |
numberAlign | bool | Whether to right-align numeric values. | true |
saveToFile | boolean | Whether the output should be saved on the local filesystem. Please note that the 'filename' option must be given if this option is set to true. | false |
targetEncoding | string | If set, the content will be converted from encoding to targetEncoding. A BOM will also be added, if relevant. See PHP mbstring documentation for encoding names. Tip: for Excel use 'UTF-16LE'. | '' |
useQuotes | mixed | Whether or not to encapsulate the values with the enclosure value. true: always, false: never, 'auto': when needed | 'auto' |
writeMode | string | The mode that is used in the internal fopen() calls. Useful e.g. when you want to append to existing file. C.p. the fopen() documentation for the allowed modes. | 'wb' |
This driver is experimental and has not been officially released yet. It is only available from SVN.
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | yes |
Output Buffering | yes |
Direct Rendering | no |
Streaming | no |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
assocColumns | bool | Whether or not to build the column header as an associate array. | true |
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
columnAttributes | array | Column cells attributes. This is an array of the form: array(fieldName => array(attribute => value, ...) ...) This option is only used by XML/HTML based drivers. | array() |
columnNames | array | The set of column names to use for the column header. | array() |
convertEntities | bool | Whether or not to convert html entities. This calls htmlspecialchars(). | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
evenRowAttribute | string | The css class to be used for the even row listings. | 'even' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
formatter | array | The callback array for a column header formatter method. | array($this,'defaultHeaderFormatter') |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
numberAlign | bool | Whether to right-align numeric values. | true |
oddRowAttribute | string | The css class to be used for odd row listings. | 'odd' |
onMove | string | Name of a Javascript function to call on onClick/onSubmit events when the user is either paging or sorting the data. This function receives a single object argument of the form: { page: <page>, sort: [{field: <field>, direction: <direction>}, ...], data: <user_data> }. Remark: setting this option doesn't remove the href attribute, you should return false from your handler function to void it (eg: for AJAX, etc..). | null |
onMoveData | string | User data passed in the "data" member of the object argument passed to onMove. No JSON serialization is performed, this is assigned as a raw string to the "data" attribute. It's up to you to add quotes, slashes, etc... | '' |
pagerOptions | array | The custom options to be sent to the Pager renderer. | |
resultsFormat | string | The format of the results message in sprintf format. | 'You have %s results in %s pages' |
selfPath | string | The complete path for sorting and paging links. | $_SERVER['PHP_SELF'] |
sortingResetsPaging | bool | Whether sorting HTTP queries reset paging. | true |
This driver does not support the render() method, it only is able to be attached setting the container to a current Flexy instance. Options to the renderer must also be passed using the setOptions() method.
Flexy output is buffered using the DataGrid getOutput() method.
This driver assigns the following Flexy template variables: - columnSet: array of columns' labels and sorting links - columnHeader: object of columns' labels and sorting links - recordSet: associate array of records values - numberedSet: numbered array of records values - currentPage: current page (starting from 1) - recordLimit: number of rows per page - pagesNum: number of pages - columnsNum: number of columns - recordsNum: number of records in the current page - totalRecordsNum: total number of records - firstRecord: first record number (starting from 1) - lastRecord: last record number (starting from 1)
This driver also register a Smarty custom function named getPaging that can be called from Smarty templates with {getPaging} in order to print paging links. This function accepts any of the Pager::factory() options as parameters.
Dynamic Template example, featuring sorting and paging:
<!-- Show paging links using the custom getPaging function --> {getPaging():h} <p>Showing records {firstRecord} to {lastRecord} from {totalRecordsNum}, page {currentPage} of {pagesNum}</p> <table cellspacing="0"> <!-- Build header --> <tr> <th> {foreach:columnSet,column} <td><a href="{column[link]:h}">{column[label]:h}</a></td> {end:} </th> </tr> <!-- Build body --> <tr class="{getRowCSS()}" flexy:foreach="numberedSet,k,row"> {foreach:row,field} <td>{field}</td> {end:} </tr> </table>
Static Template example, featuring sorting and paging:
<table cellspacing="0"> <!-- Build header --> <tr> <th> <td> <a href="{columnHeader.name[link]:h}">{columnHeader.field1[label]:h}</a> </td> <td> <a href="{columnHeader.surname[link]:h}">{columnHeader.field2[label]:h}</a> </td> </th> </tr> <!-- Build body --> <tr class="{getRowCSS()}" flexy:foreach="recordSet,k,row"> <td>{row[field1]}</td> <td>{row[field2]}</td> </tr> </table>
require_once 'HTML/Template/Flexy.php'; require_once 'Structures/DataGrid.php'; require_once 'Structures/DataGrid/Renderer/Flexy.php'; $tpl = new HTML_Template_Flexy($config['HTML_Template_Flexy']); $dg =& new Structures_DataGrid($_GET['setPerPage'] ? $_GET['setPerPage'] : 10,$_GET['page'] ? $_GET['page'] : 1); $dg->bind($dataObject); $renderer = new Structures_DataGrid_Renderer_Flexy(); $renderer->setContainer($tpl); $renderer->setOptions($config['Structures_DataGrid']); $dg->attachRenderer($renderer); $this->tpl->compile($template); echo $dg->getOutput();
This driver is experimental and has not been officially released yet. It is only available from SVN.
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | yes |
Output Buffering | yes |
Direct Rendering | no |
Streaming | no |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
numberAlign | bool | Whether to right-align numeric values. | true |
onMove | string | Name of a Javascript function to call on onclick/onsubmit events when the user is either paging or sorting the data. This function receives a single object argument of the form: { page: <page>, sort: [{field: <field>, direction: <direction>}, ...], data: <user_data> }. Remark: setting this option doesn't remove the href attribute, you should return false from your handler function to void it (eg: for AJAX, etc..). | null |
onMoveData | string | User data passed in the "data" member of the object argument passed to onMove. No JSON serialization is performed, this is assigned as a raw string to the "data" attribute. It's up to you to add quotes, slashes, etc... | '' |
textSubmit | string | Label for the submit button | 'Submit' |
Basic usage
<?php
require_once 'Structures/DataGrid.php';
$datagrid =& new Structures_DataGrid();
$datagrid->bind(...); // bind your data here
$datagrid->setRenderer('HTMLEditForm');
$datagrid->render();
?>
Usage with tableless renderer and DHTMLRules
<?php
// don't forget to include the stylesheet to get a reasonable layout
require_once 'Structures/DataGrid.php';
require_once 'HTML/QuickForm/DHTMLRulesTableless.php';
require_once 'HTML/QuickForm/Renderer/Tableless.php';
$datagrid =& new Structures_DataGrid();
$datagrid->bind(...); // bind your data here
// create the form object, using DHTMLRules
$form = new HTML_QuickForm_DHTMLRulesTableless('editform', null, null,
null, null, true);
$form->removeAttribute('name'); // for XHTML validity
// to get a legend for the fieldset, we add a header element
$form->addElement('header', 'header', 'EditForm example');
// fill() makes the renderer to generate the needed form elements
$datagrid->fill($form, null, 'HTMLEditForm');
// we have to add a submit button ourselves
$form->addElement('submit', null, 'Submit');
// to show the DHTMLRules functionality, we add a required rule
// (we assume that there is an element with name 'id')
$form->addRule('id', 'Please enter the ID.', 'required', null, 'client');
// to get validation onChange/onBlur events, we need the following call
$form->getValidationScript();
// instantiate the tableless renderer and output the form
$renderer =& new HTML_QuickForm_Renderer_Tableless();
$form->accept($renderer);
echo $renderer->toHtml();
?>
This driver renders a form (using HTML_QuickForm) so that the user can select several fields and directions to sort the datagrid by.
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | yes |
Output Buffering | yes |
Direct Rendering | no |
Streaming | no |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
directionStyle | string | Whether to render the direction form elements as 'select' or 'radio' elements | 'select' |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
numberAlign | bool | Whether to right-align numeric values. | true |
onMove | string | Name of a Javascript function to call on onclick/onsubmit events when the user is either paging or sorting the data. This function receives a single object argument of the form: { page: <page>, sort: [{field: <field>, direction: <direction>}, ...], data: <user_data> }. Remark: setting this option doesn't remove the href attribute, you should return false from your handler function to void it (eg: for AJAX, etc..). | null |
onMoveData | string | User data passed in the "data" member of the object argument passed to onMove. No JSON serialization is performed, this is assigned as a raw string to the "data" attribute. It's up to you to add quotes, slashes, etc... | '' |
sortFieldsNum | int | How many fields the user will be able to sort by. This has no effect if the backend does not support sorting by multiple fields. | 3 |
textAscending | string | Label for the ASC direction | 'Ascending' |
textChoose | string | What to display in the select box when no field is selected (first option) | 'Choose...' |
textDescending | string | Label for the DESC direction | 'Descending' |
textSortBy | string | Label for the first field | 'Sort by:' |
textSubmit | string | Label for the submit button | 'Submit' |
textThenBy | string | Label for the second and following fields | 'Then by:' |
Fill a form with sort fields
<?php
require_once 'HTML/QuickForm.php';
// Create an empty form with your settings
$form = new HTML_QuickForm('myForm', 'POST');
// Customize it, add a header, text field, etc..
$form->addElement('header', null, 'Search & Sort Form Example');
$form->addElement('text', 'my_search', 'Search for:');
// Let the datagrid add sort fields, radio style
$options = array('directionStyle' => 'radio');
$datagrid->fill($form, $options, 'HTMLSortForm');
// You must add a submit button. fill() never does this
$form->addElement('submit', null, 'Submit');
// Use the native HTML_QuickForm::display() to print your form
$form->display();
?>
Driver for rendering the DataGrid as an HTMLTable
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | yes |
Output Buffering | yes |
Direct Rendering | no |
Streaming | no |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
classASC | string | A CSS class name for TH elements to define that sorting is currently ascending. | '' |
classDESC | string | A CSS class name for TH elements to define that sorting is currently descending. | '' |
columnAttributes | array | Column cells attributes. This is an array of the form: array(fieldName => array(attribute => value, ...) ...) This option is only used by XML/HTML based drivers. | array() |
convertEntities | bool | Whether or not to convert html entities. This calls htmlspecialchars(). | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
emptyRowAttributes | array | An associative array containing the attributes for empty rows. | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
evenRowAttributes | array | An associative array containing each attribute of the even rows. | array() |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
headerAttributes | array | Attributes for the header row. This is an array of the form: array(attribute => value, ...) | array() |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
numberAlign | bool | Whether to right-align numeric values. | true |
oddRowAttributes | array | An associative array containing each attribute of the odd rows. | array() |
onMove | string | Name of a Javascript function to call on onclick/onsubmit events when the user is either paging or sorting the data. This function receives a single object argument of the form: { page: <page>, sort: [{field: <field>, direction: <direction>}, ...], data: <user_data> }. Remark: setting this option doesn't remove the href attribute, you should return false from your handler function to void it (eg: for AJAX, etc..). | null |
onMoveData | string | User data passed in the "data" member of the object argument passed to onMove. No JSON serialization is performed, this is assigned as a raw string to the "data" attribute. It's up to you to add quotes, slashes, etc... | '' |
selfPath | string | The complete path for sorting and paging links. | $_SERVER['PHP_SELF'] |
sortIconASC | string | The icon to define that sorting is currently ascending. Can be text or HTML to define an image. | '' |
sortIconDESC | string | The icon to define that sorting is currently descending. Can be text or HTML to define an image. | '' |
sortingResetsPaging | bool | Whether sorting HTTP queries reset paging. | true |
Simple AJAX support using the Prototype framework
<?php
require_once 'PEAR.php';
require_once 'Structures/DataGrid.php';
$datagrid =& new Structures_DataGrid(10);
$options['dsn'] = 'mysql://username@localhost/mydatabase';
$datagrid->bind("SELECT * FROM mytable", $options);
// Set the javascript handler function for onclick events
$datagrid->setRendererOption('onMove', 'updateGrid', true);
if (isset($_GET['ajax'])) {
// Handle table AJAX requests
if ($_GET['ajax'] == 'table') {
$datagrid->render();
}
// Handle pager AJAX requests
if ($_GET['ajax'] == 'pager') {
$datagrid->render('Pager');
}
exit();
}
// No AJAX request, render the initial content..
?>
<html>
<head>
<!-- Require the Prototype JS framework from http://www.prototypejs.org -->
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript">
function updateGrid(info)
{
var url = '<?php echo $_SERVER['PHP_SELF']; ?>';
var pars = 'page=' + info.page;
if (info.sort.length > 0) {
pars += '&orderBy=' + info.sort[0].field + '&direction=' + info.sort[0].direction;
}
new Ajax.Updater( 'grid', url, { method: 'get', parameters: pars + '&ajax=table' });
new Ajax.Updater( 'pager', url, { method: 'get', parameters: pars + '&ajax=pager' });
// Important: return false to avoid href links
return false;
}
</script>
</head>
<body>
Pages: <span id="pager"><?php $datagrid->render('Pager'); ?></span>
<div id="grid"><?php $datagrid->render(); ?></div>
</body>
</html>
This driver provides generic paging. This driver has full container support. You can use the Structures_DataGrid::fill() method with it. It buffers output, you can use Structures_DataGrid::getOutput()
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | yes |
Output Buffering | yes |
Direct Rendering | no |
Streaming | no |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
numberAlign | bool | Whether to right-align numeric values. | true |
onMove | string | Name of a Javascript function to call on onclick/onsubmit events when the user is either paging or sorting the data. This function receives a single object argument of the form: { page: <page>, sort: [{field: <field>, direction: <direction>}, ...], data: <user_data> }. Remark: setting this option doesn't remove the href attribute, you should return false from your handler function to void it (eg: for AJAX, etc..). | null |
onMoveData | string | User data passed in the "data" member of the object argument passed to onMove. No JSON serialization is performed, this is assigned as a raw string to the "data" attribute. It's up to you to add quotes, slashes, etc... | '' |
pagerOptions | array | Options passed to Pager::factory(). Basic defaults are: mode: Sliding, delta: 5, separator: "|", prevImg: "<<" (<<), nextImg: ">>" (>>). The extraVars and excludeVars options are populated according to the Renderer common extraVars and excludeVars options. You may also specify some variables to be added or excluded here. The totalItems, perPage, urlVar, and currentPage options are set accordingly to the data statistics reported by the DataGrid and DataSource. You may overload these values here if you know what you are doing. |
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | yes |
Output Buffering | yes |
Direct Rendering | no |
Streaming | no |
Object Preserving | yes |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
associative | bool | By default the column set and the records are numerically indexed arrays. By setting this option to true the keys will be field names instead. | false |
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
columnAttributes | array | Column cells attributes. This is an array of the form: array(fieldName => array(attribute => value, ...) ...) This option is only used by XML/HTML based drivers. | array() |
convertEntities | bool | Whether or not to convert html entities. This calls htmlspecialchars(). | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
numberAlign | bool | Whether to right-align numeric values. | true |
onMove | string | Name of a Javascript function to call on onclick/onsubmit events when the user is either paging or sorting the data. This function receives a single object argument of the form: { page: <page>, sort: [{field: <field>, direction: <direction>}, ...], data: <user_data> }. Remark: setting this option doesn't remove the href attribute, you should return false from your handler function to void it (eg: for AJAX, etc..). | null |
onMoveData | string | User data passed in the "data" member of the object argument passed to onMove. No JSON serialization is performed, this is assigned as a raw string to the "data" attribute. It's up to you to add quotes, slashes, etc... | '' |
selfPath | string | The complete path for sorting and paging links. | $_SERVER['PHP_SELF'] |
sortingResetsPaging | bool | Whether sorting HTTP queries reset paging. | true |
varPrefix | string | Prefix for smarty variables and functions assigned by this driver. Can be used in conjunction with Structure_DataGrid::setRequestPrefix() for displaying several grids on a single page. | '' |
To use this driver you need the Smarty template engine from http://smarty.php.net
This driver does not support the render() method, it is only able to:
Either fill() a Smarty object by assigning variables and registering the {getPaging} smarty function. It's up to you to call Smarty::display() after the Smarty object has been filled.
Or return all variables as a PHP array from getOutput(), for maximum flexibility, so that you can assign them the way you like to your Smarty instance.
This driver assigns the following Smarty variables:
- $columnSet: array of columns specifications structure: array ( 0 => array ( 'name' => field name, 'label' => column label, 'link' => sorting link, 'attributes' => attributes string, 'direction' => 'ASC', 'DESC' or '', 'onclick' => onMove call ), ... ) - $recordSet: array of records values - $currentPage: current page (starting from 1) - $nextPage: next page - $previousPage: previous page - $recordLimit: number of rows per page - $pagesNum: number of pages - $columnsNum: number of columns - $recordsNum: number of records in the current page - $totalRecordsNum: total number of records - $firstRecord: first record number (starting from 1) - $lastRecord: last record number (starting from 1) - $currentSort: array with column names and the directions used for sorting - $datagrid: a reference that you can pass to {getPaging}
This driver registers a Smarty custom function named getPaging that can be called from Smarty templates with {getPaging} in order to print paging links. This function accepts the same parameters as the pagerOptions option of Structures_DataGrid_Renderer_Pager.
{getPaging} accepts an optional "datagrid" parameter which you can pass the $datagrid variable, to display paging for an arbitrary datagrid (useful with multiple dynamic datagrids on a single page).
Object Records : this drivers preserves object records if provided. This means that if your datasource provides objects instead of associative arrays as records, you can access their properties and methods in your smarty template, with something like: {$recordSet[col]->getSomeInformation()}.
Using the Smarty renderer
<?php
require_once 'Structures/DataGrid.php';
require_once 'Smarty.class.php';
$datagrid =& new Structures_DataGrid(10);
$options = array('dsn' => 'mysql://username@localhost/mydatabase');
$datagrid->bind("SELECT * FROM mytable", $options);
$smarty = new Smarty();
$datagrid->fill($smarty);
$smarty->display('smarty-simple.tpl');
?>
Smarty template with sorting and paging (smarty-simple.tpl)
<?php
<!-- Show paging links using the custom getPaging function -->
{getPaging prevImg="<<" nextImg=">>" separator=" | " delta="5"}
<p>Showing records {$firstRecord} to {$lastRecord}
from {$totalRecordsNum}, page {$currentPage} of {$pagesNum}</p>
<table cellspacing="0">
<!-- Build header -->
<tr>
{section name=col loop=$columnSet}
<th {$columnSet[col].attributes}>
<!-- Check if the column is sortable -->
{if $columnSet[col].link != ""}
<a href="{$columnSet[col].link}">{$columnSet[col].label}</a>
<!-- Show the current ordering with an arrow -->
{if $columnSet[col].direction == "ASC"}
↓
{elseif $columnSet[col].direction == "DESC"}
↑
{/if}
{else}
{$columnSet[col].label}
{/if}
</th>
{/section}
</tr>
<!-- Build body -->
{section name=row loop=$recordSet}
<tr {if $smarty.section.row.iteration is even}bgcolor="#EEEEEE"{/if}>
{section name=col loop=$recordSet[row]}
<td {$columnSet[col].attributes}>{$recordSet[row][col]}</td>
{/section}
</tr>
{/section}
</table>
?>
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | yes |
Output Buffering | no |
Direct Rendering | not really, see below |
Streaming | no |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
bodyFormat | mixed | The format for body cells (either 0 [= "no format"] or a Spreadsheet_Excel_Writer_Format object) Please see the NOTE ABOUT FORMATTING below. | 0 |
border | int | Border drawn around the whole datagrid: 0 => none, 1 => thin, 2 => thick (NOT IMPLEMENTED YET) | 0 |
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
filename | string | The filename of the spreadsheet | 'spreadsheet.xls' |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
headerBorder | int | Border between the header and body: 0 => none, 1 => thin, 2 => thick (NOT IMPLEMENTED YET) | 0 |
headerFormat | mixed | The format for header cells (either 0 [= "no format"] or a Spreadsheet_Excel_Writer_Format object) Please see the NOTE ABOUT FORMATTING below. | 0 |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
numberAlign | bool | Whether to right-align numeric values. | true |
sendToBrowser | bool | Should the spreadsheet be send to the browser? (true = send to browser, false = write to a file) | true |
startCol | int | The Worksheet column number to start rendering at | 0 |
startRow | int | The Worksheet row number to start rendering at | 0 |
tempDir | string | A temporary directory to be used by Spreadsheet_Excel_Writer (cp. "General Notes" section). | null |
version | int | If you don't pass a worksheet object to this renderer, you can set the BIFF version with this option. The only accepted value by Spreadsheet_Excel_Writer is 8 (for usage of the BIFF8 format). All other values will lead to the older format (which is needed if you get errors in Excel, e.g. about a broken file). | 8 |
worksheet | object | Optional reference to a Spreadsheet_Excel_Writer_Worksheet object. You can leave this to null except if your workbook contains several worksheets and you want to fill a specific one. | null |
This driver does not support the flatten() method. You can not retrieve its output with DataGrid::getOutput(). You can either render it directly to the browser or save it to a file. See the "sendToBrowser" and "filename" options.
This driver has container support. You can use Structures_DataGrid::fill() with it; that's even recommended.
If PHP's safe_mode is enabled, Spreadsheet_Excel_Writer sometimes fails to generate the Excel file. You can avoid this problem by setting the 'tempDir' option to a (temporary) directory that is writable by PHP.
NOTE ABOUT FORMATTING:
You can specify some formatting with the 'headerFormat' and 'bodyFormat' options, or with setBodyFormat() and setHeaderFormat().
But beware of the following from the Spreadsheet_Excel_Writer manual: "Formats can't be created directly by a new call. You have to create a format using the addFormat() method from a Workbook, which associates your Format with this Workbook (you can't use the Format with another Workbook)."
What this means is that if you want to pass a format to this driver you have to "derive" the Format object out of the workbook used in the driver.
The easiest way to do this is:
// Create a workbook $workbook = new Spreadsheet_Excel_Writer(); // Specify that spreadsheet must be sent the browser $workbook->send('test.xls'); // Create your format $format_bold =& $workbook->addFormat(); $format_bold->setBold(); // Fill the workbook, passing the format as an option $options = array('headerFormat' => &$format_bold); $datagrid->fill($workbook, $options);
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | no |
Output Buffering | yes |
Direct Rendering | yes |
Streaming | yes |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
columnAttributes | array | Column cells attributes. This is an array of the form: array(fieldName => array(attribute => value, ...) ...) This option is only used by XML/HTML based drivers. | array() |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fieldAttribute | string | The name of the attribute for the field name. null stands for no attribute | null |
fieldTag | string | The name of the tag for each field inside a row, without brackets. The special value '{field}' is replaced by the field name. | '{field}' |
filename | string | Filename of the generated XML file; boolean false means that no filename will be sent | false |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
labelAttribute | string | The name of the attribute for the column label. null stands for no attribute | null |
numberAlign | bool | Whether to right-align numeric values. | true |
outerTag | string | The name of the tag for the datagrid, without brackets | 'DataGrid' |
rowTag | string | The name of the tag for each row, without brackets | 'Row' |
saveToFile | boolean | Whether the output should be saved on the local filesystem. Please note that the 'filename' option must be given if this option is set to true. | false |
useXMLDecl | bool | Whether the XML declaration string should be added to the output. The encoding attribute value will get set from the common "encoding" option. If you need to further customize the XML declaration (version, etc..), then please set "useXMLDecl" to false, and add your own declaration string. | true |
writeMode | string | The mode that is used in the internal fopen() calls. Useful e.g. when you want to append to existing file. C.p. the fopen() documentation for the allowed modes. | 'wb' |
This driver supports the following operation modes:
Mode | Supported? |
---|---|
Container Support | no |
Output Buffering | yes |
Direct Rendering | no |
Streaming | no |
Object Preserving | no |
This driver accepts the following options:
Option | Type | Description | Default Value |
---|---|---|---|
buildFooter | bool | Whether to build the footer. | true |
buildHeader | bool | Whether to build the header. | true |
defaultCellValue | string | What value to put by default into empty cells. | null |
defaultColumnValues | array | Per-column default cell value. This is an array of the form: array(fieldName => value, ...). | array() |
encoding | string | The content encoding. If the mbstring extension is present the default value is set from mb_internal_encoding(), otherwise it is ISO-8859-1. | 'ISO-8859-1' |
excludeVars | array | Variables to be removed from the generated HTTP queries. | array() |
extraVars | array | Variables to be added to the generated HTTP queries. | array() |
fillWithEmptyRows | bool | Ensures that all pages have the same number of rows. | false |
hideColumnLinks | array | By default sorting links are enabled on all columns. With this option it is possible to disable sorting links on specific columns. This is an array of the form: array(fieldName, ...). This option only affects drivers that support sorting. | array() |
numberAlign | bool | Whether to right-align numeric values. | true |
onMove | string | Name of a Javascript function to call on onclick/onsubmit events when the user is either paging or sorting the data. This function receives a single object argument of the form: { page: <page>, sort: [{field: <field>, direction: <direction>}, ...], data: <user_data> }. Remark: setting this option doesn't remove the href attribute, you should return false from your handler function to void it (eg: for AJAX, etc..). | null |
onMoveData | string | User data passed in the "data" member of the object argument passed to onMove. No JSON serialization is performed, this is assigned as a raw string to the "data" attribute. It's up to you to add quotes, slashes, etc... | '' |
selfPath | string | The complete path for sorting links | $_SERVER['PHP_SELF'] |
This renderer class will render a XUL listbox. For additional information on the XUL Listbox, refer to this url: http://www.xulplanet.com/references/elemref/ref_listbox.html
You have to setup your XUL document, just as you would with an HTML document. This driver will only generated the <listbox> element and content.
Using the XUL renderer
<?php
require_once 'Structures/DataGrid.php';
$datagrid =& new Structures_DataGrid(10);
$options = array('dsn' => 'mysql://username@localhost/mydatabase');
$datagrid->bind("SELECT * FROM mytable", $options);
header('Content-type: application/vnd.mozilla.xul+xml');
echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
echo "<?xml-stylesheet href=\"myStyle.css\" type=\"text/css\"?>\n";
echo "<window title=\"MyDataGrid\"
xmlns=\"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul\">\n";
$datagrid->render('XUL');
echo "</window>\n";
?>
Games_Chess is like a brilliant helper who can tell you anything you need to know about an abstract chess game. The only thing Games_Chess cannot do is play against you - it is not a chess engine. Games_Chess handles the logic a chessboard and parsing standard FEN (Forsyth-Edwards Notation) for describing a position as well as SAN (Standard Algebraic Notation) for describing individual moves. This package can be used as a backend driver for playing chess, or for validating and/or creating PGN files using the File_ChessPGN package (when it is completed)
Games_Chess provides a basic interface for validating and playing
chess. Games_Chess has facilities for calculating all of the important
chess rules including check, checkmate, stalemate, basic draws such as bishop+king
versus king, 50 move draw, en passant pawn captures, castling, double space
first pawn move and basic piece movement. In addition, Games_Chess can
set up a board to a position using the Forsyth-Edwards Notation (FEN
)
and can output a list of moves in Standard Algebraic Notation (SAN
)
as well as parse any valid SAN move, no matter how obscure (Qa3xf3, for instance),
as well as simple "move the piece on a3 to h3" commands.
The Games_Chess package comes with a demonstration file, which has highlighted source at This location.
The Games_Chess package provides three different drivers, one for playing a standard chess game, and two for playing interesting variant games that are popular on the Internet Chess Club (ICC).
Crazyhouse. basic rules in Crazyhouse chess allow you to place pieces you have captured from your opponent on the board as new pieces for your own army. This is a wild and highly tactical game.
Loser's Chess. Loser's chess is similar to checkers in that if a capture is possible, it must be executed. For this reason, most of the moves are forcing moves in this game, and it results in very fast games.
To use Games_Chess on the most basic level is simple. Here is a sample script showing the initialization of the three drivers:
<?php
require_once 'Games/Chess/Standard.php';
require_once 'Games/Chess/Crazyhouse.php';
require_once 'Games/Chess/Losers.php';
$standard = new Games_Chess_Standard;
$crazyhouse = new Games_Chess_Crazyhouse;
$losers = new Games_Chess_Losers;
?>
By default, a Games_Chess driver is created with a blank board. To set up a new starting position, use this code:
<?php
require_once 'Games/Chess/Standard.php';
$standard = new Games_Chess_Standard;
$standard->resetGame();
?>
If you wish to load a particular starting position in FEN notation, pass the FEN string to resetGame() like so:
<?php
require_once 'Games/Chess/Standard.php';
$standard = new Games_Chess_Standard;
$standard->resetGame('rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2');
?>
There are two basic methods for performing moves with Games_Chess, moveSAN and moveSquare. Both of these methods either return TRUE or a PEAR_Error class upon error, so the proper way to handle for a return value is:
<?php
require_once 'Games/Chess/Standard.php';
$standard = new Games_Chess_Standard;
$err = $standard->moveSAN('Nf9');
if ($standard->isError($err)) {
echo $err->getMessage();
die;
}
?>
Note that Games_Chess->isError() should be used as the PEAR_Error class is only loaded when needed, to help with efficiency. If this is not a concern (and it will only be for sites serving thousands of pages per second), it is simpler to set a PEAR_Error callback, which is demonstrated in the next example.
The example below demonstrates the two ways that Games_Chess should be called to make moves on the chess board, including the way to represent a placement move in the Crazyhouse variant:
<?php
require_once 'PEAR.php'; // for the PEAR_Error class
function showerror($err)
{
echo $err->getMessage();
exit;
}
require_once 'Games/Chess/Standard.php';
require_once 'Games/Chess/Crazyhouse.php';
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'showerror');
$standard = new Games_Chess_Standard;
$crazyhouse = new Games_Chess_Crazyhouse;
$standard->resetGame();
$crazyhouse->resetGame();
$standard->moveSAN('e4'); // 'e2' to 'e4' if using moveSquare
$standard->moveSquare('d7', 'd5'); // 'd5' if using moveSAN
$standard->moveSAN('exd5');
$crazyhouse->moveSquare('e2', 'e4'); // same as moveSAN() above
$crazyhouse->moveSAN('d5'); // save as moveSquare above
$crazyhouse->moveSAN('exd5');
$crazyhouse->moveSAN('Qxd5');
$crazyhouse->moveSAN('P@d7'); // dumb move, but demonstrates piece placement
?>
Note that if promoting a pawn, moveSquare() requires
that the newly promoted piece type be passed in.
Options are Q
for queen,
R
for rook, B
for bishop and
N
for knight.
<?php
require_once 'PEAR.php'; // for the PEAR_Error class
function showerror($err)
{
echo $err->getMessage();
exit;
}
require_once 'Games/Chess/Crazyhouse.php';
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'showerror');
$crazyhouse = new Games_Chess_Crazyhouse;
$crazyhouse->resetGame();
$crazyhouse->moveSAN('e4');
$crazyhouse->moveSAN('d5');
$crazyhouse->moveSAN('exd5');
$crazyhouse->moveSAN('Qxd5');
$crazyhouse->moveSAN('d3');
$crazyhouse->moveSAN('Kd7');
$crazyhouse->moveSAN('d4');
$crazyhouse->moveSAN('Ke6');
$crazyhouse->moveSAN('P@d7');
$crazyhouse->moveSAN('Qd6');
// $crazyhouse->moveSAN('d8=Q'); // normal way to do this
$crazyhouse->moveSquare('d7', 'd8', 'Q'); // using moveSquare to do this
// dumb moves, but demonstrates pawn promotion to queen
?>
To retrieve necessary information such as the current move list, current FEN, list of captured pieces (Crazyhouse only), determine whether the game is over and so on, you'll want to use one of the following methods:
gameOver().
This method can be used to determine if the game has concluded, and simply returns
an abbreviation of W
if the white pieces have won (checkmate),
B
if the black pieces have won (checkmate), D
if there is a draw, or false if the game is still in progress.
This method returns a Forsyth-Edwards Notation (FEN
)
representation of the current game state. Note that the FEN standard does
not contain a way to represent captured pieces, and so cannot be used to
exactly replicate a Crazyhouse game in progress, although it can be close.
For the Standard and Loser's chess drivers, this returns an associative array of
algebraic square name (a1
to h8
) mapped
to its contents. If the square is unoccupied, it will contain its algebraic
square name (a1
will map to a1
), otherwise
it will contain the piece name, one of P
, R
,
N
, B
, Q
or
K
. If the piece is a white piece, it will be in upper case,
like P
, and if the piece is a black piece, it will be in
lower case like p
.
For the Crazyhouse driver, this returns an associative array with two
indices, board
and captured
.
The board
element is identical to the return for
Standard/Loser's chess drivers described in the previous paragraph. The
captured
sub-array contains an array of this format:
<?php
$captured =
array(
'W' =>
array(
'P' => 6, // 6 black pawns captured by white
'R' => 0,
'N' => 0,
'B' => 0,
'Q' => 0,
),
'B' =>
array(
'P' => 4, // 4 white pawns captured by black
'R' => 0,
'N' => 0,
'B' => 0,
'Q' => 0,
),
);
?>
Each segment represents white's or black's pieces (W and B), and each sub-element represents the number of captured pieces of that type. In the example above, white can choose to place a pawn for up to the next 6 moves in a row, whereas black can choose to place a pawn for up to the next 4 moves in a row.
This methods simply returns the color of the pieces that have the next move.
W
is returned for white, B
is
returned for black.
Graph datastructure manipulation library
Structures_Graph is a package for creating and manipulating graph datastructures. It allows building of directed and undirected graphs, with data and metadata stored in nodes. The library provides functions for graph traversing as well as for characteristic extraction from the graph topology.
This package implements linked list structures. It supports both singly-linked lists (Structures_LinkedList_Single) and doubly-linked lists (Structures_LinkedList_Double).
Generic tree management, currently supports DB and XML as data sources
Provides methods to read and manipulate trees, which are stored in the DB or an XML file. The trees can be stored in the DB either as nested trees. Or as simple trees ('brain dead method'), which use parentId-like structure. Currently XML data can only be read from a file and accessed. The package offers a big number of methods to access and manipulate trees. For example methods like: getRoot, getChild[ren[Ids]], getParent[s[Ids]], getPath[ById] and many more.
Unfortunately, complete documentation is not available at the moment.
System Utilities
Returns locations of system folders like home, desktop, documents and others.
System_Folders provides methods to get the locations of various system folders like home directory, desktop folder and "My documents". You can use it on nearly every operating system: It works on Linux, Windows and Mac OS; allowing you to write OS independent programs.
The methods return a string of the directory (with trailing slash)
if the directory can be determined, and NULL
if
it fails or isn't available on the system. For example, the
"Shared Documents" folder exists on Windows only. If you run
getSharedDocuments() on a Mac or Linux, the method
will return NULL
.
The class does heavy use of environment variables. That means it is very likely that the methods fail when running in a php server module (e.g. apache) because there is user information available. Using them on command line (cli) scripts gives best results, as the class is meant to be used for such ones.
Folder | Method to use | Notes |
---|---|---|
All users directory | getAllUsers() | Windows only |
Application data | getAppData() | |
Desktop | getDesktop() | |
Documents / My Documents | getDocuments() | |
Home directory | getHome() | |
Programs folder (installed ones) | getPrograms() | |
Temporary files | getTemp() | |
Shared documents | getSharedDocuments() | Windows only |
Windows directory | getWindows() | Windows only |
On every getXXX() call, the whole procedure of looping through environment variables, checking the existence of the folders and trying some common locations is executed. So if you call the same method again and again, it will cost you the same amount of cpu cycles every time and perhaps be a slowdown in your application.
Another problem might be that some methods fail or don't return the correct path, e.g. because the user has an unusual installation or unusual preferences.
Both issues are addressed with System_Folders_Cached. It provides a cached version of the getXXX() methods, meaning that the result of the first method calls are stored locally and returned on every further call, which speeds up consecutive calls to the same method a lot.
Further, the class provides methods to set the folder locations and save this settings into an ini file. Saving them can be done with saveToFile() and they can be loaded with loadFromFile(). This allows customization of the folder locations, and persistency across sessions.
General usage
<?php
require_once 'System/Folders.php';
$sf = new System_Folders();
$arData = array(
'Username' => $sf->getUserName(),
'Home' => $sf->getHome(),
'Documents' => $sf->getDocuments(),
'Shared documents' => $sf->getSharedDocuments(),
'Temp' => $sf->getTemp(),
'Desktop' => $sf->getDesktop(),
'AppData' => $sf->getAppData(),
'Programs' => $sf->getPrograms(),
'Windows' => $sf->getWindows()
);
echo 'System: ' . $sf->getSys() . "\r\n";
var_dump($arData);
?>
At first, you need to instantitiate a new System_Folders object. The operating system is determined there, which is needed for all the other methods.
Then just use the getXXX()
methods to retrieve
the folder locations. Remember that they return NULL
if the location can't be determined.
General usage
<?php
require_once 'System/Folders/Cached.php';
$sf = new System_Folders_Cached();
//show the path of the config file
echo 'Config file: ' . $sf->getDefaultConfigFile() . "\r\n";
//load the stored settings from last time
$err = $sf->loadFromFile();
echo 'Home: ' . $sf->getHome() . "\r\n";
echo 'Documents: ' . $sf->getDocuments() . "\r\n";
echo "\r\n";
$doc = $sf->getDocuments();
if (file_exists($doc)) {
echo "Setting new Documents directory\r\n";
//Set an own documents directory
// and save the settings for next time
$sf->setDocuments('/strange/path/to/MyDocuments/');
$sf->saveToFile();
echo 'New Documents directory: ' . $sf->getDocuments() . "\r\n";
echo "Run this program again to reset the path to default\r\n";
} else {
//unset the path
echo "Unsetting the documents directory\r\n";
$sf->setDocuments(null);
$sf->saveToFile();
echo 'New Documents directory: ' . $sf->getDocuments() . "\r\n";
}
?>
This example displays the location of the config file (in which the folder settings are stored), shows some folder locations and sets the documents directory to a (probably non-existing) directory and saves the folder locations.
In the next program call, the documents directory is reset to the normal one and saved again.
Launch files with associated applications
By using the operating system's features for opening a file or URL, this package simplifies writing command-line tools that open HTML documentation et cetera. For instance, upon successful install, the "Getting started" URL could be loaded in the browser.
Open a local HTML file in the web browser
<?php
require_once 'System/Launcher.php';
$launcher = new System_Launcher;
$launcher->launch(dirname(__FILE__) . '/test.htm', true);
?>
At first, you need to instantitiate a new System_Launcher object. The operating system is determined there, which is needed for choosing a way of launching programs.
The launch() method tells the operating system to open the given file as it is associated. Browsers regularly open HTML documents, and image viewers open PNGs, and so on.
You can also feed it a URL instead of a file.
This package allows you to mount and unmount disks and partitions listed in your
file systems table (/etc/fstab
), which is found on Unix/Linux
based systems.
You can install this package by using pear install System_Mount. Once you have the package installed it is very easy to use.
System_Mount provides methods to mount and unmount
filesystems and partitions listed in a system's
/etc/fstab
. The fstab file is common on Unix/Linux
based systems and is used to list the available filesystems and partitions
which users can mount into the filesystem hierarchy. Common entries include
root partitions, boot partitions, as well as cdrom drives and other
removable media.
The System_Mount command utilizes File_Fstab to discover entries in the
/etc/fstab
file, and allow easy mount/unmount operations.
General usage
<?php
require_once 'System/Mount.php';
// Create the mount class
$m = new System_Mount();
// Get an object representing the CD-ROM entry
$cdrom = $m->getEntryForPath('/media/cdrom0');
if (PEAR::isError($cdrom)) {
die($cdrom->message."\n");
}
// Mount it
$res = $cdrom->mount();
if (PEAR::isError($res)) {
die($res->getMessage()."\n");
}
// List its contents
print `ls {$cdrom->mountPoint}`;
// Unmount it
$cdrom->unmount();
if (PEAR::isError($res)) {
die($res->getMessage()."\n");
}
?>
At first, you need to instantitiate a new System_Mount object.
Use the mount point for the corresponding entry in your
/etc/fstab
to obtain an object to mount,
then check if there are any errors. If there are no errors mount the cdrom.
Allows developers to create their own daemon applications on Linux systems using nothing but PHP.
Everyone knows PHP can be used to create websites. But it can also be used to create desktop applications and commandline tools. And now with a class called System_Daemon, you can even create daemons using nothing but PHP. And did I mention it was easy?
This how-to was ported from: http://kevin.vanzonneveld.net/techblog/article/create_daemons_in_php/. you can leave a comment there as well.
A daemon is a Linux program that run in the background, just
like a 'Service' on Windows. It can perform all sorts of tasks
that do not require direct user input. Apache is a daemon, so
is MySQL. All you ever hear from them is found in somewhere in
/var/log
, yet they silently power over 40%
of the Internet.
You reading this page, would not have been possible without them.
So clearly: a daemon is a powerful thing, and can be bend to do a
lot of different tasks.
Most daemons are written in C. It's fast & robust. But if you are in a LAMP oriented company like me, and you need to create a lot of software in PHP anyway, it makes sense:
Efficiency. Reuse & connect existing code Think of database connections, classes that create customers from your CRM, etc.
Speed. Deliver new applications very fast PHP has a lot of build in functions that speed up development greatly.
Continuity. Everyone knows PHP (right?) If you work in a small company: chances are there are more PHP programmers than there are C programmers. What if your C guy abandons ship?
Website optimization If you're running a (large) website, jobs that do heavy lifting should be taken away from the user interface and scheduled to run on the machine separately.
Log parser Continually scan logfiles and import critical messages in your database.
SMS daemon Read a database queue, and let your little daemon interface with your SMS provider. If it fails, it can easily try again later!
Video converter (think Youtube) Scan a queue/directory for incoming video uploads. Make some system calls to ffmpeg to finally store them as Flash video files. Surely you don't want to convert video files right after the upload, blocking the user interface that long? No: the daemon will send the uploader a mail when the conversion is done, and proceed with the next scheduled upload
Some people use cronjobs for the same Possible use cases. Crontab is fine but it only allows you to run a PHP file every minute or so.
What if the previous run hasn't finished yet? Overlap can seriously damage your data & cause siginificant load on your machines.
What if you can't afford to wait a minute for the cronjob to run? Maybe you need to trigger a process the moment a record is inserted?
What if you want to keep track of multiple 'runs' and store data in memory.
What if you need to keep your application listening (on a socket for example)
Cronjobs are a bit rude for this, they may spin out of control and don't provide a framework for logging, etc. Creating a daemon would offer more elegance & possibilities. Let's just say: there are very good reasons why a Linux OS isn't composed entirely of Cronjobs :)
Nerd alert!) When a daemon program is started, it fires up a second child process, detaches it, and then the parent process dies. This is called forking. Because the parent process dies, it will give the console back and it will look like nothing has happened. But wait: the child process is still running. Even if you close your terminal, the child continues to run in memory, until it either stops, crashes or is killed. In PHP: forking can be achieved by using the Process Control Extensions. Getting a good grip on it, may take some studying though.
Because the Process Control Extensions' documentation is a bit rough, I decided to figure it out once, and then wrap my knowledge and the required code inside a PEAR class called: System_Daemon. And so now you can just:
System_Daemon Usage
<?php
require_once "System/Daemon.php"; // Include the Class
System_Daemon::setOption("appName", "mydaemon"); // Minimum configuration
System_Daemon::start(); // Spawn Deamon!
?>
And that's all there is to it. The code after that, will run in your server's background. So next, if you create a while(true) loop around that code, the code will run indefinitely. Remember to build in a sleep(5) to ease up on system resources.
Features & Characteristics Here's a grab of System_Daemon's features:
Daemonize any PHP-CLI script
Simple syntax
Driver based Operating System detection
Unix only
Additional features for Debian / Ubuntu based systems like:
Automatically generate startup files (init.d)
Support for PEAR's Log package
Can run with PEAR (more elegance & functionality) or without PEAR (for standalone packages)
Default signal handlers, but optionally reroute signals to your own handlers.
Set options like max RAM usage
To get the feeling how System_Daemon is used, try out the following examples.
If you run this code successfully, a daemon will be spawned
and stopped directly. You should find a log enty in
/var/log/simple.log
#!/usr/bin/php -q
<?php
/**
* System_Daemon turns PHP-CLI scripts into daemons.
*
* PHP version 5
*
* @category System
* @package System_Daemon
* @author Kevin <kevin@vanzonneveld.net>
* @copyright 2008 Kevin van Zonneveld
* @license http://www.opensource.org/licenses/bsd-license.php New BSD Licence
* @version SVN: Release: $Id: simple.php 276201 2009-02-20 16:55:07Z kvz $
* @link http://trac.plutonia.nl/projects/system_daemon
*/
/**
* System_Daemon Example Code
*
* If you run this code successfully, a daemon will be spawned
* and stopped directly. You should find a log enty in
* /var/log/simple.log
*
*/
// Make it possible to test in source directory
// This is for PEAR developers only
ini_set('include_path', ini_get('include_path').':..');
// Include Class
error_reporting(E_ALL);
require_once "System/Daemon.php";
// Bare minimum setup
System_Daemon::setOption("appName", "simple");
System_Daemon::setOption("authorEmail", "kevin@vanzonneveld.net");
//System_Daemon::setOption("appDir", dirname(__FILE__));
System_Daemon::log(System_Daemon::LOG_INFO, "Daemon not yet started so ".
"this will be written on-screen");
// Spawn Deamon!
System_Daemon::start();
System_Daemon::log(System_Daemon::LOG_INFO, "Daemon: '".
System_Daemon::getOption("appName").
"' spawned! This will be written to ".
System_Daemon::getOption("logLocation"));
// Your normal PHP code goes here. Only the code will run in the background
// so you can close your terminal session, and the application will
// still run.
System_Daemon::stop();
?>
Now that we've created an example daemon, it's time to fire it up! I'm going to assume the name of your daemon is logparser. This can be changed with the statement:
<?php
System_Daemon::setOption("appName", "logparser")
?>
But the name of the daemon is very important because it is also used in filenames (like the logfile).
Execute Just make your daemon script executable, and then execute it:
chmod a+x ./logparser.php ./logparser.php
Check Your daemon has no way of communicating through your console, so check for messages in:
tail /var/log/logparser.log
And see if it's still running:
ps uf -C logparser.php
Kill Without the start/stop files (see below for howto), you need to:
killall -9 logparser.php
Autch.. Let's work on those start / stop files, right?
Start / Stop files (Debian & Ubuntu only) Real daemons have an init.d file. Remember you can restart Apache with the following statement?
/etc/init.d/apache2 restart
That's a lot better than killing. So you should be able to control your own daemon like this as well:
/etc/init.d/logparser stop /etc/init.d/logparser start
Well with System_Daemon you can write autostartup files using the writeAutoRun() method, look:
<?php
$path = System_Daemon::writeAutoRun();
?>
On success, this will return the path to the autostartup file: /etc/init.d/logparser, and you're good to go!
Run on boot Surely you want your daemon to run at system boot.. So on Debian & Ubuntu you could type:
update-rc.d logparser defaults
Done your daemon now starts every time your server boots. Cancel it with:
update-rc.d -f logparser remove
Here are some issues you may encounter down the road.
Connect to MySQL after you start() the daemon. Otherwise only the parent process will have a MySQL connection, and since that dies.. It's lost and you will get a 'MySQL has gone away' error.
Error handling Good error handling is imperative. Daemons are often mission critical applications and you don't want an uncatched error to bring it to it's knees.
Reconnect to MySQL A connection may be interrupted. Think about network downtime or lock-ups when your database server makes backups. Whatever the cause: You don't want your daemon to die for this, let it try again later.
Write your own php error handler It should forward all the PHP errors to the log() method, so they end up in your logfile. Otherwise it's very hard to spot errors, since the daemon has no way of writing to your console anymore! See set_error_handler.
Monit Monit is a standalone program that can kickstart any daemon, based on your parameters. Should your daemon fail, monit will mail you and try to restart it.
I know I'm saying MySQL a lot, but you can obviously replace that with Oracle, MSSQL, PostgreSQL, etc.
The package provides methods to monitor system process on Unix-like systems.
System::ProcWatch is a small collection of classes to ease monitoring of system processes based on the Unix program procps (ps).
To use System::ProcWatch you simply have to define a ruleset on which based System::ProcWatch operates. A rule (or job, watch) defines what actions should happen if a condition evaluates to true.
To be continued...
You can use System::ProcWatch out of the box, by utilizing the shipped shell scripts procwatch and procwatch-lint.
The procwatch command is meant to be used for system diagnosis - run as daemon or by cron.
The usage is best described by the output of procwatch -h:
The procwatch-lint is meant to validate procwatchs configurtation files written in XML by utilizing XML::DTD::XmlValidator.
Once again synopsis is best described by the output of procwatch-lint -h:
There are three methods to configure your System::ProcWatch application:
Configuring System::ProcWatch by XML is the preferred way. It is as simple as powerful.
The root element of an XML configuration file/string is the procwatch element. It has one implicit attribute, the version attribute, set to "1.0".
The procwatch element symbolizes our ruleset, and the childs of the root are our rules.
The direct descendants of the root element procwatch, are the watch elements, which can occur 1 time or more often.
The watch element has one required attribute, the name attribute, which gives the watch (or job, rule) a descriptive name like "httpd-count".
Each watch element symbolizes a single rule, containing a regular expression to search for, one or more conditions to evaluate and one or more actions to be taken.
A rule consists of three child elements:
The pattern element describes the perl compatible regular expression which should be evaluated against a column of the output of ps.
There is one required attribute, the match attribute, defining the column name of the output of ps in lowercase like "command" or "vsz".
This makes System::ProcWatch highliy versatile and should make it usable with any platforms procps program.
The pattern elements content solely consists of the perl compatible regular expression to match against the column defined in the match attribute. The PCRE MUST contain the start and end delimiter and MAY contain any PCRE modifiers.
Example: <pattern match="command">/sbin\/httpd/</pattern>
The condition element defines conditions that MUST evaluate to TRUE at all so that later defined actions will be executed.
The condition element has one required and one optional attribute, the required one being type, which MUST be one of "presence" or "attr", and the optional one being attr. However, if the type attribute equals to "attr" the attr attribute MUST be present.
The attr attribute represents a column of procps' output like "user" or "%mem".
Dependent on the content of the type attribute, syntax and behavior of the condition element differ.
A condition with type "presence" MAY be empty, thus always evaluating to true.
Child Elements of condition may be:
You may combine them to define for instance a range from min to max.
The execute element defines actions that should be taken if the condition applies.
It has one required attribute, the type attribute, which MUST equal to one of "shell" or "php".
Obviously the content of the execute element is executed either on the shell through shell_exec() or directly in PHP through eval().
The execute element MAY occur any times.
There are some special variables that will automagically be available in execute statements:
XML configuration file
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE procwatch SYSTEM "/usr/share/pear/data/System_ProcWatch/procwatch-1_0.dtd"> <procwatch> <!-- SOME EXAMPLE CONFIGURATIONS =========================== This job looks for the count of running httpd processes by matching the PCRE "/httpd/" against the COMMAND column of ps. If there are less than 10 or more than 30 httpd processes found the speicified string is executed on the shell. --> <watch name="httpd-count"> <pattern match="command">/httpd/</pattern> <condition type="presence"> <min>10</min> <max>30</max> </condition> <execute type="shell">echo $msg $pids >> /var/log/procwatch</execute> </watch> <!-- This job looks for the amount of physical memory all httpd processes use together by matching the PCRE "/httpd/" against the COMMAND column of ps. It adds all %MEM columns of ps that match the pattern together and compares the reslut to the specified sum. If the result exceeds the sum the specified string is executet on the shell. --> <watch name="httpd-usage"> <pattern match="command">/httpd/</pattern> <condition type="attr" attr="%mem"> <sum>5</sum> </condition> <execute type="shell">echo $msg $pids >> /var/log/procwatch</execute> </watch> <!-- This job looks for zombie processes. It matches the PCRE "/Z/" against the STAT column of ps and executes the specified string on the shell if more than 0 zombies have been found. --> <watch name="ZOMBIES"> <pattern match="stat">/Z/</pattern> <condition type="presence"> <max>0</max> </condition> <execute type="shell">echo $msg $pids >> /var/log/procwatch</execute> </watch> <!-- This job looks for running processes. It matches the PCRE pattern "/R/" against the STAT column of ps and executes the specified string on the shell if any running processes have been found. --> <watch name="running"> <pattern match="stat">/R/</pattern> <condition type="presence" /> <execute type="shell">echo $msg $pids >> /var/log/procwatch</execute> </watch> </procwatch>
Configuring by INI file is effectively the same except that only executes of type "shell" can be defined.
INI configuration file
A valid array to configure System::ProcWatch may look similar to the following example.
PHP configuration array
<?php
$watches = array();
$watches['job1'] = array();
$watches['job1']['pattern'] = array();
$watches['job1']['condition'] = array();
$watches['job1']['execute'] = array();
$watches['job1']['pattern']['command'] = '/httpd/';
$watches['job1']['condition']['presence'] = array('min' => 10, 'max' => 100);
$watches['job1']['execute']['shell'] = array('/usr/bin/mail2admin $msg');
$watches['job2'] = array();
// ...
?>
This constants are mostly used internally by System_ProcWatch
Name |
---|
SYSTEM_PROCWATCH_IS |
SYSTEM_PROCWATCH_ISNOT |
SYSTEM_PROCWATCH_MAX |
SYSTEM_PROCWATCH_MIN |
SYSTEM_PROCWATCH_PRESENCE |
SYSTEM_PROCWATCH_PRESENCE_MAX |
SYSTEM_PROCWATCH_PRESENCE_MIN |
SYSTEM_PROCWATCH_SUM |
Monitor processes
Usage:
<?php
1 require_once 'System/ProcWatch.php';
2 require_once 'System/ProcWatch/Config.php';
3
4 $cf =
?>
System_ProcWatch_Config
<?php
::
?>
fromXmlFile
<?php
('/etc/procwatch.xml');
5 $pw = &new
?>
System_ProcWatch
<?php
($cf);
6 $pw->
?>
run
<?php
();
?>
object &new System_ProcWatch (
array $config
)
Instantiate a new System_ProcWatch object configured by the supplied configuration array.
void System_ProcWatch::run (
string $ps_args = 'aux'
)
Run once.
$ps_args
arguments that should be passed to ps
Throws no exception.
This function can not be called statically.
void System_ProcWatch::daemon (
int $interval
, string $ps_args = 'aux'
)
Runs System_ProcWatch in daemon mode with the defined interval of seconds to sleep.
$interval
seconds to sleep
$ps_args
ps' arguments
Throws no exception.
This function can not be called statically.
void System_ProcWatch::setConfig (
array $config
)
Configure System_ProcWatch with an config array from System_ProcWatch_Config.
Throws no exception.
This function can not be called statically.
Build a configuration array for System_ProcWatch
Usage:
1 $cf =
System_ProcWatch_Config::
fromXmlFile('/etc/procwatch.xml');
2 $pw = &new
System_ProcWatch($cf);
mixed System_ProcWatch_Config::fromXml (
string $xml
)
Parses an XML string into an config array to configure System_ProcWatch with.
$xml
XML string
Returns config array on success or PEAR_Error on failure.
Throws PEAR_Error if XML file does not exist or problems parsing the XML string have occured.
This function should be called statically.
mixed System_ProcWatch_Config::fromXmlFile (
string $file
)
Parses an XML file into an config array to configure System_ProcWatch with.
$file
path to XML file
Returns config array on success or PEAR_Error on failure.
Throws PEAR_Error if XML file does not exist, or problems parsing the XML file have occured.
This function should be called statically.
mixed System_ProcWatch_Config::fromIniFile (
string $file
)
Parses an INI file into an array to configure System_ProcWatch with.
INI configuration file
$file
path to INI file
Returns config array on success or PEAR_Error on failure.
Throws PEAR_Error if INI file doesn't exist.
This function should be called statically.
mixed System_ProcWatch_Config::fromArray (
mixed $array
)
This method in fact does a sanity check on the supplied config array and should only be used for testing purposes.
$array
config array to check
Returns the same array on success or PEAR_Error on failure.
Throws PEAR_Error if an invalid configuration array was supplied.
This function should be called statically.
Fetches output from `ps` and parses it into an associative array
Usage:
object &new System_ProcWatch_Parser (
string $ps_args = 'aux'
)
Instantiates a new System_ProcWatch parser object with the supplied arguments to pass to ps.
$ps_args
ps' arguments
Throws no exception.
array &System_ProcWatch_Parser::getParsedData (
string $ps_args = 'aux'
, bool $refresh
= false
)
This is the main method of this class. It fetches to output of ps, executed on the shell, and returns the parsed data as an 2 dimensional indexed and associatve array.
$ps_args
ps' arguments
$refresh
whether to refresh our data
Returns array of processes parsed from ps' output.
Throws no exception.
This function can not be called statically.
string System_ProcWatch_Parser::fetch (
string $ps_args = ''
)
Fetches the output of ps, executed on the shell.
$ps_args
ps' arguments
Returns string ps' output.
Throws no exception.
This function can not be called statically.
array System_ProcWatch_Parser::parse (
string $data
)
Parses the output of ps.
$data
output of ps
Returns array of processes.
Throws no exception.
This function can not be called statically.
array System_ProcWatch_Parser::getProcByPid (
int $pid
)
Get information about a process by its PID.
$pid
the PID of the process
Returns array with information about the process with the supplied PID.
Throws no exception.
This function can not be called statically.
array System_ProcWatch_Parser::getProcInfo (
string $pattern
, string $search
)
Get information about processes matching the defined search.
$pattern
PCRE to match for process
$search
the ps field/column to search in
Returns array of processes matching the search.
Throws no exception.
This function can not be called statically.
Provides functions to enumerate root directories ("Drives") on Windows systems by using win32 api calls.
System_WinDrives uses win32 api calls to get a list of all installed roots ("Drives") on the current Windows operating system (The folders "A:\" until "Z:\").
Additional to the list of drives, detail information like the name and the drive type (network, harddisk, removable etc) is given.
The win32 api functions are used to access the Windows application programming interface. This means that the extension php_w32api.dll on PHP4 and php_ffi.dll on PHP5 have to be installed to use all functions. If no dll is available, the drives are guessed, but drive types and drive names can't be determined.
General usage
<?php
require_once 'System/WinDrives.php';
//if you want to read the names, pass "true" as first parameter
//this may crash your php.exe, so it's disabled by default
$wd = new System_WinDrives(false);
echo 'API available: ';
echo $wd->isApiAvailable() ? 'yes' : 'no';
echo "\r\n";
$arInfo = $wd->getDrivesInformation();
foreach ($arInfo as $strDrive => $objInfo) {
echo $strDrive . "\r\n";
echo ' Type: ' . $objInfo->type . "\r\n";
echo ' Type title: ' . $objInfo->typetitle . "\r\n";
echo ' Name: ' . $objInfo->name . "\r\n";
?>
At first, you need to instantitiate a new System_WinDrives object. You can pass "true" as parameter to enable hard disk names, but that can crash PHP and is disabled by default.
The next thing to do is checking if the class can be used.
That depends on the available extensions (win32api on PHP4,
and php_ffi on PHP5). If the isApiAvailable
method returns true, everything is ok.
Now that everything is ok, you can read the driver information
via getDrivesInformation
and display the output.
In case the API is not available, the method tries to guess the drive list by checking if the root directories exist.
Name | Value | Description |
---|---|---|
SYSTEM_WINDRIVE_ERROR | 1 | Drive type couldn't be determined |
SYSTEM_WINDRIVE_REMOVABLE | 2 | Removable (Floppy, ZIP) |
SYSTEM_WINDRIVE_FIXED | 3 | Harddisk |
SYSTEM_WINDRIVE_REMOTE | 4 | Network drive |
SYSTEM_WINDRIVE_CDROM | 5 | CD/DVD drive |
SYSTEM_WINDRIVE_RAMDISK | 6 | Virtual disk in the RAM |
System_WinDrives System_WinDrives (
boolean $bReadName = false
)
Constructor. Creates a new System_WinDrives instance.
$bReadName
If the titles/names of drives shall be read. It is disabled by default, because it might cause PHP to crash.
array System_WinDrives::getDriveList (
void
)
Returns an array with all drive paths (e.g. A:\
,
C:\
).
If no API is available, the drive list is guessed. To get more data (like drive type and name), you should use getDrivesInformation().
array - array with all the drives
Using myFunction()
<?php
require_once 'System/WinDrives.php';
$swd = new System_WinDrives();
$arPaths = $swd->getDriveList();
foreach ($arPaths as $strPath) {
echo $strPath . "\r\n";//echoes things like "A:\" and "C:\"
}
?>
Output:
A:\
C:\
string System_WinDrives::getDriveName (
string $strDrive
)
Returns the user-given name for the given drive. The
function does not work on PHP5 and returns an empty string
''
.
$strDrive
The drive path, whose name shall be read.
string - The drive label
Using myFunction()
<?php
require_once 'System/WinDrives.php';
$swd = new System_WinDrives();
echo $swd->getDriveName('C:\\');
?>
Output:
Windrive
array System_WinDrives::getDrivesInformation (
void
)
Returns an array with all pieces of information one can get for the drives.
array - Array of little objects. Key is the drive path, the value
is an object with the following values: type
,
name
and typetitle
.
Using myFunction()
<?php
require_once 'System/WinDrives.php';
$swd = new System_WinDrives();
$arInfo = $swd->getDrivesInformation();
foreach ($arInfo as $strPath => $objInfo) {
echo 'Drive ' . $strPath . "\r\n";
echo ' type: ' . $objInfo->type . '(' . $objInfo->typetitle . ")\r\n";
echo ' name: ' . $objInfo->name . "\r\n";
}
?>
Output:
Drive A:\
2 (Removable)
diskette
Drive C:\
3 Harddisk
Windisk
int System_WinDrives::getDriveType (
string $strDrive
)
Returns the type of a drive.
Value | Constant | Meaning |
---|---|---|
1 | SYSTEM_WINDRIVE_ERROR | Non-existent drive or other error |
2 | SYSTEM_WINDRIVE_REMOVABLE | Removable drive (e.g. floppy) |
3 | SYSTEM_WINDRIVE_FIXED | Harddisk |
4 | SYSTEM_WINDRIVE_REMOTE | Network share |
5 | SYSTEM_WINDRIVE_CDROM | CD-Rom or DVD |
6 | SYSTEM_WINDRIVE_RAMDISK | RAM disk |
$strDrive
The drive that type shall be returned.
integer - the type (use the constants for comparison)
boolean System_WinDrives::getReadName (
void
)
Returns of drive names should be read or not.
boolean - true if they shall be read, false if not.
string System_WinDrives::getTypeTitle (
int $nType
)
Gets the readable name to a drive type, e.g. "Harddisk" for
the type 3
(SYSTEM_WINDRIVE_FIXED
).
$nType
The type whose name shall be returned
string - the name for the type
array System_WinDrives::guessDriveList (
void
)
Tries to guess the drive list by brute-force checking all letters.
array - Array with all found drive paths.
boolean System_WinDrives::isApiAvailable (
void
)
Checks if the API methods to read the drive list are available, and returns that.
They may not be available if the necessary extensions (win32api on PHP4, php_ffi on PHP5) are not installed on the system.
If the API is not available, the drive list is brute-force guessed. Drive types are not available in that case.
boolean - true if they are there, false if not.
System_WinDrives::setReadName (
boolean $bReadName
)
Set, if drive names shall be read or not. Drive name reading may crash PHP, so this is disabled by default. You can enable label reading with this method.
$bReadName
If the names shall be read or not
Packages for text manipulation and analysis.
Implementation of CAPTCHAs (Completely Automated Public Turing tests to tell Computers and Humans Apart).
This package provides the functionality to create CAPTCHAs (Completely Automated Public Turing tests to tell Computers and Humans Apart). Features include:
The package creates CAPTCHAs; due to the stateless nature of the HTTP protocol, the logic to secure a webpage using the package must be specifically implemented. See the usage example for detailled information.
Currently, the graphical CAPTCHA driver depends on Image_Text which in turn requires TTF support to be compiled into PHP.
The following example implements the standard use of a CAPTCHA: Submitted form data is only evaluated when a CAPTCHA has been solved correctly.
Creating a CAPTCHA
The following code creates a CAPTCHA, provides the relevant information for the package, anhd retrieves the CAPTCHA as a PNG image.
<?php
require_once 'Text/CAPTCHA.php';
// Set CAPTCHA options (font must exist!)
$imageOptions = array(
'font_size' => 24,
'font_path' => './',
'font_file' => 'COUR.TTF',
'text_color' => '#DDFF99',
'lines_color' => '#CCEEDD',
'background_color' => '#555555'
);
// Set CAPTCHA options
$options = array(
'width' => 200,
'height' => 80,
'output' => 'png',
'imageOptions' => $imageOptions
);
// Generate a new Text_CAPTCHA object, Image driver
$c = Text_CAPTCHA::factory('Image');
$retval = $c->init($options);
if (PEAR::isError($retval)) {
printf('Error initializing CAPTCHA: %s!',
$retval->getMessage());
exit;
}
// Get CAPTCHA secret passphrase
$_SESSION['phrase'] = $c->getPhrase();
// Get CAPTCHA image (as PNG)
$png = $c->getCAPTCHAAsPNG();
if (PEAR::isError($png)) {
echo 'Error generating CAPTCHA!';
echo $png->getMessage();
exit;
}
file_put_contents(md5(session_id()) . '.png', $png);
?>
Securing a form with a CAPTCHA
The following code implements the functionality to check whether a CAPTCHA was solved correctly or not. for this, the CAPTCHA's phrase is stored in a session variable to retain this information between requests. It is important to unset the session after solving the CAPTCHA to avoid the reuse of the session ID.
<?php
session_start();
$ok = false;
$msg = 'Please enter the text in the image in the field below!';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_POST['phrase']) && is_string($_POST['phrase']) && isset($_SESSION['phrase']) &&
strlen($_POST['phrase']) > 0 && strlen($_SESSION['phrase']) > 0 &&
$_POST['phrase'] == $_SESSION['phrase']) {
$msg = 'OK!';
$ok = true;
unset($_SESSION['phrase']);
} else {
$msg = 'Please try again!';
}
unlink(md5(session_id()) . '.png');
}
print "<p>$msg</p>";
if (!$ok) {
// create the CAPTCHA as seen above
// and send it to the client
}
?>
See the file CAPTCHA_test.php
in the package distribution for a full, working
example (GD and TTF support required).
Text_CAPTCHA provides information about the CAPTCHA and makes its inner workings accessible using several functions defined in the base class; the specific implementation of them is up to the driver.
Implementation of mathematic operation CAPTCHAs (Completely Automated Public Turing tests to tell Computers and Humans Apart).
This package provides the functionality to create Numeral CAPTCHAs (Completely Automated Public Turing tests to tell Computers and Humans Apart). Features include:
The package creates numeral CAPTCHAs; due to the stateless nature of the HTTP protocol, the logic to secure a webpage using the package must be specifically implemented. See the usage example for detailled information.
The following example implements the standard use of a CAPTCHA: Submitted form data is only evaluated when a CAPTCHA has been solved correctly.
Creating a Numeral CAPTCHA
This example is just to show you how to generate a simple mathematic operation with Text_CAPTCHA_Numeral.
<?php
require_once 'Text/CAPTCHA/Numeral.php';
$num = new Text_CAPTCHA_Numeral;
$operation = $num->getOperation();
/**
* This will print the mathematical operation
* that has been generated by the package.
*/
print $operation;
?>
Securing a form with a Numeral CAPTCHA
This example will show you how to secure a form using the numeral captcha. It generates an operation and stores its result into a session variable.
<?php
require_once 'Text/CAPTCHA/Numeral.php';
$numcap = new Text_CAPTCHA_Numeral;
if (isset($_POST['captcha']) && isset($_SESSION['answer'])) {
if ($_POST['captcha'] == $_SESSION['answer']) {
$errors[] = 'Ok... You might be human...';
} else {
$errors[] = 'You are dumb or not human';
}
}
if (!empty($errors)) {
foreach ($errors as $error) {
print "<h1><font color='red'>$error</font></h1><br />";
}
}
print '
<form name="capter" action="index.php?page=liveExample" method="post">
<table>
<tr>
<th>What is this result pilgrim?: '.$numcap->getOperation().'</th>
<td><input type="text" value="" name="captcha" /></td>
</tr>
<tr>
<th/>
<td><input type="submit" value="Let me prove you that I am human!" /></td>
</tr>
</form>
';
$_SESSION['answer'] = $numcap->getAnswer();
?>
Text_CAPTCHA_Numeral gives you the ability to generate numerical mathematical captchas.
Generate and display difference analysis between text based files.
Text_Diff helps you generating difference views between two or three text files.
@@ -1,3 +1,3 @@ This line is the same. -This line is different in 1.txt +This line is different in 2.txt This line is the same.
Diffs are created in two steps:
In Text_Diff,
an Engine is used to compute the difference
between the files/strings. The packages contains several of them:
native,
shell,
string
and xdiff.
One should use auto
to let
Text_Diff decide which one is
the best for the system.
The second part, creating the output, is made possible by renderers. Text_Diff comes with context , inline and unified renderers.
Context renderer
<?php
require_once 'Text/Diff.php';
require_once 'Text/Diff/Renderer/context.php';
$lines1 = file('1.htm');
$lines2 = file('2.htm');
$diff = new Text_Diff('auto', array($lines1, $lines2));
$renderer = new Text_Diff_Renderer_context();
echo $renderer->render($diff);
?>
generates the following output:
*************** *** 3,13 **** <head> ! <title>PEAR Bug Tracking</title> </head> <body> <h1>PEAR Bug Tracking System</h1> ! <div class="bug-box"> ! <h2>Report new Bug</h2> ! <p>Got a test case?</p> ! ! <p>Reproducable steps?</p> </div> </body> </html> --- 3,13 ---- <head> ! <title>PEAR Bug Tracker</title> </head> <body> <h1>PEAR Bug Tracking System</h1> ! <div class="bug-box bug-box-first"> ! <h2>Report New Bug</h2> ! <p> ! Got a test case or reproducible steps? ! </p> </div> </body> </html>
Inline renderer
<?php
require_once 'Text/Diff.php';
require_once 'Text/Diff/Renderer/inline.php';
$lines1 = file('1.htm');
$lines2 = file('2.htm');
$diff = new Text_Diff('auto', array($lines1, $lines2));
$renderer = new Text_Diff_Renderer_inline();
echo $renderer->render($diff);
?>
generates the following output:
<?xml version="1.0" encoding="utf-8"?> <html> <head> <title>PEAR Bug <del>Tracking</title></del><ins>Tracker</title></ins> </head> <body> <h1>PEAR Bug Tracking System</h1> <div <del>class="bug-box"></del><ins>class="bug-box bug-box-first"></ins> <h2>Report <del>new</del><ins>New</ins> Bug</h2><del> <p>Got</del><ins> <p> Got</ins> a test <del>case?</p> <p>Reproducable steps?</p></del><ins>case or reproducible steps? </p></ins> </div> </body> </html>
Unified renderer
<?php
require_once 'Text/Diff.php';
require_once 'Text/Diff/Renderer/unified.php';
$lines1 = file('1.htm');
$lines2 = file('2.htm');
$diff = new Text_Diff('auto', array($lines1, $lines2));
$renderer = new Text_Diff_Renderer_unified();
echo $renderer->render($diff);
?>
generates the following output:
@@ -3,13 +3,13 @@ <head> - <title>PEAR Bug Tracking</title> + <title>PEAR Bug Tracker</title> </head> <body> <h1>PEAR Bug Tracking System</h1> - <div class="bug-box"> - <h2>Report new Bug</h2> - <p>Got a test case?</p> - - <p>Reproducable steps?</p> + <div class="bug-box bug-box-first"> + <h2>Report New Bug</h2> + <p> + Got a test case or reproducible steps? + </p> </div> </body> </html>
Together with Console_Color, it is possible to use Text_Diff to generate colored diffs on an ANSI terminal.
<?php
require_once 'Console/Color.php';
require_once 'Text/Diff.php';
require_once 'Text/Diff/Renderer/inline.php';
$lines1 = file('1.htm');
$lines2 = file('2.htm');
$diff = new Text_Diff('auto', array($lines1, $lines2));
$renderer = new Text_Diff_Renderer_inline(
array(
'ins_prefix' => '%g',
'ins_suffix' => '%n',
'del_prefix' => '%r',
'del_suffix' => '%n',
)
);
echo Console_Color::convert(
htmlspecialchars_decode(
$renderer->render($diff)
)
);
?>
is displayed like that on a shell:
Render text using FIGlet fonts.
With Text_Figlet you can create ASCII-art like text using FIGlet font description files (FIGlet fonts). The package supports horizontal smushing, German symbols (e.g. äöü), RTL and LTR writing directions as well as incomplete and gzipped fonts (as long as php's zlib extension is installed).
"Hello, world!" written using a FIGlet font
.
/ / / /__ / / /___ _ ______ _____/ /___/ / /
/ /_/ / _ \/ / / __ \ | | /| / / __ \/ ___/ / __ / /
/ __ / __/ / / /_/ / | |/ |/ / /_/ / / / / /_/ /_/
/_/ /_/\___/_/_/\____( ) |__/|__/\____/_/ /_/\__,_(_)
|/
Text_Figlet has only two methods you can use: loadFont() to select a FIGlet font file that will be used later on, and lineEcho() that actually returns the passed text, rendered with the previously selected font.
loadFont() takes the name/path of the FIGlet font file
(*.flf
or *.flf.gz
) as first parameter,
and the option if German characters should also be loaded as second, optional
parameter (defaults to true
).
The method either returns true
if the font could
be loaded, or a PEAR_Error in case of a problem.
lineEcho() can be called (multiple times if needed) after
loading the font with loadFont(). The method takes the text
that should be rendered using the font as first parameter, and an optional
second parameter that determines if the text should be outputted in HTML
text. In this case, special characters are escaped and everything is setup
so that the text will look correctly. This parameter defaults to
false
. You should also wrap the line into <pre> tags,
since they will monospace the text. Unlike the name implies, the rendered text
is not echoed but returned.
Rendering "Hello, world!" using PHP
<?php
require_once 'Text/Figlet.php';
$figlet = new Text_Figlet();
$error = $figlet->LoadFont('slant.flf');
if (PEAR::isError($error)) {
echo 'Error: ' . $error->getMessage() . "\n";
} else {
echo $figlet->LineEcho('Hello, world!') . "\n";
}
?>
The Text_Figlet package already ships some fonts.
They are stored in its data directory which can be found calling
pear config-get data_dir, and appending
Text_Figlet/fonts/
to it. It will contain the following
fonts:
3-d.flf
alligator2.flf
bell.flf
block.flf
contessa.flf
cybermedium.flf
isometric1.flf
larry3d.flf
script.flf
slant.flf
Passing one of the font names to loadFont() will automatically look them up in the Text_Figlet font data directory if they are not found.
Provides functionality to perform syntax highlighting for different file formats.
With Text_Highlighter it is possible to create syntax highlighted versions of different file formats.
Currently, the following formats are supported:
There are different options for getting the results of the highlighting, through the use of renderers. The list of renderers is as follows.
The class Text_Highlighter contains all necessary functionality to perform the syntax highlighting except for the actual highlighting rules for the different formats. These rules are defined in subclasses of Text_Highlighter, but one must not directly instantiate these subclasses. Instead the object-oriented factory pattern is used to create a highlighter object depending on the format:
Highlighting a SQL query
<?php
require_once "Text/Highlighter.php";
$hlSQL =& Text_Highlighter::factory("SQL");
echo $hlSQL->highlight("SELECT * FROM some_table WHERE id = 12");
?>
This code generates a highlighted version of the SQL SELECT-query
that is passed to Text_Highlighter::highlight
in HTML. It is possible to customize the output to e.g. instead
generate output suitable for usage on a console. This is
described in the section
Output Customization.
In order to produce syntax highlighting for other formats, one must
replace the argument value SQL
of Text_Highlighter::factory
with one of ABAP
, CPP
, CSS
, DIFF
,
DTD
, HTML
, JAVA
,
JAVASCRIPT
, MYSQL
,
PERL
, PHP
,
PYTHON
, RUBY
, SQL
,
or XML
.
The default behaviour of Text_Highlighter is to generate a syntax highlighted HTML version of the input.
It is possible to instead generate output that is suitable for being displayed on color-capable terminals such as xterm or through less(1) by telling Text_Highlighter to use another renderer:
Using the console renderer
<?php
require_once "Text/Highlighter.php";
require_once "Text/Highlighter/Renderer/Console.php";
$hlSQL =& Text_Highlighter::factory("SQL");
$hlSQL->setRenderer(new Text_Highlighter_Renderer_Console);
echo $hlSQL->highlight("SELECT * FROM some_table WHERE id = 12");
?>
Also it is possible to further customize the output of both the HTML- and the console-renderer by passing an associative array of options to the constructor:
Options for the HTML renderer
<?php
require_once "Text/Highlighter.php";
require_once "Text/Highlighter/Renderer/Html.php";
$renderer = new Text_Highlighter_Renderer_Html(array("numbers" => HL_NUMBERS_LI, "tabsize" => 4));
$hlJava =& Text_Highlighter::factory("JAVA");
$hlJava->setRenderer($renderer);
echo $hlJava->highlight('class JavaProgram {
public static void main(String args[]) {
System.out.println("Hello World!");
}
}');
?>
The above example configures a HTML renderer with two options:
The first one tells it to number the lines in the output
using the <ol />
HTML tag and the
second one instructs the renderer to use a tab width of 4
spaces for indentation.
The following options are applicable:
Name | Description | Available in HTML renderer | Available in console renderer | Hints |
---|---|---|---|---|
numbers |
Line numbering style | yes | yes |
In the console renderer, this option takes just
TRUE or FALSE in order to denote if line numbers
should be displayed or not. The HTML rendered accepts
three different values: HL_NUMBERS_LI
instructs the class to number the lines using the
<ol /> HTML tag, while
HL_NUMBERS_TABLE uses a two-column
table with the line numbers in the first and the source code
in the second column. Setting the value to FALSE turns
line numbering off in the HTML renderer.
|
tabsize |
Tab width | yes | yes | |
colors |
Additional colors | no | yes |
An associate array of additional colors for color-enabled
consoles. The key of each array entry must be the name of
the color and the corresponding value must be the escape
sequence for it. (Example: \033[1;31mRed .)
|
Detects the language of a given piece of text.
The package attempts to detect the language of a sample of text by correlating ranked 3-gram frequencies to a table of 3-gram frequencies of known languages.
It implements a version of a technique originally proposed by Cavnar & Trenkle (1994): "N-Gram-Based Text Categorization".
At first, you might want to get a list of supported languages.
It can be retrieved by calling
getLanguages
on a Text_LanguageDetect object. It returns
an array of strings that represent the languages, e.g.
array('albanian', 'arabic', 'azeri')
.
To actually detect the language of a piece of text, use the
detect
method on the
Text_LanguageDetect object. It takes
the text as first parameter, and an optional
$limit
as second parameter, determining
how many (likely) languages shall be returned at most.
The method returns a sorted array with the languages as key, and
their score as value. If no language is detected, an empty
array is returned.
To get the most likely language only, use
detectSimple
which directly returns the string of the language, or
null
if none was detected.
To detect the language correctly, the length of the input text should be at least some sentences.
Text_LanguageDetect works with language names. It accepts language names for a number of methods, e.g. omitLanguages() and returns language names. By default, a "language name" is a lowercase english name of a language.
Often, applications work with ISO 639-1 or ISO 639-2 language codes - two-letter or three letter codes. Text_LanguageDetect supports them since version 0.3.0, and you may enable them with setNameMode():
<?php
$text = 'Das ist ein kleiner deutscher Text';
require_once 'Text/LanguageDetect.php';
$ld = new Text_LanguageDetect();
//default mode: full language name: "german"
echo $ld->detectSimple($text) . "\n";
//two-letter mode: "de"
$ld->setNameMode(2);
echo $ld->detectSimple($text) . "\n";
//three-letter mode: "deu"
$ld->setNameMode(3);
echo $ld->detectSimple($text) . "\n";
?>
The above example gives the following output:
Output
german de deu
<?php
require_once 'Text/LanguageDetect.php';
$l = new Text_LanguageDetect();
echo "Supported languages:\n";
try {
$langs = $l->getLanguages();
sort($langs);
echo implode(', ', $langs) . "\n\n";
} catch (Text_LanguageDetect_Exception $e) {
die($e->getMessage());
}
$text = <<<EOD
Hallo! Das ist ein Text in deutscher Sprache.
Mal sehen, ob die Klasse erkennt, welche Sprache das hier ist.
EOD;
try {
//return 2-letter language codes only
$l->setNameMode(2);
$result = $l->detect($text, 4);
print_r($result);
} catch (Text_LanguageDetect_Exception $e) {
die($e->getMessage());
}
?>
The above example would give the following output:
Output
Supported languages: albanian, arabic, azeri, bengali, bulgarian, cebuano, croatian, czech, danish, dutch, english, estonian, farsi, finnish, french, german, hausa, hawaiian, hindi, hungarian, icelandic, indonesian, italian, kazakh, kyrgyz, latin, latvian, lithuanian, macedonian, mongolian, nepali, norwegian, pashto, pidgin, polish, portuguese, romanian, russian, serbian, slovak, slovene, somali, spanish, swahili, swedish, tagalog, turkish, ukrainian, urdu, uzbek, vietnamese, welsh Array ( [de] => 0.40703703703704 [nl] => 0.2880658436214 [en] => 0.28333333333333 [da] => 0.23452674897119 )
Creating passwords with PHP.
Generating passwords is a very common task in web applications. This package provides an easy-to-use and intuitive API to generate
For the last point, multiple, simple "obfuscation" algorithms are supported.
Text_Password requires PHP 4.2.0 or better.
The Text_Password package can be installed using the PEAR installer command pear install Text_Password.
Alternative installation methods for situations where one has no access to the pear installer command can be found in the general installation chapter.
Uninstalling the package can be done with pear uninstall Text_Password.
One feature of the package is the ability to create passwords that are pronounceable using the static create() method. createMultiple() can be used to create several passwords at once.
Creating a pronounceable password:
<?php
require_once "Text/Password.php";
echo "Creating pronounceable password of 10 chars....: ";
echo Text_Password::create() . "\n\n";
echo "Creating 3 different pronounceable passwords...: ";
print_r(Text_Password::createMultiple(3));
?>
In addition to pronounceable passwords Text_Password can also handle passwords that are not pronounceable.
Creating a unpronounceable password:
<?php
require_once "Text/Password.php";
echo "\nCreating unpronounceable password of 8 chars with a,b,c as possible chars....:\t";
echo Text_Password::create(8, 'unpronounceable', 'a,b,c') . "\n\n";
echo "\nCreating 4 different unpronounceable passwords...:\n";
print_r(Text_Password::createMultiple(4, 10, 'unpronounceable'));
echo "\nCreating unpronounceable password of 8 chars with numeric chars:\t";
echo Text_Password::create(8, 'unpronounceable', 'numeric') . "\n\n";
echo "\nCreating unpronounceable password of 8 chars with alphanumeric chars:\t";
echo Text_Password::create(8, 'unpronounceable', 'alphanumeric') . "\n\n";
?>
Text_Password provides the ability to create passwords that are based on a given string. In a lot of cases this string is a existing username for a authentication system.
Creating passwords based on a given string:
<?php
require_once "Text/Password.php";
echo "\nCreating password from login 'olivier', type is 'reverse':\t";
echo Text_Password::createFromLogin('olivier', 'reverse') . "\n\n";
echo "\nCreating password from login 'olivier', type is 'rot13':\t";
echo Text_Password::createFromLogin('olivier', 'rot13') . "\n\n";
echo "\nCreating password from login 'olivier', type is 'rotx':\t";
echo Text_Password::createFromLogin('olivier', 'rotx', 13) . "\n\n";
echo "\nCreating password from login 'olivier', type is 'rotx++':\t";
echo Text_Password::createFromLogin('olivier', 'rotx++', 13) . "\n\n";
echo "\nCreating password from login 'olivier', type is 'rotx--':\t";
echo Text_Password::createFromLogin('olivier', 'rotx--', 13) . "\n\n";
echo "\nCreating password from login 'olivier', type is 'xor':\t";
echo Text_Password::createFromLogin('olivier', 'xor', 5) . "\n\n";
echo "\nCreating password from login 'olivier', type is 'ascii_rotx':\t";
echo Text_Password::createFromLogin('olivier', 'ascii_rotx', 5) . "\n\n";
echo "\nCreating password from login 'olivier', type is 'ascii_rotx++':\t";
echo Text_Password::createFromLogin('olivier', 'ascii_rotx++', 5) . "\n\n";
echo "\nCreating password from login 'olivier', type is 'ascii_rotx--':\t";
echo Text_Password::createFromLogin('olivier', 'ascii_rotx--', 5) . "\n\n";
echo "\nCreating password from login 'olivier', type is 'shuffle':\t";
echo Text_Password::createFromLogin('olivier', 'shuffle', 1) . "\n\n";
echo "\nCreating password from an array of login 'olivier', 'martin', 'vanhoucke', 'jansen', type is 'reverse':\n";
$logins = array('olivier', 'martin', 'vanhoucke', 'jansen');
print_r(Text_Password::createMultipleFromLogin($logins, 'reverse'));
?>
Currently the following obfuscation algorithms are supported:
computation of readability scores for text documents.
Text_Statistics allows for computation of readability indexes for text documents.
Text_Statistics calculates some basic readability metrics on a block of text. The number of words, the number of sentences, and the number of total syllables is counted. These statistics can be used to calculate the Flesch score for a sentence, which is a number (usually between 0 and 100) that represents the readability of the text. A basic breakdown of scores is:
90 to 100 |
5th grade |
80 to 90 |
6th grade |
70 to 80 |
7th grade |
60 to 70 |
8th and 9th grade |
50 to 60 |
10th to 12th grade (high school) |
30 to 50 |
college |
0 to 30 |
college graduate |
More info can be read up on WikiPedia article
Example Text_Statistics
<?php
require 'Text/Statistics.php';
$block = new Text_Statistics($sometext);
$block->flesch; // returns flesch score for $sometext
?>
see the unit tests for additional examples.
Text_Word calculates the number of syllables in a word, based off of the number of contiguous vowel groupings in the word and applying matches to detect special cases.
Example numSyllables()
<?php
require_once 'Text/Word.php'
$word = new Text_Word('word');
$word->numSyllables(); // returns 1
?>
Abstracts parsing and rendering rules for Wiki markup in structured plain text
The Text_Wiki package allows you to transform text structured using Wiki rules into any defined target output format, such as XHTML, RTF, LaTeX, and so on.
Utility class for dealing with MIME types.
MIME_Type provides methods to detect the MIME (Multipurpose Internet Mail Extension) type of files, retrieving information about MIME types and parsing them.
The most simple way to detect the MIME type of any file is to use MIME_Type's static autoDetect() method. It will try to determine the file's type and return it as a string. If an error occurs, a PEAR_Error object is returned.
By default, only the plain MIME type will be returned, without any
comments or parameters. If you pass true
as second
parameter to the method, all available MIME parameters will be appended
to the returned type.
Detecting the MIME type of a file
<?php
require_once 'MIME/Type.php';
$filename = '/path/to/some/file.jpg';
echo MIME_Type::autoDetect($filename);
?>
The MIME type of the given file will be echoed.
If you want to check if a certain MIME type matches a wildcard type,
use the static wildcardMatch().
It takes the wildcard as first, and the type to be checked as
second parameter.
It returns true
if the wildcard matches the MIME type,
false
if not.
Matching a wildcard type
<?php
require_once 'MIME/Type.php';
$filename = '/path/to/some/file.jpg';
$type = MIME_Type::autoDetect($filename);
if (MIME_Type::wildcardMatch('image/*', $type)) {
echo 'File ' . $filename . ' is an image.';
} else {
echo 'File is no image.';
}
?>
Using MIME_Type you can get different data about a MIME type. All methods mentioned here can be called statically.
getMedia() returns the main part (media part, portion
before the slash) of a MIME type. It would return image
for image/png
.
getSubType() returns the subtype of the given type.
For image/png
, it returns png
.
isExperimental() checks if a given MIME type is
experimental. It returns true
or
false
(e.g. text/x-vcard
).
isVendor() determines if the given type is a vendor
specific MIME type
(e.g. as in application/vnd.mozilla.xul+xml
).
isWildcard() tells you if the passed type is
a wildcard type (is either */*
or
something/*
).
With MIME_Type, you can edit existing MIME types or create new ones from scratch. Just create a new instance of MIME_Type - with the MIME type string as only parameter, if you have one - or no parameter if you want to begin a new one.
To set or retrieve the media type (part before the slash), use the object's
$media
property. The same applies to the subtype,
the property name is $subType
.
If you are done editing your MIME type, use get() to retrieve the MIME type's string representation.
To retrieve parameters, use the $parameters
property.
It is an array consisting of MIME_Type_Parameter
objects if there are any.
You can remove any parameter by calling removeParameter() and passing the parameter's name as only parameter.
Parameters are added with addParameter().
It takes the $name
, $value
and an optional $comment
as parameters.
Again: If you are done editing the parameters, use get() to retrieve the MIME type's string representation.
There are three static methods that help you working with parameters:
hasParameters() checks if the passed MIME type string has any parameters in it.
getParameters() returns an array of MIME_Type_Parameter objects if there are any parameters in the passed type string.
stripParameters() removes all parameters and comments from the given type string.
VersionControl_SVN is a simple Object-Oriented interface for the svn command-line application that makes up the core of Subversion, a free/open-source version control system.
Subversion can be used to manage trees of source code, text files, image files -- just about any collection of files.
VersionControl_SVN's features include:
Full support of svn
subcommands.
Flexible error reporting provided by PEAR_ErrorStack.
Multi-object factory design.
Fully documented source code
The power of a version control system like Subversion, when accessed through VersionControl_SVN, can be extended far beyond typical "source code" repositories.
For example, what content management system (CMS) couldn't benefit from version control functionality? For many non-programmers, version control is a confusing subject to get a firm grasp on. With VersionControl_SVN, developers are now able to customize the interface to Subversion with the ease-of-use goals of their particular audience in mind. VersionControl_SVN lets you leverage the strengths of version control without burdening end-users with the learning curve of change control fundamentals.
So you've got Subversion repository set up somewhere, and you want to take a look at what's inside with a PHP script. With the VersionControl_SVN::VersionControl_SVN_List() command, you're just a few steps away.
Reading the content of a Subversion repository
<?php
require_once 'VersionControl/SVN.php';
// Setup error handling -- always a good idea!
$svnstack = &PEAR_ErrorStack::singleton('VersionControl_SVN');
// Set up runtime options.
$options = array('fetchmode' => VERSIONCONTROL_SVN_FETCHMODE_ARRAY);
// Request list class from factory
$svn = VersionControl_SVN::factory('list', $options);
// Define any switches and aguments we may need
$switches = array('username' => 'user', 'password' => 'pass');
$args = array('svn://svn.example.com/repos/TestProject');
// Run command
if ($output = $svn->run($args, $switches)) {
print_r($output);
} else {
if (count($errs = $svnstack->getErrors())) {
foreach ($errs as $err) {
echo '<br />'.$err['message']."<br />\n";
echo "Command used: " . $err['params']['cmd'];
}
}
}
?>
If your example repository above happened to have the VersionControl_SVN source in it, your output would be something like this:
<?php
Array
(
[0] => Array
(
[name] => docs
[type] => D
)
[1] => Array
(
[name] => package.xml
[type] => F
)
[2] => Array
(
[name] => SVN.php
[type] => F
)
[3] => Array
(
[name] => SVN
[type] => D
)
[4] => Array
(
[name] => tests
[type] => D
)
)
?>
Note that in the above output, directories are flagged as type
D
, and files are flagged as type
F
.
For additional information in the output, try setting
verbose
to TRUE in your$options
array.
Have a script that needs to utilize several VersionControl_SVN
subclasses? At the expense of a little overhead, you can be sure
your $svn
objects are fully-loaded by using the
VersionControl_SVN::factory() command keyword
__ALL__
.
For example, in a basic script to get the list of current files in a repository, you just need the VersionControl_SVN::VersionControl_SVN_List() subclass.
Getting the list of current files in a repository
<?php
require_once 'VersionControl/SVN.php';
// Setup error handling -- always a good idea!
$svnstack = &PEAR_ErrorStack::singleton('VersionControl_SVN');
// Set up runtime options.
$options = array('fetchmode' => VERSIONCONTROL_SVN_FETCHMODE_ARRAY);
// Request list class from factory
$svn = VersionControl_SVN::factory('list', $options);
// Define any switches and aguments we may need
$switches = array('username' => 'user', 'password' => 'pass');
$args = array('svn://svn.example.com/repos/TestProject');
// Run command
if ($output = $svn->run($args, $switches)) {
print_r($output);
} else {
if (count($errs = $svnstack->getErrors())) {
foreach ($errs as $err) {
echo '<br />'.$err['message']."<br />\n";
echo "Command used: " . $err['params']['cmd'];
}
}
}
?>
However, if you need to get a recursive list of files in a repository, look up the recent log activity for those files, and view the annotated source for those files, you've got two options.
Recursively getting the list of current files in a repository
<?php
require_once 'VersionControl/SVN.php';
// Setup error handling -- always a good idea!
$svnstack = &PEAR_ErrorStack::singleton('VersionControl_SVN');
// Set up runtime options.
$options = array('fetchmode' => VERSIONCONTROL_SVN_FETCHMODE_RAW);
// METHOD ONE: Lowest Overhead
// Create svn object with subcommands we need listed out individually
$svn = VersionControl_SVN::factory(array('list', 'log', 'blame'), $options);
// Define any switches and aguments we may need
$switches = array('username' => 'user', 'password' => 'pass');
$args = array('svn://svn.example.com/repos/TestProject');
print_r($svn->list->run($args, $switches));
// Pick files out of the above output, and see who did what
$args = array('svn://svn.example.com/repos/TestProject/trunk/index.php');
echo "<pre>" . $svn->blame->run($args) . "</pre>";
// METHOD TWO: Put all available commands at your disposal
// Load up all subcommands - higher overhead, but convenient for certain occasions
$svn = VersionControl_SVN::factory('__ALL__', $options);
// Now you may run whatever you need to ...
$svn->cat->run($args, $switches);
$svn->info->run($args, $switches);
// ... and so on.
?>
If you are interested in learning more about Subversion, see the following:
Version Control with Subversion - The primary reference manual for all things related to Subversion, from general use to repository administration.
Subversion Website - The official Subversion website offers a FAQ, mailing list, and of course, the Subversion source code. Also included are links to GUI Subversion applications.
VersionControl_Git is a library that provides OO interface to handle Git repository.
You can use Git command via the wrapper class. Some features are provided by high-featured interface.
The VersionControl_Git class represents your Git repository.
You should specified a path to repository to VersionControl_Git.
<?php
require_once 'VersionControl/Git.php';
// Specify a directory
$git = new VersionControl_Git('/path/to/repository');
Do you want to create new repository? It is easy.
First, create an instance of VersionControl_Git and pass a directory what you want to create new Git repository.
Next, call the VersionControl_Git::initRepository() method.
<?php
require_once 'VersionControl/Git.php';
// Specify a directory
$git = new VersionControl_Git('/path/to/repository');
// create new repository
$git->initRepository();
// If you want to create bare repository, pass true as the first argument
$git->initRepository(true);
Do you want to create clone repository? In this case, you can use the VersionControl_Git::createClone()
<?php
require_once 'VersionControl/Git.php';
// Specify a directory
$git = new VersionControl_Git('/path/to/repository');
// create new repository
$git->createClone('http://example.com/repository.git');
// If you want to create bare repository, pass true as the second argument
$git->createClone('http://example.com/repository.git', true);
Now, you've readied to use full futures of VersionControl_Git.
The commit object in Git is provided as VersionControl_Git_Object_Commit.
There are some ways to get a list of VersionControl_Git_Object_Commit objects.
You can get commits by calling VersionControl_Git::getCommits().
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
var_dump($git->getCommits());
/*
results:
array(100) {
[0]=>
object(VersionControl_Git_Object_Commit)#3 (9) {
:
}
[1]=>
object(VersionControl_Git_Object_Commit)#6 (9) {
:
}
:
*/
Calling without arguments, VersionControl_Git::getCommits() returns a list of a hundred VersionControl_Git_Object_Commit instances from master branch. You can specify branch name, commit object name, tag name, tree object name and an instance of VersionControl_Git_Object.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
// from master branch
$git->getCommits();
// specify stable branch
$git->getCommits('stable');
// specify object name
$git->getCommits('6c8284e4902c3adaf356adeed40d8bda715b73a0');
// specify tag name
$git->getCommits('v1.0');
// specify an instance of VersionControl_Git_Object
$commits = $git->getCommits();
$git->getCommits($commits[0]);
You can specify the maximum number of commits by the second argument.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
// get 1000 commits from master branch
$git->getCommits('master', 1000);
You can specify the starting point of fetching by the third argument.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
// get 100 commits from master branch (starting at 100 th commit)
$git->getCommits('master', 100, 100);
The VersionControl_Git_Util_RevListFetcher is utility class for wrapping "git-rev-list" command. So you can use any feature of "git-rev-list" by this. VersionControl_Git::getCommits() provides by using VersionControl_Git_Util_RevListFetcher.
You can use VersionControl_Git_Util_RevListFetcher to specify your VersionControl_Git instance to the constructor
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
$fetcher = new VersionControl_Git_Util_RevListFetcher($git);
The VersionControl_Git provides the getRevListFetcher() convenience method. Use this normally.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
$fetcher = $git->getRevListFetcher();
After specifying target (branch name, commit name, tree name, etc) and options, you can get an array of VersionControl_Git_Object_Commit by calling the fetch() method.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
$result = $git->getRevListFetcher()
->target('master')
->setOption('max-count', 10)
->setOption('grep', 'initial')
->setOption('date', '3 hours ago')
->fetch();
An instance of the VersionControl_Git_Object_Commit provides information about a commit.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
$commits = $git->getCommits();
var_dump($commits[0]->getAuthor());
/*
string(35) "Kousuke Ebihara <kousuke@co3k.org>"
*/
var_dump($commits[0]->getCreatedAt());
/*
object(DateTime)#5 (3) {
}
*/
var_dump($commits[0]->getCommitter());
/*
string(35) "Kousuke Ebihara <kousuke@co3k.org>"
*/
var_dump($commits[0]->getCommittedAt());
/*
object(DateTime)#6 (3) {
}
*/
var_dump($commits[0]->getMessage());
/*
string(62) "changed url to JSON version of dashboard contents (fixes #509)"
*/
var_dump($commits[0]->getParents());
/*
array(1) {
[0]=>
object(VersionControl_Git_Object_Commit)#303 (9) {
}
}
*/
var_dump($commits[0]->getTree());
/*
string(40) "ba2dd3d861f68c6ea31c4cd4444a5d86869a3401"
*/
An instance of the VersionControl_Git_Object_Tree represents tree object in Git. It has pointers to contents of the direcotry.
An instance of VersionControl_Git_Object_Tree can get via VersionControl_Git::getTree().
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
$commits = $git->getCommits();
$tree = $git->getTree($commits[0]->getTree());
The instance doesn't have any contents at the first. If you want to get contents, you must call "fetch()" method.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
$commits = $git->getCommits();
$tree = $git->getTree($commits[0]->getTree());
$tree->fetch();
The VersionControl_Git_Object_Commit implements the SeekableIterator interface. You can iterate and seek contents of tree.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/path/to/repository');
$commits = $git->getCommits();
$tree = $git->getTree($commits[0]->getTree());
$tree->fetch();
foreach ($tree as $content) {
var_dump($content);
}
/*
results:
object(VersionControl_Git_Object_Blob)#304 (3) {
}
object(VersionControl_Git_Object_Blob)#305 (3) {
}
object(VersionControl_Git_Object_Blob)#306 (3) {
}
object(VersionControl_Git_Object_Blob)#307 (3) {
}
object(VersionControl_Git_Object_Tree)#308 (4) {
}
object(VersionControl_Git_Object_Tree)#309 (4) {
}
object(VersionControl_Git_Object_Tree)#311 (4) {
}
object(VersionControl_Git_Object_Tree)#312 (4) {
}
object(VersionControl_Git_Object_Tree)#313 (4) {
}
object(VersionControl_Git_Object_Tree)#314 (4) {
}
object(VersionControl_Git_Object_Tree)#315 (4) {
}
object(VersionControl_Git_Object_Tree)#316 (4) {
}
object(VersionControl_Git_Object_Blob)#317 (3) {
}
object(VersionControl_Git_Object_Tree)#318 (4) {
}
*/
An instance of the VersionControl_Git_Object_Blob represents tree object in Git. It has contents of the file.
It can get via VersionControl_Git_Object_Tree
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/home/co3k/sf/op3-ebihara');
$commits = $git->getCommits();
$tree = $git->getTree($commits[0]->getTree());
$tree->fetch();
$blob = $tree->current();
var_dump($blob);
/*
results:
object(VersionControl_Git_Object_Blob)#304 (3) {
}
*/
The instance doesn't have any contents at the first. If you want to get contents, call the getContents() after calling the fetch() method.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/home/co3k/sf/op3-ebihara');
$commits = $git->getCommits();
$tree = $git->getTree($commits[0]->getTree());
$tree->fetch();
$blob = $tree->current()->fetch();
var_dump($blob->getContent());
The VersionControl_Git prepares OO interface of some Git feature, but it is not completely.
If you want to use full feature of Git by using VersionControl_Git, you should run Git command via VersionControl_Git_Util_Command.
To tell the truth, most of the feature of VersionControl_Git uses VersionControl_Git_Util_Command.
Now, I explain you how to use VersionControl_Git_Util_Command.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/home/co3k/sf/op3-ebihara');
$command = $git->getCommand('show');
You can get an instance of VersionControl_Git_Util_Command by calling VersionControl_Git::getCommand() with sub-command name.
If you want to use options, add calling the setOption() method or the setOptions() method.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/home/co3k/sf/op3-ebihara');
$command = $git->getCommand('show')
->setOptions(array(
'pretty' => 'raw',
))
->setOptions('pretty', 'raw');
The boolean option value is special. "true" is specified as option value, it doesn't have value. "false" is specified as option value, the option will not be used.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/home/co3k/sf/op3-ebihara');
$command = $git->getCommand('show')
->setOptions('oneline', true);
If you want to use arguments, add calling the addArgument() method or the setArguemnts() method.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/home/co3k/sf/op3-ebihara');
$command = $git->getCommand('show')
->setArguments(array('master', 'branch1'))
->addArgument('branch2');
Ready to execute the command? Now you can call "execute()" method and get result.
<?php
require_once 'VersionControl/Git.php';
$git = new VersionControl_Git('/home/co3k/sf/op3-ebihara');
$result = $git->getCommand('show')
->setOption('oneline', true)
->addArgument('master')
->execute();
var_dump($result);
/*
result:
string(829) "9a259a5 changed url to JSON version of dashboard contents (fixes #509)
diff --git a/apps/pc_backend/modules/default/templates/topSuccess.php b/apps/pc_backend/modules/default/templates/topSuccess.php
index 3c7764e..cbfcf26 100644
:
*/
Validate offers a set of packages to validate common or specific data. The main package validates the common data. Specific data are localised or thematic.
Localized validation class
Argentina: Validate_AR
Austria: Validate_AT
Australia : Validate_AU
Belgium: Validate_BE
Brasil: Validate_PTBR
Canada: Validate_CA
Denmark: Validate_DK
Finland: Validate_FI
France: Validate_FR
Germany: Validate_DE
Iceland: Validate_IS
Ireland: Validate_IE
Netherlands: Validate_NL
New Zealand: Validate_NZ
Poland: Validate_PL
Republic of India: Validate_IN
South Africa: Validate_ZA
Spain: Validate_ES
Switzerland: Validate_CH
United Kingdom: Validate_UK
United States: Validate_US
Thematic validation class
Financial: Validate_Finance
Credit Cards: Validate_Finance_CreditCard
International Standard Product Numbers : Validate_ISPN
The main package provides methods to validate various data. It includes :
numbers (min/max, decimal or not)
email (syntax, domain check)
string (predifined type alpha upper and/or lowercase, numeric,...)
date (date, options) - Options can be format (Format of the date (%d-%m-%Y) or rfc822_compliant), min (The date has to be greater than this array ($day, $month, $year) or PEAR::Date object), max (The date hast to be smaller than this array ($day, $month, $year) or PEAR::Date object)
uri (RFC2396)
possibility valid multiple data with a single method call (::multiple)
See also :
With this package, one can easily validate various data. It includes numbers, email, string, date, URI and posibility valid multiple data with a single method call.
Some of above mentioned features have corresponding RFCs, in case, Validate conforms to those RFCs. For example email validation mostly covers RFC 822 or URI conforms to RFC 2396.
Validating an email address
<?php
require_once 'Validate.php';
if (!Validate::email('johndoe@example.net')) {
echo 'Invalid Email address';
} else {
echo 'Its Valid!';
}
?>
Validating dates
In this example, date would first checked against desired format, and then get checked to see if it's not before "15 February 1986" or after "23 August 2008".
<?php
require_once 'Validate.php';
var_dump(
Validate::date(
'121202',
array(
'format' => '%d%m%y',
'min' => array('15', '02', '1986'),
'max' => array('23', '08', '2008')
)
)
);
?>
This method validate email addresses against both the general format and the one described in RFC 822.
This method takes two arguments:
Various option are:
check_domain
(boolean) - Check or not if the domain exists.
use_rfc822
(boolean) - Apply the full RFC822 grammar.
fullTLDValidation
(boolean) - All top-level domains.
VALIDATE_GTLD_EMAILS
(boolean) - Only generic top-level domains.
VALIDATE_CCTLD_EMAILS
(boolean) - Only country code top-level domains.
VALIDATE_ITLD_EMAILS
(boolean) - Only international top-level domains.
Email Validation
The following example assumes that one wants to use RFC822 strict mode to validate email addresses.
<?php
require_once 'Validate.php';
$email = '"Doe, John" <johndoe@example.net>';
if (Validate::email($email, array('use_rfc822' => true))) {
echo 'Valid!';
} else {
echo $email . ' failed.';
}
?>
And the following one assumes that one wants to check if the domain exists.
<?php
require_once 'Validate.php';
$email = 'info@example.com';
if (Validate::email($email, array('check_domain' => 'true'))) {
echo $email . ' is valid and domain exists';
}
?>
This method validates numbers. Decimal or not, max and min.
This method takes two arguments:
Various option are:
decimal
(mixed) - Decimal chars or false when decimal
not allowed. For example ",." to allow both "." and ",".
dec_prec
(int) - Number of allowed decimals.
min
(float) - Minimum value.
max
(float) - Maximum value.
Number Validation
The following example assumes that one wants to validate a number when decimals are allowed and decimal character is ".", and number of allowed decimals is 4.
<?php
require_once 'Validate.php';
if (Validate::number(8.0004, array('decimal' => '.', 'dec_prec' => 4))) {
echo 'Valid number';
}
?>
And the following one assumes that one wants to validate a decimal number with decimal character "," or "." while it's less than "-7" and greater than "-9".
<?php
require_once 'Validate.php';
$number = '-8,1';
if (Validate::number($number,
array('decimal' => '.,', 'min' => -9, 'max' => -7 ))) {
echo 'Valid';
} else {
echo 'Invalid';
}
?>
This method validates string using the given format.
This method takes two arguments:
Various option are:
format
(mixed) - Format of the string
VALIDATE_NUM
- Number (0-9).
VALIDATE_SPACE
- Space (\s).
VALIDATE_ALPHA_LOWER
- Lower case alphabets (a-z).
VALIDATE_ALPHA_UPPER
- Upper case alphabets (A-Z).
VALIDATE_ALPHA
- Alphabets (a-Z).
VALIDATE_EALPHA_LOWER
- Lower case letters with an
accent (French, ...), umlauts (German), special
characters (e.g. Skandinavian) and
VALIDATE_ALPHA_LOWER
.
VALIDATE_EALPHA_UPPER
- Upper case letters with an
accent (French, ...), umlauts (German), special
characters (e.g. Skandinavian) and
VALIDATE_ALPHA_UPPER
.
VALIDATE_EALPHA
- Letters with an
accent (French, ...), umlauts (German), special
characters (e.g. Skandinavian) and
VALIDATE_ALPHA
.
VALIDATE_PUNCTUATION
- Punctuation .,;:&"?!', "(" and ")".
VALIDATE_NAME
- VALIDATE_EALPHA
,
VALIDATE_SPACE
, "'" and "-".
VALIDATE_STREET
- VALIDATE_NUM
and
VALIDATE_NAME
, "\./", "º" and "ª".
min_length
(int) - Minimum length.
max_length
(int) - Maximum length.
String Validation
The following example assumes that one wants to validate a string when only uppercase alphabets, numbers and space are allowed
<?php
require_once 'Validate.php';
if (Validate::string("1984 GEORGE ORWELL", array(
'format' => VALIDATE_NUM . VALIDATE_SPACE . VALIDATE_ALPHA_UPPER))) {
echo 'Valid!';
} else {
echo 'Invalid!';
}
?>
Country |
Package |
PostCode |
SIN |
Car registration |
SSN |
Phone |
Region |
bankCode |
Others |
---|---|---|---|---|---|---|---|---|---|
Argentina |
AR |
Yes |
Yes |
||||||
Austria |
AT |
Yes |
Yes |
||||||
Australia |
AU |
Yes |
Yes |
|
|||||
Belgium |
BE |
Yes |
Yes |
Yes |
Yes |
|
|||
Brasil |
PTBR |
Yes |
Yes |
Yes |
Yes |
Yes |
|
||
Canada |
CA |
Yes |
Yes |
n |
y |
y |
n |
||
Denmark |
DK |
Yes |
Yes |
Yes |
Yes |
||||
France |
FR |
Yes |
Yes |
Departements |
RIB |
|
|||
Germany |
DE |
Yes |
Yes |
||||||
Iceland |
IS |
Yes |
Yes |
Yes |
address |
||||
Mexico |
esMX |
Yes |
Yes (DNI) |
Yes |
Yes |
||||
Netherlands |
NL |
Yes |
Yes |
Yes |
Account Number |
||||
New Zealand |
NZ |
Yes |
Yes |
Region number |
Account Number |
IRD Number |
|||
Poland |
PL |
Account number |
|
||||||
Republic of India |
IN |
Yes |
Yes |
Yes |
State and union territory |
Pan and TAN |
|||
South Africa |
ZA |
Yes |
Yes |
Yes |
|||||
Spain |
ES |
Yes (DNI) |
|||||||
Switzerland |
CH |
Yes |
Yes |
|
|||||
United Kingdom |
UK |
Yes |
Yes |
Yes |
Yes |
Account Number |
|
||
United States |
US |
Yes |
Yes |
Yes |
Yes |
This package is in alpha state
Package contains locale validation for Argentina such as:
Postal code CPA (Código Postal Argentino)
Regions - provinces of Argentina
See also :
This package is in alpha state
Package contains locale validation for Austria such as:
SSN
Postal Code
See also :
This package is in alpha state
Package contains locale validation for Australia such as:
Tax File Number
Postal Code
Phone Number
Australian Business Number
Australian Company Number
Regions (States)
See also :
The package offers some methods to validate specific data for Australia
Every module of Validate follows the same philosophy. Propose some validation method which returns a boolean result. Some methods have some optional parameters to set stronger checks.
<?php
// Include the package
require_once 'Validate/AU.php';
?>
Most Australian's will have a TFN (Tax File Number) however not all, it is the closet equivalent we have to a Social Security Number. Note that this validation routine can be accessed through both Validate_AU::tfn() and Valdiate::ssn() methods.
<?php
// Include the package
require_once('Validate/AU.php');
$badTFN = '23 456 782';
$result = Validate_AU::tfn($badTFN);
echo 'Test ' . $badTFN .' : <br />';
var_export($result);
echo '<br /><br />';
$goodTFN = '123 456 782';
$result = Validate_AU::tfn($goodTFN);
echo 'Test ' . $goodNationalId .' : <br />';
var_export($result);
?>
Output :
Test 23 456 782 :
false
Test 123 456 782 :
true
Australian post code are 4 digit formed.
First parameter is the post code to validate.
An optional parameter for activate strong checks using a list of postcodes.
<?php
// Include the package
require_once('Validate/AU.php');
$badPostCode = 'ABCD';
$result = Validate_AU::postalCode($badPostCode);
echo 'Test ' . $badPostCode .' : <br />';
var_export($result);
echo '<br /><br />';
$goodPostCode = '3000';
$result = Validate_AU::postalCode($goodPostCode);
echo 'Test ' . $goodPostCode .' : <br />';
var_export($result);
?>
Output :
Test ABCD :
false
Test 3000 :
true
1234 appears to be a valid 4 digit postcode, however it does not appear in the official list.
<?php
// Include the package
require_once('Validate/AU.php');
$badPostCode = '1234';
$goodPostCode = '7930';
$result = Validate_AU::postalCode($badPostCode);
echo 'Test ' . $badPostCode .' : <br />';
var_export($result);
$result = Validate_AU::postalCode($badPostCode, false);
echo '<br /><br />Test ' . $badPostCode .' : <br />';
var_export($result);
$result = Validate_AU::postalCode($badPostCode, true);
echo '<br /><br />Test ' . $badPostCode .' : <br />';
var_export($result);
$result = Validate_AU::postalCode($goodPostCode, true);
echo '<br /><br />Test ' . $goodPostCode .' : <br />';
var_export($result);
?>
Output :
Test 1234 :
true
Test 1234 :
true
Test 1234 :
false
Test 7930 :
true
Validate an Australian Business Number.
<?php
// Include the package
require_once('Validate/AU.php');
$badABN = '00 043 145 470';
$result = Validate_AU::abn($badABN);
echo 'Test ' . $badRegion .' : <br />';
var_export($result);
echo '<br /><br />';
$goodABN = '28 043 145 470';
$result = Validate_AU::abn($goodABN);
echo 'Test ' . $goodRegion .' : <br />';
var_export($result);
?>
Output :
Test 00 043 145 470 :
false
Test 28 043 145 470 :
true
Validates a 2/3 region (state) code.
<?php
// Include the package
require_once('Validate/AU.php');
$badRegion = 'asdf';
$result = Validate_AU::region($badVAT);
echo 'Test ' . $badRegion .' : <br />';
var_export($result);
echo '<br /><br />';
$goodRegion = 'VIC';
$result = Validate_AU::region($goodRegion);
echo 'Test ' . $goodRegion .' : <br />';
var_export($result);
?>
Output :
Test asdf :
false
Test VIC :
true
Validate an Australian phone number passed as first param. Second parameter can be used to specify flags that signify the type of number to be validated.
Flags can be any combination of the bitwise constants VALIDATE_AU_PHONENUMBER_* as follows:
Flag | Description |
---|---|
"VALIDATE_AU_PHONENUMBER_STRICT" | If supplied then no spaces, parenthesis or dashes (-) will be removed. |
"VALIDATE_AU_PHONENUMBER_NATIONAL" | If supplied, then valid national numbers, both landline and mobile will pass validation. |
"VALIDATE_AU_PHONENUMBER_INDIAL" | If supplied, then valid indial (13/1300/1800/1900) numbers will pass validation. |
"VALIDATE_AU_PHONENUMBER_INTERNATIONAL" | If supplied, then valid international syntax will pass validation. EG. +61.3 9999 9999 |
<?php
// Include the package
require_once('Validate/AU.php');
$nationalPhone = '03 9999 9999';
$nationalStrictPhone = '0399999999';
$indialPhone = '1300 131 121';
$internationalSyntax = '+61.3 8779 7212';
echo 'Test ' . $goodPhone .' : <br />';
$result = Validate_AU::phoneNumber($nationalPhone); // the flag VALIDATE_AU_PHONENUMBER_NATIONAL is default
var_export($result) . '-';
$result = Validate_AU::phoneNumber($nationalPhone, VALIDATE_AU_PHONENUMBER_NATIONAL | VALIDATE_AU_PHONENUMBER_STRICT);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($nationalPhone, VALIDATE_AU_PHONENUMBER_INDIAL);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($nationalPhone, VALIDATE_AU_PHONENUMBER_INTERNATIONAL);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($nationalPhone, VALIDATE_AU_PHONENUMBER_NATIONAL | VALIDATE_AU_PHONENUMBER_INDIAL | VALIDATE_AU_PHONENUMBER_INTERNATIONAL);
var_export($result);
echo '<br /><br />';
echo 'Test ' . $nationalStrictPhone .' : <br />';
$result = Validate_AU::phoneNumber($nationalStrictPhone);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($nationalStrictPhone, VALIDATE_AU_PHONENUMBER_NATIONAL | VALIDATE_AU_PHONENUMBER_STRICT);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($nationalStrictPhone, VALIDATE_AU_PHONENUMBER_INDIAL);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($nationalStrictPhone, VALIDATE_AU_PHONENUMBER_INTERNATIONAL);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($nationalStrictPhone, VALIDATE_AU_PHONENUMBER_NATIONAL | VALIDATE_AU_PHONENUMBER_INDIAL | VALIDATE_AU_PHONENUMBER_INTERNATIONAL);
var_export($result) . '-';
echo '<br /><br />';
echo 'Test ' . $indialPhone .' : <br />';
$result = Validate_AU::phoneNumber($indialPhone);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($indialPhone, VALIDATE_AU_PHONENUMBER_INDIAL | VALIDATE_AU_PHONENUMBER_STRICT);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($indialPhone, VALIDATE_AU_PHONENUMBER_INDIAL);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($indialPhone, VALIDATE_AU_PHONENUMBER_INTERNATIONAL);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($indialPhone, VALIDATE_AU_PHONENUMBER_NATIONAL | VALIDATE_AU_PHONENUMBER_INDIAL | VALIDATE_AU_PHONENUMBER_INTERNATIONAL);
var_export($result) . '-';
echo '<br /><br />';
echo 'Test ' . $internationalSyntax .' : <br />';
$result = Validate_AU::phoneNumber($internationalSyntax);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($internationalSyntax, VALIDATE_AU_PHONENUMBER_INTERNATIONAL | VALIDATE_AU_PHONENUMBER_STRICT);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($internationalSyntax, VALIDATE_AU_PHONENUMBER_INDIAL);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($internationalSyntax, VALIDATE_AU_PHONENUMBER_INTERNATIONAL);
var_export($result) . '-';
$result = Validate_AU::phoneNumber($internationalSyntax, VALIDATE_AU_PHONENUMBER_NATIONAL | VALIDATE_AU_PHONENUMBER_INDIAL | VALIDATE_AU_PHONENUMBER_INTERNATIONAL);
var_export($result) . '-';
?>
Output :
Test 03 9999 9999 :
true - false - false - false - true
Test 0399999999 :
true - true - false - false - true
Test 1300 131 121 :
false - false - true - false - true
Test +61.3 8779 7212 :
false - false - true - true - true
Name | Value | Line Number |
---|---|---|
VALIDATE_AU_PHONENUMBER_INDIAL | 4 | 37 |
VALIDATE_AU_PHONENUMBER_INTERNATIONAL | 8 | 38 |
VALIDATE_AU_PHONENUMBER_NATIONAL | 2 | 36 |
VALIDATE_AU_PHONENUMBER_STRICT | 1 | 35 |
bool Validate_AU::abn (
string $abn
)
Validates an ABN using a modulus calculation
$abn
ABN to validate
returns Returns true on success, otherwise false
throws no exceptions thrown
author Byron Adams <byron.adams54@gmail.com>
This function can not be called statically.
bool Validate_AU::acn (
string $acn
)
The ACN is a nine digit number with the last digit being a check digit calculated using a modified modulus 10 calculation.
$acn
ACN number to validate
returns Returns true on success, false otherwise
throws no exceptions thrown
author Byron Adams <byron.adams54@gmail.com>
author Daniel O'Connor <daniel.oconnor@gmail.com>
see http://www.asic.gov.au/asic/asic_infoco.nsf/byheadline/Australian+Company+Number+(ACN)+Check+Digit
This function can not be called statically.
bool Validate_AU::phoneNumber (
string $number
, int $flags = VALIDATE_AU_PHONENUMBER_NATIONAL
)
Note that this function supports the following notations:
Landline: 03 9999 9999
Mobile: 0400 000 000 (as above, but usually notated differently)
Indial: 131 812 / 1300 000 000 / 1800 000 000 / 1900 000 000
International: +61.3 9999 9999
For International numbers, only +61 will be valid, as this is Australia's dial code, and the format MUST be +61.3, where 3 represents the state dial code, in this case, Victoria.
Note: If the VALIDATE_AU_PHONENUMBER_STRICT flag is not supplied, then all spaces, dashes and parenthesis are removed before validation. You will have to strip these yourself if your data storage does not allow these characters.
$number
The telephone number
$flags
Can be a combination of the following flags:
VALIDATE_AU_PHONENUMBER_STRICT: if supplied then no spaces, parenthesis or dashes (-) will be removed.
VALIDATE_AU_PHONENUMBER_NATIONAL: when supplied valid national numbers (eg. 03 9999 9999) will return TRUE.
VALIDATE_AU_PHONENUMBER_INDIAL: when supplied valid indial numbers (eg. 13/1300/1800/1900) will return TRUE.
VALIDATE_AU_PHONENUMBER_INTERNATIONAL: when supplied valid international notation of Australian numbers (eg. +61.3 9999 9999) will return TRUE.
todo Check that $flags contains a valid flag.
throws no exceptions thrown
This function can not be called statically.
bool Validate_AU::postalCode (
string $postcode
, bool $strong
= false
)
This package is not documented yet.
$postcode
postcode to validate
$strong
strong checks against a list of postcodes
returns true if postcode is ok, false otherwise
throws no exceptions thrown
This function can not be called statically.
bool Validate_AU::region (
string $region
)
This package is not documented yet.
$region
region code to validate
returns returns true on success, false otherwise
throws no exceptions thrown
author Byron Adams <byron.adams54@gmail.com>
This function can not be called statically.
bool Validate_AU::ssn (
string $ssn
)
Australia does not have a social security number system, the closest equivalent is a Tax File Number
$ssn
ssn number to validate
returns Returns true on success, false otherwise
throws no exceptions thrown
This function can not be called statically.
bool Validate_AU::tfn (
string $tfn
)
Australia does not have a social security number system, the closest equivalent is a Tax File Number.
$tfn
Tax File Number
returns Returns true on success, false otherwise
throws no exceptions thrown
author Byron Adams <byron.adams54@gmail.com>
This function can not be called statically.
This package is in beta state
This package offers methods to validate specific values from Belgium
See also :
The package offers some methods to validate specific data for Belgium
Every module of Validate follows the same philosophy. Propose some validation method retuning boolean result. Some methods have some optional parameters to set a stronger check.
<?php
// Include the package
require_once 'Validate/BE.php';
?>
The belgian nationalId on the identity card of all belgian.
A check digit is the last one, computed the standard _get_control_number function.
<?php
// Include the package
require_once('Validate/BE.php');
$badNationalId = '730111-361-99';
$result = Validate_BE::nationalId($badNationalId);
echo 'Test ' . $badNationalId .' : <br />';
var_export($result);
echo '<br /><br />';
$goodNationalId = '730111 361 73';
$result = Validate_BE::nationalId($goodNationalId);
echo 'Test ' . $goodNationalId .' : <br />';
var_export($result);
?>
Output :
Test 730111-361-99 :
false
Test 730111 361 73 :
true
The belgian social security number is on the SIS card of all belgian.
A check digit is the last one, computed the standard _get_control_number function.
<?php
// Include the package
require_once('Validate/BE.php');
$badSsn = '72011136173';
$result = Validate_BE::ssn($badSsn);
echo 'Test ' . $badSsn .' : <br />';
var_export($result);
echo '<br /><br />';
$goodSsn = '73011136173';
$result = Validate_BE::ssn($goodSsn);
echo 'Test ' . $goodSsn .' : <br />';
var_export($result);
?>
Output :
Test 72011136173 :
false
Test 73011136173 :
true
Belgian post code are 4 digit formed.
First parameter is the post code to validate.
An optional parameter for activate strong checks using a list of postcodes.
<?php
// Include the package
require_once('Validate/BE.php');
$badPostCode = 'ABCD';
$result = Validate_BE::postalCode($badPostCode);
echo 'Test ' . $badPostCode .' : <br />';
var_export($result);
echo '<br /><br />';
$goodPostCode = '7930';
$result = Validate_BE::postalCode($goodPostCode);
echo 'Test ' . $goodPostCode .' : <br />';
var_export($result);
?>
Output :
Test ABCD :
false
Test 7930 :
true
1234 like a good post code, but don't exit in the official list.
<?php
// Include the package
require_once('Validate/BE.php');
$badPostCode = '1234';
$goodPostCode = '7930';
$result = Validate_BE::postalCode($badPostCode);
echo 'Test ' . $badPostCode .' : <br />';
var_export($result);
$result = Validate_BE::postalCode($badPostCode,false);
echo '<br /><br />Test ' . $badPostCode .' : <br />';
var_export($result);
$result = Validate_BE::postalCode($badPostCode,true);
echo '<br /><br />Test ' . $badPostCode .' : <br />';
var_export($result);
$result = Validate_BE::postalCode($goodPostCode);
echo '<br /><br />Test ' . $goodPostCode .' : <br />';
var_export($result);
?>
Output :
Test 1234 :
true
Test 1234 :
true
Test 1234 :
false
Test 7930 :
true
Belgian bankcodes consist of
<?php
// Include the package
require_once('Validate/BE.php');
$badBankCode = '310164533227';
$result = Validate_BE::bankCode($badBankCode);
echo 'Test ' . $badBankCode .' : <br />';
var_export($result);
echo '<br /><br />';
$goodBankCode = '310164533207';
$result = Validate_BE::bankCode($goodBankCode);
echo 'Test ' . $goodBankCode .' : <br />';
var_export($result);
?>
Output :
Test 310164533227 :
false
Test 310164533207 :
true
Belgian transfert (virement) can be done with a structured message 12 figures
<?php
// Include the package
require_once('Validate/BE.php');
$badBankTransferMessage = '053/3140/16211';
$result = Validate_BE::bankTransferMessage($badBankTransferMessage);
echo 'Test ' . $badBankTransferMessage .' : <br />';
var_export($result);
echo '<br /><br />';
$goodBankTransferMessage = '054/3140/16211';
$result = Validate_BE::bankTransferMessage($goodBankTransferMessage);
echo 'Test ' . $goodBankTransferMessage .' : <br />';
var_export($result);
?>
Output :
Test 053/3140/16211 :
false
Test 054/3140/16211 :
true
Belgian VAT consist of 3-figure number.
Actually no doc was found about a checksum.
<?php
// Include the package
require_once('Validate/BE.php');
$badVAT = '102.239.951';
$result = Validate_BE::vat($badVAT);
echo 'Test ' . $badVAT .' : <br />';
var_export($result);
echo '<br /><br />';
$goodVAT = '202-239-951';
$result = Validate_BE::vat($goodVAT);
echo 'Test ' . $goodVAT .' : <br />';
var_export($result);
?>
Output :
Test 102.239.951 :
false
Test 202-239-951 :
true
Validate a belgian phone number passed as first param second specify if it would be a mobile or a traditional line or both. "/" (slash), "-" (dash), "." (dot), and white spaces are ignored. "+" are use a exit code : 0 in Belgium.
NOTE : this validate want a BELGIAN phonenumber to return true, not a valid number to call FROM belgium
<?php
// Include the package
require_once('Validate/BE.php');
$badPhone = '00 32 12 123 45 67';
$result = Validate_BE::phoneNumber($badPhone);
echo 'Test ' . $badPhone .' : <br />';
var_export($result);
echo '<br /><br />';
$goodPhone = '00 32 45 12 34 56';
$result = Validate_BE::phoneNumber($goodPhone);
echo 'Test ' . $goodPhone .' : <br />';
var_export($result);
?>
Output :
Test '00 32 12 123 45 67' :
false
Test '00 32 45 12 34 56' :
true
See now with the parameter
<?php
// Include the package
require_once('Validate/BE.php');
$goodPhone = '00 32 45 12 34 56';
$mobilePhone = '00 32 485 34 56';
echo 'Test ' . $goodPhone .' : <br />';
$result = Validate_BE::phoneNumber($goodPhone);
var_export($result) . '-';
$result = Validate_BE::phoneNumber($goodPhone,VALIDATE_BE_PHONENUMBER_TYPE_ANY);
var_export($result) . '-';
$result = Validate_BE::phoneNumber($goodPhone,VALIDATE_BE_PHONENUMBER_TYPE_NORMAL);
var_export($result) . '-';
$result = Validate_BE::phoneNumber($goodPhone,VALIDATE_BE_PHONENUMBER_TYPE_MOBILE);
var_export($result) . '-';
echo '<br /><br />';
$result = Validate_BE::phoneNumber($mobilePhone);
var_export($result) . '-';
$result = Validate_BE::phoneNumber($mobilePhone,VALIDATE_BE_PHONENUMBER_TYPE_ANY);
var_export($result) . '-';
$result = Validate_BE::phoneNumber($mobilePhone,VALIDATE_BE_PHONENUMBER_TYPE_NORMAL);
var_export($result) . '-';
$result = Validate_BE::phoneNumber($mobilePhone,VALIDATE_BE_PHONENUMBER_TYPE_MOBILE);
var_export($result);
?>
Output :
Test 00 32 45 12 34 56 :
true - true - true - false
Test 00 32 485 34 56 :
false - false - false - false
This package is in alpha state
Package contains locale validation for Canada such as:
Social Inurance Numbers (SIN)
Regions (Provinces)
Postal Codes
Phone Numbers
See also:
The package offers some methods to validate specific data for Canada
Every module of Validate follows the same philosophy. Propose some validation method retuning boolean result. Some methods have some optional parameters to set a stronger check.
<?php
// Include the package
require_once 'Validate/CA.php';
?>
The canadian social security number is on the SIS card of all canadian.
A check digit is the last one, computed the standard _get_control_number function.
<?php
// Include the package
require_once('Validate/CA.php');
$badSsn = '012345674';
$result = Validate_CA::ssn($badSsn);
echo 'Test ' . $badSsn .' : <br />';
var_export($result);
echo '<br /><br />';
$goodSsn = '123456782';
$result = Validate_CA::ssn($goodSsn);
echo 'Test ' . $goodSsn .' : <br />';
var_export($result);
?>
Output :
Test 012345674 :
false
Test 123456782 :
true
The postal code is a six-character, uniformly structured alphanumeric code in the form of ANA NAN where "A" represents an alphabetic character and "N" represents a numeric character.
The postal code is made up of two segments:
The first, the Forward Sortation Area (FSA), is a combination of three characters (alpha - numeric - alpha).
It identifies a major geographic area in an urban or a rural location.
The third character of the FSA segment (M4B), in conjunction with the first two characters, describes an exact area of a city or town or other geographic area.
The second segment, Local Delivery Unit (LDU), is a combination of three characters (numeric - alpha - numeric).
It identifies the smallest delivery unit within a forward sortation area.
The LDU, identified by the last three characters of the postal code, allows for a final sort within an FSA.
In Urban Areas, the last three digits may indicate a specific city block (one side of a street between two intersecting streets), a single building or, in some cases, a large volume mail receiver.
In Rural Areas, the last three digits (LDU), together with the FSA, identify a specific Rural community.
First parameter of the method is the post code to validate.
An optional parameter for limite the request to a province.
<?php
// Include the package
require_once('Validate/CA.php');
$badPostCode = '48103';
$result = Validate_CA::postalCode($badPostCode);
echo 'Test ' . $badPostCode .' : <br />';
var_export($result);
echo '<br /><br />';
$goodPostCode = 'H2M 2J1';
$result = Validate_CA::postalCode($goodPostCode);
echo 'Test ' . $goodPostCode .' : <br />';
var_export($result);
?>
Output :
Test 48103 :
false
Test H2M 2J1 :
true
H2M 2J1 like a good post code, but province don't exit.
<?php
// Include the package
require_once('Validate/CA.php');
$postalCode = 'H2M 2J1'; // in Montreal area
$result = Validate_CA::postalCode($postalCode);
echo 'Test ' . $postalCode .' : <br />';
var_export($result);
echo '<br /><br />';
$result = Validate_CA::postalCode($postalCode,'QC');
// QC for Montreal
echo 'Test ' . $postalCode .' in QC: <br />';
var_export($result);
echo '<br /><br />';
$result = Validate_CA::postalCode($postalCode,'AB');
// AB for Toronto
echo 'Test ' . $postalCode .' in AB: <br />';
var_export($result);
?>
Output :
Test H2M 2J1 :
true
Test H2M 2J1 in QC:
true
Test H2M 2J1 in AB:
false
Canada and the United States share the same numbering plan, hence you can also call Validate_US::phoneNumber()
Can allow only seven digit numbers.
Also allows the formats, (xxx) xxx-xxxx, xxx xxx-xxxx, and now x (xxx) xxx-xxxx or various combination without spaces or dashes.
<?php
// Include the package
require_once('Validate/CA.php');
$phoneNumber = '467875098x';
$result = Validate_CA::phoneNumber($phoneNumber);
echo 'Test ' . $phoneNumber .' : <br />';
var_export($result);
echo '<br />';
$phoneNumber = '4678750987';
$result = Validate_CA::phoneNumber($phoneNumber);
echo 'Test ' . $phoneNumber .' : <br />';
var_export($result);
?>
Output :
Test 467875098x :
false
Test 4678750987 :
true
See now with the parameter
<?php
// Include the package
require_once('Validate/CA.php');
$phoneNumber = '8750987';
$result = Validate_CA::phoneNumber($phoneNumber,false);
echo 'Test ' . $phoneNumber .' : <br />';
var_export($result);
echo '<br /><br />';
$phoneNumber = '8750987';
echo 'Test ' . $phoneNumber .' : <br />';
echo 'With $requireAreaCode false <br />';
$result = Validate_CA::phoneNumber($phoneNumber,false);
var_export($result);
echo '<br />';
echo 'With $requireAreaCode true<br />';
$result = Validate_CA::phoneNumber($phoneNumber,true);
var_export($result);
echo '<br /><br />';
$phoneNumber = '(467)8750987';
echo 'Test ' . $phoneNumber .' : <br />';
echo 'With $requireAreaCode false <br />';
$result = Validate_CA::phoneNumber($phoneNumber,false);
var_export($result);
echo '<br />';
echo 'With $requireAreaCode true<br />';
$result = Validate_CA::phoneNumber($phoneNumber,true);
var_export($result);
?>
Output :
Test 8750987 :
true
Test 8750987 :
With $requireAreaCode false
true
With $requireAreaCode true
false
Test (467)8750987 :
With $requireAreaCode false
false
With $requireAreaCode true
true
This package is in alpha state
Package contains locale validation for Switzerland such as:
Social insurance number (aka SSN)
Postal Codes
Swiss university's immatriculation number
See also :
This package is in alpha state
Package contains locale validation for Germany such as:
Postal Code
Bank Code
See also :
This package is in alpha state
Package contains locale validation for Denmark such as:
Social Security Number (CPR Nummer)
Car Registration number
Postal Codes
Phone Numbers
See also :
This package is in alpha state
Package contains locale validation for Spain such as:
DNI (El Documento Nacional de Indentidad a chequear)
See also :
This package never released
Package contains locale validation for Mexico such as:
DNI (El Documento Nacional de Indentidad a chequear)
Postal code
Region (states)
Phone numbers
See also :
Specific validation methods for data used in Finland.
See also:
Specific validation methods for data used in Finland.
Every module of Validate follows the same philosophy. Propose some validation method which returns a boolean result.
Some methods have some optional parameters to set stronger checks.
<?php
// Include the package
require_once 'Validate/FI.php';
// Validate Finnish telephone number
$phoneNumber = '+358 40 1234567';
if ( Validate_FI::phoneNumber($phoneNumber) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
Output:
Valid
bool Validate_FI::postalCode
(
string $number
,
bool $strong
)
Validate Finnish postal code.
Five digit postal code, maybe with a leading 'FI-' (XXXXX or FI-XXXXX).
An optional parameter for activate strong checks using a list of postcodes (not implemented).
$number
The postal code to be validated
$strong
optional; strong checks (e.g. against a list of postal codes)(not implemented)
boolean - true if postal code is valid, false otherwise
Using postalCode()
<?php
// Include the package
require_once 'Validate/FI.php';
$postalCode = '00100';
if ( Validate_FI::postalCode($postalCode) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
bool Validate_FI::phoneNumber
(
string $number
)
Validate Finnish telephone number.
Simple check: number must be numeric when (, ), -, +, ., ' ' chars are removed and 3-20 digit number.
$number
The telephone number to be validated
boolean - true if telephone number is valid, false otherwise
Using phoneNumber()
<?php
// Include the package
require_once 'Validate/FI.php';
$phoneNumber = '+358 40 1234567';
if ( Validate_FI::phoneNumber($phoneNumber) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
bool Validate_FI::carLicensePlate
(
string $number
)
Validate Finnish car license plate number. Format: AAA-XXX, CD-XXXX or C-XXXXX. First or only number cannot be zero.
AAA-XXX: AAA is 2-3 letters UPPERCASE A-Z + ÅÄÖ and XXX is 1-3 numbers.
CD-XXXX: CD- and XXXX is 1-4 numbers (diplomat licence plate).
C-XXXXX: C- and XXXXX is 1-5 numbers (other tax-free diplomat licence plate).
$number
The license plate number to be validated
boolean - true if license plate number is valid, false otherwise
Using carLicensePlate()
<?php
// Include the package
require_once 'Validate/FI.php';
$carLicensePlate = 'ABC-123';
if ( Validate_FI::carLicensePlate($carLicensePlate) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
bool Validate_FI::bikeLicensePlate
(
string $number
)
Validate Finnish motorbike license plate number. Format: AAAXXX. First or only number cannot be zero.
Where AAA is 2-3 letters UPPERCASE A-Z + ÅÄÖ and XXX is 1-3 numbers. Letters and numbers are actually in separate lines.
$number
The license plate number to be validated
boolean - true if license plate number is valid, false otherwise
Using bikeLicensePlate()
<?php
// Include the package
require_once 'Validate/FI.php';
$bikeLicensePlate = 'ABC123';
if ( Validate_FI::bikeLicensePlate($bikeLicensePlate) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
mixed Validate_FI::pin
(
string $number
,
bool $info
)
Validate Personal Identity Number (HETU).
The Finnish PIN number (HETU) aka Social Security Number (SSN) is a 11 digit number with birthdate as ddmmyycxxxy where c is century, xxx is a three digit individual number and the last digit is a control number (y).
If xxx is odd it's a male PIN number and if even a female.
Return gender (Male or Female) and date of birth (YYYY-MM-DD) in array if PIN is valid, is available by switching $info (2nd parameter) to true.
Example: 010101-123N would be a male and born in 1st of January 1901.
$number
PIN number to be validated
$info
optional; Return gender and date of birth on success
mixed - Returns true or false if $info = false or gender (Male or Female) and date of birth (YYYY-MM-DD) in array if PIN is valid, false otherwise
Using pin() in default mode
<?php
// Include the package
require_once 'Validate/FI.php';
$pin = '010101-123N';
if ( Validate_FI::pin($pin) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
Using pin() with $info
= TRUE
<?php
// Include the package
require_once('Validate/FI.php');
$pin = '010101-123N';
if ( $userinfo = Validate_FI::pin($pin, true) ) {
print_r($userinfo);
} else {
print 'Not valid!';
}
?>
Output:
Array
(
[0] => Male
[1] => 1901-01-01
)
bool Validate_FI::finuid
(
string $number
)
Validate Finnish Unique Identification Number (SATU). Format: xxxxxxxxy.
FINUID (SATU) is a 9 digit number. The last digit is a control number.
$number
FINUID number to be validated
boolean - true if FINUID is valid, false otherwise
Using finuid()
<?php
// Include the package
require_once 'Validate/FI.php';
$finuid = '10011187H';
if ( Validate_FI::finuid($finuid) ) {{
print 'Valid';
} else {
print 'Not valid!';
}
?>
bool Validate_FI::businessId
(
string $number
)
Validate Finnish Business ID (Y-tunnus). Format: xxxxxxx-y.
The Finnish Business ID number (Y-tunnus) is a 9 digit number. The last digit is a control number (y).
$number
Business ID number to be validated
boolean - true if Business ID is valid, false otherwise
Using businessId()
<?php
// Include the package
require_once 'Validate/FI.php';
$businessId = '1572860-0';
if ( Validate_FI::businessId($businessId) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
bool Validate_FI::partyId
(
integer $number
)
Validate Finnish Party Identification number (OVT-tunnus).
The Finnish Party Identification number (OVT-tunnus) is a 12-17 digit number and it is generated from Business ID.
Example: 0037AAAAAAAABBBBB, 0037 indicates Finland, AAAAAAAA is the Business ID and BBBBB is optional organization number.
$number
Party Identification number to be validated
boolean - true if number is valid, false otherwise
Using partyId()
<?php
// Include the package
require_once 'Validate/FI.php';
$partyId = '003715728600';
if ( Validate_FI::partyId($partyId) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
bool Validate_FI::vatNumber
(
string $number
)
Validate Finnish Value Added Tax number (ALV-numero). Format: FIXXXXXXXX.
The Finnish VAT number (ALV-numero) is maximum of 14 digits and is generated from Business ID.
$number
VAT number to be validated
boolean - true if VAT number is valid, false otherwise
Using vatNumber()
<?php
// Include the package
require_once 'Validate/FI.php';
$vatNumber = 'FI15728600';
if ( Validate_FI::vatNumber($vatNumber) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
bool Validate_FI::bankAccount
(
string $number
)
Validate Finnish bank account number. Format: XXXXXX-XXXXXXXX, 6 digits, - and 2-8 digits.
This method checks the bank account number according to Finnish Bank Association.
$number
Finnish bank account number to be validated
boolean - true if bank account is valid, false otherwise
Using bankAccount()
<?php
// Include the package
require_once 'Validate/FI.php';
$bankAccount = '159030-776';
if ( Validate_FI::bankAccount($bankAccount) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
bool Validate_FI::refNum
(
string $number
)
Validate Finnish bank reference number.
This method checks the bank reference number according to Finnish Bank Association.
$number
Finnish bank reference number to be validated (spaces and dashes tolerated)
boolean - true if reference number is valid, false otherwise
Using refNum()
<?php
// Include the package
require_once 'Validate/FI.php';
$refNum = '61 74354';
if ( Validate_FI::refNum($refNum) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
bool Validate_FI::creditCard
(
string $number
)
Validate credit card number.
This method checks the credit card number according to Luhn algorithm. This method doesn't guarantee that the card is legitimate.
$number
credit card number to be validated (spaces and dashes tolerated)
boolean - true if credit card number is valid, false otherwise
Using creditCard()
<?php
// Include the package
require_once 'Validate/FI.php';
$creditCard = '5427 0073 1297 6425';
if ( Validate_FI::creditCard($creditCard) ) {
print 'Valid';
} else {
print 'Not valid!';
}
?>
Package contains locale validation for France such as:
SSN
Postal Code
RIB
SIREN
SIRET
Region (Departements)
See also :
This package is in beta state
Package contains locale validation for Ireland such as:
Bank Account
Drivers Licence Numbers
IBAN
Passport Numbers
Phone Numbers
Postal Code
SSN/PPSN
SWIFT Codes
See also :
The package offers some methods to validate specific data for Ireland
Every module of Validate follows the same philosophy. Propose some validation method retuning boolean result. Some methods have some optional parameters to set a stronger check.
<?php
// Include the package
require_once 'Validate/IE.php';
?>
The Personal Public Service Number (PPS No) is an identifier issued by Client Identity Services, Department of Social and Family Affairs on behalf of the Minister for Social and Family Affairs in the Republic of Ireland.
<?php
// Include the package
require_once 'Validate/IE.php';
$badSsn = '012345674';
$result = Validate_IE::ssn($badSsn);
echo 'Test ' . $badSsn .' : <br />';
var_export($result);
echo '<br /><br />';
$goodSsn = '1234567W';
$result = Validate_IE::ssn($goodSsn);
echo 'Test ' . $goodSsn .' : <br />';
var_export($result);
?>
Output :
Test 012345674 :
false
Test 1234567W :
true
There is no national post code system in Ireland; at present the postalCode method only validates Dublin postal districts: "Dublin 6W", or "D 4" for example.
Ireland has phone numbers that are built similar to US and Canadian numbers however they have a couple of distinctions.
For instance the STD [Standard Trunk Dial] prefix varies in length: Dublin has the code '01', Cork has '021', Galway 091, and so on.
The phoneNumber method takes two parameters: the first is the phone number to be tested and the second is a flag indicating whether the number should be checked for a valid prefix. This boolean flag defaults to true.
<?php
// Include the package
require_once 'Validate/IE.php';
$phoneNumber = '467875098x';
$result = Validate_IE::phoneNumber($phoneNumber);
echo 'Test ' . $phoneNumber .' : <br />';
var_export($result);
echo '<br />';
$phoneNumber = '014142438';
$result = Validate_IE::phoneNumber($phoneNumber);
echo 'Test ' . $phoneNumber .' : <br />';
var_export($result);
?>
Output :
Test 467875098x :
false
Test 014142438:
true
See now with the requiredAreaCode parameter being utilised.
<?php
// Include the package
require_once 'Validate/IE.php';
$phoneNumber = '87509824';
$result = Validate_IE::phoneNumber($phoneNumber,false);
echo 'Test ' . $phoneNumber .' : <br />';
var_export($result);
echo '<br /><br />';
$phoneNumber = '8750987';
echo 'Test ' . $phoneNumber .' : <br />';
echo 'With $requireAreaCode false <br />';
$result = Validate_IE::phoneNumber($phoneNumber,false);
var_export($result);
echo '<br />';
echo 'With $requireAreaCode true<br />';
$result = Validate_IE::phoneNumber($phoneNumber,true);
var_export($result);
echo '<br /><br />';
$phoneNumber = '(0915)8750987';
echo 'Test ' . $phoneNumber .' : <br />';
echo 'With $requireAreaCode false <br />';
$result = Validate_IE::phoneNumber($phoneNumber,false);
var_export($result);
echo '<br />';
echo 'With $requireAreaCode true<br />';
$result = Validate_IE::phoneNumber($phoneNumber,true);
var_export($result);
?>
Output :
Test 87509824 :
true
Test 8750987 :
With $requireAreaCode false
true
With $requireAreaCode true
false
Test (091)8750987 :
With $requireAreaCode false
false
With $requireAreaCode true
true
This package is in alpha state
Package contains locale validation for Indian such as:
Permanent Account Number (PAN and TAN)
State and Union Territory codes
Telephone Numbers
Postal (Zip) Codes
Vehicle License Plate Numbers
See also :
This package is in alpha state
Package contains locale validation for Iceland such as:
SSN (Social Security Number (Icelandic: kennitala))
Postal code (Icelandic: post numer)
Address (Icelandic: heimilisfang)
Telephone number (Icelandic: simanumer)
See also :
This package is in alpha state
Package contains locale validation for Netherlands such as:
Social insurance number (aka SIN)
Postal Code
Phone Number
Bank Account Number (based on 11proef)
See also :
This package is in alpha state
Package contains locale validation for New Zealand such as:
Postal Codes
IRD numbers
Regional codes
Telephone number
Bank AC
See also :
This package is in alpha state
Package contains locale validation for Poland such as: This class provides methods to validate:
NIP (Polish tax identification number)
Bank account numbers
PESEL (Polish human identification number)
REGON (Polish statistical national economy register)
See also :
This package is in alpha state
Package contains locale validation for Brazil such as:
Social Inurance Numbers (SIN)
Region (brazilian states)
Postal Codes
Phone Numbers
CNPJ
CPF
Vehicle's plate
See also :
This package is in alpha state
Package contains locale validation for United Kingdom such as:
SSN (National Insurance/IN)
Postal Code
Phone Number
Bank Account Number
Sort Code
Car registration numbers
Passports
Driver license
See also :
This package is in beta state
Package contains locale validation for United States such as:
Social insurance number (aka SSN)
Region (state code)
Postal Codes
Phone Numbers
See also :
This package is in alpha state.
The PEAR Validate_ZA package provides users with various locale validation routines currently for , Postal Codes and Provinces.
South African Identity Number
Regions (Provinces)
Postal Codes
See also :
Validation class for Finance data
Package to validate Finance data.
IBAN
See also :
Validation class for Credit Card
Package to validate Credit card.
Credit card number
Card security code
Card type (i.e. Visa, Mastercard...)
Theses methods only check the format of the data. For instance the package does NOT check if a card is a legitimate card registered with a card issuer, or if the card is reported stolen, etc...
See also :
Validation class for ISPN (International Standard Product Numbers)
Package contains ISPN (International Standard Product Numbers) validations such as:
ISSN (International Standard Book Number)
ISBN (International Standard Serial Number)
ISMN (International Standard Music Number)
EAN/UCC-8 number
EAN/UCC-13 number
EAN/UCC-14 number
UCC-12 (U.P.C.) ID number
SSCC (Serial Shipping Container Code)
See also :
PHP implementation of the Akismet REST API
A wrapper for the AWS S3 webservices.
Services_Amazon_S3 is a wrapper for AWS S3. It provides both a OO-style interface, but also an extension in order to use S3 through PHP's stream wrappers.
Retrieving all buckets from a S3 account
The following examples illustrates how to retrieve all buckets of a given S3 account. All you need to replace in the snippet are the $key and $secret variables.
<?php
require_once 'Services/Amazon/S3.php';
$key = 'your key';
$secret = 'your secret';
$s3 = Services_Amazon_S3::getAccount($key, $secret);
echo '<ul>';
foreach ($s3->getBuckets() as $bucket) {
echo "<li>{$bucket->name}</li>";
}
echo '</ul>';
?>
The following examples illustrates how to retrieve all objects of a given S3 bucket. All you need to replace in the snippet are the $bucket, $key and $secret variables.
Retrieving all objects from a bucket in S3
<?php
require_once 'Services/Amazon/S3.php';
$key = 'your key';
$secret = 'your secret';
$bucket = 'foobar';
$s3 = Services_Amazon_S3::getAccount($key, $secret);
$bucket = $s3->getBucket($bucket);
echo '<ul>';
foreach ($bucket->getObjects() as $object) {
echo "<li>{$object->name}</li>";
}
echo '</ul>';
?>
This example details how to retrieve the meta of the objects - for example, size, mimetype, etc..
Retrieving all objects meta data from a bucket in S3
<?php
require_once 'Services/Amazon/S3.php';
$key = 'your key';
$secret = 'your secret';
$bucket = 'foobar';
$s3 = Services_Amazon_S3::getAccount($key, $secret);
$bucket = $s3->getBucket($bucket);
echo '<ul>';
foreach ($bucket->getObjects() as $object) {
$object->load(Services_Amazon_S3_Resource_Object::LOAD_METADATA_ONLY);
echo "<li>{$object->name}: {$object->size} bytes ({$object->contentType})</li>";
}
echo '</ul>';
?>
The following example illustrates how to create an access URL to the objects, with a TTL (time to live, expire time).
Retrieving all objects from a bucket in S3
<?php
require_once 'Services/Amazon/S3.php';
$key = 'your key';
$secret = 'your secret';
$bucket = 'foobar';
$s3 = Services_Amazon_S3::getAccount($key, $secret);
$bucket = $s3->getBucket($bucket);
echo '<ul>';
foreach ($bucket->getObjects() as $object) {
$url = $object->getSignedUrl(120); // expire in 2 minutes
echo "<li>{$object->name}";
echo '<a href="' . $url . '">download it (link is valid for 2 minutes)</a>";
echo "</li>";
}
echo '</ul>';
?>
Generic driver-based package to post and read blog entries.
Weblogs are an important part of the internet: They allow normal people to express their opinions and thoughts to their family, friends and the whole world by simply publishing some text on a server. This process is very easy and you don't need a grade in computer science to even host a blog on your own or a rented server.
One drawback of blog software is mostly their user interface: Since it is browser based, it requires you to be either online all the time while writing your blog post, or write it in a normal text editor and copy&paste it when going online. Using a text editor you cannot apply font styles to your text.. An offline tool to write blog entries would be really nice in this case.
Considering the idea of an offline tool for writing blog entries leads to the availability of access to your blog from outside the normal web interface - e.g via web services. Most blog hosters and blogging software packages have support for such a web services API, mostly via XML-RPC.
Unfortunately, there are many different of these application programming interfaces in the wild. Some of them support only posting of blog entries, other ones also allow reading. Some blog softwares support images and tags in their posts, others not. The variety is large, and so you could end up writing custom code for every blog server you want to access because of the differences in their API.
This is the point at which Services_Blogging comes into play: It provides a unified API to post and read blog entries, independent of the API supported by the server software hosting the blog. It uses a driver-based approach to communicate with different APIs out there. If a new blogging API is invented, someone just needs to write a driver for the Services_Blogging package, and everyone can access also this blogs.
As of April 2007, the package has the following drivers:
Blogger
LiveJournal
MetaWeblog
To post entries to your blog, you first need to obtain an instance of the Services_Blogging_Driver suitable for your blog. You need to either find out which API your blog system supports, or let Services_Blogging magically autodiscover those settings.
Creating a driver is done using Services_Blogging's factory() method. It requires five parameters: The driver name, username, password, server URL and the path of the API on the server.
Instantiating a driver using Services_Blogging::factory()
<?php
require_once 'Services/Blogging.php';
$bl = Services_Blogging::factory(
'metaWeblog', //driver name
'username',
'password',
'http://blog.example.com',
'/xmlrpc.php'
);
?>
Some blog drivers don't need server and path variables, just fill in
null
in this case.
Since it can be tedious to find out which settings are needed for your own blog, this package has a feature called autodiscovery. You either can automatically detect the settings needed to create an instance of the driver needed, or let the class automatically detect and return an instance of the driver needed for your blog system.
Autodiscovering the settings of your blog can be done via Services_Blogging's discoverSettings(), passing the URL of your blog as only parameter. It returns an array with all the needed settings.
Most of the blogging softwares today support multiple blogging APIs. After getting a list of supported APIs via autodiscovery, you need to choose which one fits you best; it's usually the one supporting the most features. Services_Blogging also helps you with this task by providing a method called getBestAvailableDriver(), just passing the array of recently discovered settings to it. Now you have all data needed to instantiate the driver itself.
Auto-discovering settings
<?php
require_once 'Services/Blogging.php';
$settings = Services_Blogging::discoverSettings('http://blog.example.com');
var_dump($settings);
echo Services_Blogging::getBestAvailableDriver($settings) . "\r\n";
//go on with factory()
?>
If that all is still too much work for you, you can let do Services_Blogging everything by calling discoverDriver(), passing the URL of your blog, your username and password to it. It returns a the driver object.
Creating a driver automatically
<?php
require_once 'Services/Blogging.php';
$bl = Services_Blogging::discoverDriver(
'http://blog.example.com',
'username',
'password'
);
?>
The autodiscovery methods throw an exception of type Services_Blogging_Exception if something goes wrong (either autodiscovery fails because your blog software does not support it, or there is no suitable driver for your blog).
Due to the difference of features in the existing blogging APIs, there are two driver classes: Services_Blogging_Driver and Services_Blogging_ExtendetDriver (which extends the first).
Drivers extending Services_Blogging_Driver do only support creating, saving and deleting posts. They are not able to list (or modify) existing posts.
Drivers extending from Services_Blogging_ExtendetDriver do have more features: Reading existing posts, get a list of recent posts, and getting a list of the titles of the recent posts.
Beside the difference in features regarding reading and listing existing posts, the APIs have different support for blog post properties. While some simple APIs only allow to define the text of a post, better ones allow to set the title and other ones also support extendet properties like date, date to publish, categories and other.
Once you have the driver object for your blog, you should get a list of post properties supported by your driver. The method is getSupportedPostProperties(); it returns an array of property names. Currently, the following properties are possible:
title
content
publishdate
date
url
categories
See the API documentation of Services_Blogging_Post for the data types.
Posting an entry
<?php
require_once 'Services/Blogging.php';
$bl = Services_Blogging::factory(
'metaWeblog',
'username', 'password',
'http://blog.example.com', '/xmlrpc.php'
);
$post = $bl->createNewPost();
//$post->setId('14');
$post->title = 'Modified post title';
$post->content = "This is a modified test post by"
. " Services_Blogging\r\n\r\nSecond line\r\nThird one";
$post->categories = array('cat1', 'cat3');
$bl->savePost($post);
$nLastPostId = $post->id;
echo 'post id: ' . $nLastPostId . "\r\n";
//$bl->deletePost(17);
var_dump($bl->getPost($nLastPostId));
var_dump($bl->getRecentPostTitles(2));
var_dump($bl->getRecentPosts());
?>
Object-oriented abstraction for del.icio.us XML API.
Services_Delicious is an abstraction for the webservice of the social bookmarking site del.icio.us. del.icio.us allows you to easily add sites you like to your personal collection of links, to categorize those sites with keywords, and to share your collection not only between your own browsers and machines, but also with others.
del.icio.us is using "tags" to categorize your bookmarks and allows other users to browse bookmarks by topic.
Services_Delicious enables you to access, add and even delete your social bookmarks by using a Services_Delicious class that provides functions like getAllPosts(), addPost() or deletePost().
The following examples show how to use some basic features of Services_Delicious:
Fetching your recent posts
<?php
require_once 'Services/Delicious.php';
$dlc = &new Services_Delicious($username, $password);
$posts = $dlc->getRecentPosts();
echo '<pre>';
print_r($posts);
echo '</pre>';
?>
Getting all tags that you used in your bookmarks
<?php
require_once 'Services/Delicious.php';
$dlc = &new Services_Delicious($username, $password);
$tags = $dlc->getTags();
echo '<pre>';
print_r($tags);
echo '</pre>';
?>
Adding a new bookmark
<?php
require_once 'Services/Delicious.php';
$dlc = &new Services_Delicious($username, $password);
$result = $dlc->addPost('http://pear.php.net', 'PEAR', 'The PHP Extension and Application Repository', 'php');
if (PEAR::isError($result)) {
die($result->getMessage());
} else {
echo 'Success';
}
?>
Object-oriented abstraction for eBay's XML API.
Services_Ebay is an object-oriented abstraction layer for eBay's XML API. In addition to a SOAP-service, eBay provides an API, that does not follow any standards except wrapping all webservice calls and parameters in XML. This webservice still is more powerful than eBay's SOAP server and in addition has been heavily tested by real-life applications.
eBay's webservice enables you to use all of eBay's features (except bidding on items) in your own PHP applications. The features range from adding new items to managing the transaction, payment and shipping. Currently there are about 70 method calls available, all accept a range of parameters.
Services_Ebay (as of version 0.7.0) already provides wrappers for 50 methods as well as some model classes which help you working with the results from the calls.
To use this package you will need PHP5 with cURL support enabled and should be familiar with PHP5's exception handling.
The eBay webservice is of course not free to use by the public. To develop and test your applications, you will have to register as an eBay developer (which is free of charge). Furthermore, you will not be able to test and develop on the eBay site, before your application has been certified.
The eBay Developers Program Sandbox is a test environment that represents a "mini" eBay site. The Sandbox provides the most important features of the eBay site, allowing you to build and test your application in a non-production environment. The eBay Sandbox supports both API testing as well as site testing via the GUI interface.
While developing your application, you will always be using the eBay sandbox, which can be accessed via web at http://sandbox.ebay.com/. This site looks and behaves like any eBay website you are being used to.
To develop applications in the sandbox you will have to register at the eBay developers program. To do this, follow these steps:
In order to register as an eBay developer, you'll have to be an eBay user. As eBay users are valid on all international sites, it is sufficient if you have an eBay user id for your local eBay site, like ebay.com or ebay.de.
Next, you will have to register as an eBay developer at the eBay developer program. This procedure can take some time, as they require you to enter a lot of information, so you should do this carefully. If you are developing an open source application using Services_Ebay, you should apply for an Individual license.
After the registration has been finisehd, you will recieve three keys that you will need to authenticate your eBay application: DevID, AppID and CertID. You will need these keys later so you should save them somewhere.
As the sandbox does not share any data with the eBay sites, you will have to create new users that you can use to add items, make transactions and give feedback. You can create as many users as you need for testing purposes, just use the registration form in the sandbox.
You will need a valid email address for each of your users, as well as a valid US address and telephone number, you can easily get on by using YellowPages. A credit card is not required, your test users will receive money from eBay they can spend in the sandbox. Of course, this is no real money, which has not use outside of the sandbox.
If you want your test users to sell items on eBay, you will need to validate them. This can be done using the ValidateTestUserRegistration() API call, which is already supported by Services_Ebay.
eBay's Auth&Auth process is quite complex and can be the biggest hurdle for getting started with the webservice. To make an API call the following information is required:
DevID (received after registration, unique per developer)
AppID (received after registration, unique per application)
CertID (received after registration, unique per application)
Authentication Token, unique for each user of your application
While you already are in possession of the first three tokens, you still need the last one to make an API call. If your application is used by more than one user (which is the case for web applications), eBay does not want your application to receive the usernames and passwords of your users. If a user authenticates, your application is supposed to redirect him to the eBay login page and pass an additional parameter (a so called RuName). The user will then enter his login information as he is used to on the eBay website and then authorize your application to make API calls on his behalf.
After that, eBay will redirect the user back to your application and pass a unique token, which can be used to identify this user when your application is making API calls. This technique has several advantages:
Single sign-on system for eBay applications
When your application is hacked, no passwords are revealed
The user only sees the login screen he is used to.
As this authentication procedure is quite complex and requires various API calls to function there is an easier way, which can be used for testing. eBay provides the so called Single-User-Tool, an HTML-based tool, which creates tokens that you can use to authenticate a user. All you need to do is submit your DevID, AppID and CertID and select whether the token will be used in the sandbox or production environment.
Services_Ebay provides a lot of examples, which demonstrate how the API calls have to be
used. After installing Services_Ebay, they will be located in the docs/ directory of your
PEAR installation. In order to run the examples, you will have to supply the authentication
credentials you received from eBay. The easiest way to do this, is to modify the config.php
file, which is located in the examples
folder.
The configuration file
<?php
// DevID, AppID and CertID
$devId = 'HFGHSK7JKKJ82JKJFJHF84LKH86Z3JFF71KJKH';
$appId = 'IUENVCLJEGBN62JLKKLJHD34KKJL-GDJHDGJHD';
$certId = 'GHKL67JKDJLKJGFBNMBCHGDLÖWJH241KKHJKKJ';
// Username and password, only required by some calls
// that set up the Auth&Auth mechanism
$username = 'YourTestUser';
$password = 'Secret Pass';
// Token as returned from the Single-User-Tool
$token = 'AgAAAA.......3jfiEQ**';
?>
Since mid-2005, the eBay API will only accept UTF-8 encoded XML-documents. As encoding all data to UTF-8 is tedious, Services_Ebay will take care of this for you. All you need to do is specify the encoding you want to use in your script when creating a session object.
Using ISO-8859-1 in your script
<?php
require_once 'Services/Ebay.php';
// pass some authentication data
$session = Services_Ebay::getSession($devId, $appId, $certId, 'ISO-8859-1');
$session->setToken($token);
// create new proxy object
$ebay = new Services_Ebay($session);
$item = Services_Ebay::loadModel('Item', null, $session);
$item->Category = 57882;
$item->Title = 'International Item';
$item->Description = 'This description contains Umlaut characters like Ä, ü and ß';
$item->Location = 'At my home';
$item->MinimumBid = '532.0'; $item->VisaMaster = 1;
$item->ShippingType = 1;
$item->CheckoutDetailsSpecified = 1;
$item->Country = 'US';
$item->SetShipToLocations(array('US', 'DE', 'GB'));
$item->addShippingServiceOption(1, 1, 3, 1, array('US'));
$result = $ebay->AddItem($item);
?>
The umlaut characters contained in the description of the item will be automatically converted to UTF-8 when the XML-document is created. Furthermore the result document which is returned by the eBay API will be decoded again to ISO-8859-1 so you do not have to worry about UTF-8 at all.
Of course, it is also possible to supply UTF-8 encoded data to Services_Ebay. All you have to do is change the encoding type, when creating the session object.
Using UTF-8 in your script
<?php
require_once 'Services/Ebay.php';
// pass some authentication data
$session = Services_Ebay::getSession($devId, $appId, $certId, 'UTF-8');
$session->setToken($token);
// create new proxy object
$ebay = new Services_Ebay($session);
$item = Services_Ebay::loadModel('Item', null, $session);
$item->Category = 57882;
$item->Title = 'International Item';
$item->Description = utf8_encode('This description contains Umlaut characters like Ä, ü and ß');
$item->Location = 'At my home';
$item->MinimumBid = '532.0'; $item->VisaMaster = 1;
$item->ShippingType = 1;
$item->CheckoutDetailsSpecified = 1;
$item->Country = 'US';
$item->SetShipToLocations(array('US', 'DE', 'GB'));
$item->addShippingServiceOption(1, 1, 3, 1, array('US'));
$result = $ebay->AddItem($item);
?>
In this example you are using utf8_encode() to encode the data prior to passing it to Services_Ebay. To avoid duplicated encoding, you need to set the encoding to UTF-8.
As Services_Ebay is a PHP 5 only package, it uses exception handling and the PEAR_Exception class as base class for all exceptions. Exceptions can be thrown, whenever you try to call any of the API calls provided by Services_Ebay, which means you should always nest those in a try/catch-block:
Exception handling
<?php
require_once 'Services/Ebay.php';
// pass some authentication data
$session = Services_Ebay::getSession($devId, $appId, $certId);
$session->setToken($token);
// create new proxy object
$ebay = new Services_Ebay($session);
try {
// call a method
echo $ebay->GeteBayOfficialTime();
} catch (Exception $e) {
echo "Something went wrong.";
echo $e;
}
?>
When calling a non-existent API call or passing the wrong parameters to the API, eBay will abort the API call and return an XML-document that contains error information. Services_Ebay will automatically convert this into an exception that can be easily handled by your PHP application.
In some cases, the eBay API will still process your request, even if you passed invalid parameters and include error information in the resulting XML-document alongside the actual response of your request.
In this case, the errors will be tagged as warnings, as they were not serious errors. Services_Ebay will not convert these errors to exceptions, but only to instances of Services_Ebay_Error. These objects will be stored in the Services_Ebay_Session and can be retrieved by your application at a later point.
Handling warnings
<?php
require_once 'Services/Ebay.php';
// pass some authentication data
$session = Services_Ebay::getSession($devId, $appId, $certId);
$session->setToken($token);
// create new proxy object
$ebay = new Services_Ebay($session);
try {
// call a method
echo $ebay->GeteBayOfficialTime();
} catch (Exception $e) {
// Just ignore the exception and handle them
// with any warnings, that might have occured.
}
$errors = $session->getErrors();
if (count($errors) == 0) {
echo "No errors or warnings.\n";
} else {
foreach ($errors as $error) {
printf("%s: %s (%d))\n", $error->getSeverity(), $error->getLongMessage(), $error->getCode());
}
}
?>
Services_Ebay consists of a lot of small classes, which keeps the used codebase small, as only the functionality that you use in your applications are loaded and parsed.
This will give you a short overview of the different types of objects that are provided and for which tasks they are used.
The Services_Ebay class is used for the following tasks:
The Services_Ebay provides methods to load and instantiate all of the other classes, that are included in the Services_Ebay distribution. That means that this is the only class you should include and instantiate yourself. Factory methods include loadApiCall(), getSession() and loadModel().
This class also defines some constants like the eBay site ids that you will need in your applications, constants include Services_Ebay::SITEID_ID, Services_Ebay::AUTH_TYPE_TOKEN or Services_Ebay::FEEDBACK_BRIEF. Whenever the eBay webservice expects an integer value in an XML tag, Services_Ebay tries to provide a matching constant.
The class also provides some helper methods, which can be called statically like getAvailableApiCalls().
The most important usage is that Services_Ebay acts as a proxy class for the API calls, that means you can call methods on the class which will then be redirected to the appropriate call object.
The Services_Ebay_Session class is used to handle the serialization and unserialization of the incoming and outgoing XML streams. Furthermore it builds the HTTP headers that are needed and manages all user authentication.
You will probably always use the session indirectly by at first passing it to the Services_Ebay object which will then use the session for making API calls.
Using the session class
<?php
require_once 'Services/Ebay.php';
// pass some authentication data
$session = Services_Ebay::getSession($devId, $appId, $certId);
$session->setToken($token);
// create new proxy object with the instantiated session
$ebay = new Services_Ebay($session);
?>
The Services_Ebay_Transport classes are used to build up network connections to the eBay webservices and send and recieve the raw data which has been created by Services_Ebay_Session.
Theoretically there may be different transport classes, but due to bugs in PHP's stream functions and some SSL libraries, the only working transport class is Services_Ebay_Transport_Curl, which uses PHP's curl extension.
The Services_Ebay_Call classes contain information about the API calls that the eBay webservice offers. Each API call is encapsulated in an object that contains information about the API call, which XML tags have to be used and what the call is expected to return.
There are two ways in which the call objects can be used:
It is recommended to use Services_Ebay as a proxy instead of working directly on the Call objects. Services_Ebay will instantiate the class, pass the parameters and invoke the call method on the Call object.
The Services_Ebay_Model classes act as local containers for the remote data stored on the eBay server. For example, when calling Services_Ebay::getItem(), the method will return an instance of Services_Ebay_Model_Item, which contains information about the item as well as some helper methods like Services_Ebay_Model_Item::addToDescription() which encapsulates a new API call.
Currently Services_Ebay provides models for accounts, disputes (single dispute and a list of disputes), user feedback (summary and a single feedback entry), items and list of items, MyeBay, orders, preferences, search results, shipments, eBay stores, transactions and users.
The Services_Ebay_Cache classes allow you to locally cache information that you retrieved from the eBay webservice without changing anything in your scripts. After registering a cache instance for any model type, Services_Ebay will query the cache before making a time-consuming API call.
The cache classes use a very high abstraction and allow you to create new cache containers, so you could store the data in a database, shared memory or wherever you like. Currently there is only one container available, which stores the data in the local filesystem.
To determine, whether a cache is still valid an instance of Services_Ebay_Cache_ExpiryCheck is used, which allows you to build "intelligent" caches that have a shorter expiry time the nearer the end of an auction is.
The following examples show how to use some basic features of Services_Ebay:
Using a proxy object
<?php
require_once 'Services/Ebay.php';
// pass some authentication data
$session = Services_Ebay::getSession($devId, $appId, $certId);
$session->setToken($token);
// create new proxy object
$ebay = new Services_Ebay($session);
// call a method
echo $ebay->GeteBayOfficialTime();
?>
Working directly with a Call object
<?php
require_once 'Services/Ebay.php';
// pass some authentication data
$session = Services_Ebay::getSession($devId, $appId, $certId);
$session->setToken($token);
$call = Services_Ebay::loadAPICall('GetEbayOfficialTime');
$result = $call->call($session);
echo $result;
?>
Using the model classes
<?php
require_once 'Services/Ebay.php';
// pass some authentication data
$session = Services_Ebay::getSession($devId, $appId, $certId);
$session->setToken($token);
// get an eBay item
$item = $ebay->GetItem(4501333179, 2);
// The Seller property is an object as well
echo 'User-Id of the seller: '.$item->Seller->UserId.'<br />';
// convert the item to an array
echo '<pre>';
print_r($item->toArray());
echo '</pre>';
// Use methods of the model
$item->AddToDescription('I forgot some important information');
// Change the item
$item->Title = 'The new title of the item';
$ebay->ReviseItem($item);
?>
Services_GeoNames is a PHP interface to the various webservices offered by the GeoNames project.
Services_GeoNames is a PHP interface to the various webservices offered by the GeoNames project.
The GeoNames database contains over 8,000,000 geographical names corresponding to over 6,500,000 unique features. All features are categorized into one out of nine feature classes and further subcategorized into one out of 645 feature codes. Beyond names of places in various languages, data stored include latitude, longitude, elevation, population, administrative subdivision and postal codes. All coordinates use the WGS84 system (World Geodetic System 1984).
Those data are accessible free of charge through a number of Web services and a daily database export. The Web services include direct and reverse geocoding, finding places through postal codes, finding places next to a given place, and finding Wikipedia articles about neighbouring places.
To install the package with pear just do:
$ pear install -f Services_GeoNames
And to uninstall it:
$ pear uninstall Services_GeoNames
To instanciate the main class just do:
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
?>
And if you have a commercial account:
<?php require_once 'Services/GeoNames.php'; $geo = new Services_GeoNames('your_username', 'your_authtoken'); ?>
To call a webservice method, just instanciate the class as described above and call the desired method with an array of parameters, for example:
<?php
require_once 'Services/GeoNames.php';
$geonames = new Services_GeoNames('username', 'some authtoken...');
$countries = $geonames->countryInfo(array('lang' => 'es'));
echo "List of all countries in spanish language:\n";
foreach ($countries as $country) {
printf(" - %s (capital: %s)\n", $country->countryName, $country->capital);
}
?>
Every method take an array as single parameter, for example in the GeoNames API documentation when you see something like:
Webservice Type : REST Url : ws.geonames.org/citiesJSON? Parameters : north,south,east,west : coordinates of bounding box callback : name of javascript function (optional parameter) lang : language of placenames and wikipedia urls (default = en) maxRows : maximal number of rows returned (default = 10)
That means that you can call the cities() method as follows:
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
$cities = $geo->cities(array(
'north' => 44.1,
'south' => -9.9,
'east' => -22.4,
'west' => 55.2,
'lang' => 'de',
'maxRows' => 5,
));
?>
Note that for convenience, some methods can take a geonameId (integer) instead of the array as unique parameter, for example, the following two calls are equivalent:
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
// theses two method calls are equivalent
$children = $geo->children(3175395);
$children = $geo->children(array('geonameId' => 3175395));
?>
Method name | Parameters | Return type |
---|---|---|
children() | int geonameId or array | array |
cities() | int geonameId or array | array |
countryCode() | array | stdclass |
countryInfo() | array | array |
countrySubdivision() | array | stdclass |
earthquakes() | array | array |
findNearby() | array | array |
findNearbyPlaceName() | array | array |
findNearbyPostalCodes() | array | stdclass |
findNearbyStreets() | array | array |
findNearByWeather() | array | stdclass |
findNearbyWikipedia() | array | array |
findNearestAddress() | array | stdclass |
findNearestIntersection() | array | stdclass |
get() | array | stdclass |
gtopo30() | array | stdclass |
hierarchy() | int geonameId or array | array |
neighbourhood() | array | stdclass |
neighbours() | int geonameId or array | array |
postalCodeCountryInfo() | array | |
postalCodeLookup() | array | array |
postalCodeSearch() | array | stdclass |
search() | array | array |
siblings() | int geonameId or array | array |
srtm3() | array | stdclass |
timezone() | array | stdclass |
weather() | array | array |
weatherIcao() | array | stdclass |
wikipediaBoundingBox() | array | array |
wikipediaSearch() | array | array |
Method name | Parameters | Return type | Description |
---|---|---|---|
Services_GeoNames::getSupportedEndpoints() | array | returns an array of all supported services endpoints (aka methods) | |
Services_GeoNames::getRequest() | HTTP_Request2 | returns the HTTP_Request2 request instance | |
Services_GeoNames::setRequest() | HTTP_Request2 | sets the HTTP_Request2 request instance |
Services_GeoNames always raise either a Services_GeoNames_Exception or a Services_GeoNames_HTTPException instance, if you don't care of fine grained exceptions, you can just catch the Services_GeoNames_Exception, as it's the parent class of the Services_GeoNames_HTTPException. Here's an example of fine grained exception handling:
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
// now geonames uses your url
try {
$children = $geo->children(3175395);
} catch (Services_GeoNames_HTTPException $exc) {
// an http error occured
echo "HTTP error: " . $exc->getMessage();
} catch (Services_GeoNames_Exception $exc) {
// a programming error or an api error occured
echo "API error: " . $exc->getMessage();
}
?>
If for some reason you need to change the web service url, you can do the following:
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
$geo->url = 'http://alternate.geonames.org';
// now geonames uses your url
try {
$children = $geo->children(3175395);
} catch (Services_GeoNames_Exception $exc) {
echo "Failed: " . $exc->getMessage();
}
?>
If you need high availability or if you are using the commercial version of the web services, Services_GeoNames allows you to specify an array of failover servers, you would just do:
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
$geo->failoverServers[] = 'http://failover1.geonames.org';
$geo->failoverServers[] = 'http://failover2.geonames.org';
$geo->failoverServers[] = 'http://failover3.geonames.org';
// now geonames will try the main url and if it fails, it will loop through
// the failovers servers you have just configured
try {
$children = $geo->children(3175395);
} catch (Services_GeoNames_Exception $exc) {
echo "Failed: " . $exc->getMessage();
}
?>
If you need a custom request, for example if you are behind a proxy, you can modify the Services_GeoNames request instance, for example:
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
// customize the http request
$geo->getRequest()->setConfig(array(
'proxy_host' => 'localhost',
'proxy_port' => 8118,
));
// now geonames uses your proxy
try {
$children = $geo->children(3175395);
} catch (Services_GeoNames_Exception $exc) {
echo "Failed: " . $exc->getMessage();
}
?>
For more infos please read the HTTP_Request2 documentation.
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
$cities = $geo->search(array('name_equals' => 'Paris'));
echo "List of cities named Paris:\n";
foreach($cities as $city) {
printf(" - %s (%s)\n", $city->name, $city->countryName);
}
echo "\n";
?>
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
$postalCodes = $geo->findNearbyPostalCodes(array(
'lat' => 43.606,
'lng' => 1.444,
'radius' => 10, // 10km
'maxRows' => 100
));
echo "List of postal codes near by Toulouse in a radius of 10km:\n";
foreach ($postalCodes as $code) {
printf(" - %s (%s)\n", $code->postalCode, $code->placeName);
}
echo "\n";
?>
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
$countries = $geo->countryInfo(array('lang' => 'es'));
echo "List of all countries in spanish language:\n";
foreach ($countries as $country) {
printf(" - %s (capital: %s)\n", $country->countryName, $country->capital);
}
echo "\n";
?>
<?php
require_once 'Services/GeoNames.php';
$geo = new Services_GeoNames();
// retrieve the geonameId if we don't know it
$array = $geo->countryInfo(array('country' => 'FR'));
$geonameId = $array[0]->geonameId;
$neighbours = $geo->neighbours(array('geonameId' => $geonameId));
echo "Neighbours of France are:\n";
foreach ($neighbours as $neighbour) {
printf(" - %s\n", $neighbour->countryName);
}
?>
Provides access to the Google SOAP Web APIs
Services_Google allows easy access to the Google SOAP APIs for the search engine, spelling suggestions, and cache.
In order to use this package you will need to register for an API key.
Please note that as of December 5, 2006, Google is no longer issuing new API keys for the SOAP Search API.
Services_Google enables you to search, spell checks and get cached pages from Google's cache by using a Services_Google class that provides functions like search(), spellingSuggestion() or getCachedPage().
The following examples show how to use some basic features of Services_Google:
Search Google for PEAR
<?php
require_once 'Services/Google.php';
$google = new Services_Google($key);
$google->queryOptions['limit'] = 30;
$google->search("PEAR");
foreach ($google as $key => $result) {
echo "$key]\t$result->title\n";
}
?>
Spell Checking
<?php
require_once 'Services/Google.php';
$google = new Services_Google($key);
echo $google->spellingSuggestion("wahll")."\n";
?>
Services_Libravatar is a PHP5 library to the Libravatar service that delivers avatar pictures to other websites: It gives you image URLs for email addresses.
Libravatar offers several benefits over the classic Gravatar service: It is federated - you can host the avatar server for your mail addresses yourself. It is open source. It can fall back to Gravatar if Libravatar does not have a picture for the email address, making it perfectly backwards compatible. It allows you to give a picture to your OpenID.
After including the base Services_Libravatar file, create an object of the class. You may set default options for image size, default url, HTTPS mode and hashing algorithm.
After you created the Services_Libravatar
instance, call
Services_Libravatar::getUrl()
on it with the email as parameter.
Creating a libravatar instance
<?php
require_once 'Services/Libravatar.php';
$sla = new Services_Libravatar();
$sla->setSize(256)
->setDefault('identicon');
$url = $sla->getUrl('foo@example.org');
?>
Instead of manually deciding if you need HTTPS or not,
Services_Libravatar
will detect that automatically when
calling
detectHttps().
If you temporarily need different options for one image, you may
pass them as option array as second parameter to getUrl()
.
They will override the class options that were configured via the
set*()
methods, but only for the current
getUrl()
call:
Using temporary options
<?php
require_once 'Services/Libravatar.php';
$sla = new Services_Libravatar();
$sla->setSize(256)
->setDefault('identicon');
$url1Big = $sla->getUrl('foo@example.org');//size 256
$url1Small = $sla->getUrl(
'foo@example.org',
array('size' => 32, 'default' => '404')
);
//size 256 again
$url2Big = $sla->getUrl('bar@example.org');
?>
Services_Mailman provides a PHP API to Mailman, the GNU Mailing List Manager.
With it, you may suscribe and unsubscribe users to/from a mailing list, enumerate all lists and list a list's members.
It utilizes Mailman's web interface via HTTP/HTTPS, so you do not need to interface Python or the mailman binaries.
To use Services_Mailman, you need to include it into your PHP file:
<?php
require_once 'Services/Mailman.php';
?>
Then, create a Services_Mailman object and supply the "Admin Links" URL and the admin password:
<?php
$mm = new Services_Mailman('http://example.org/mailman/admin', '', 'adminpass');
?>
If you already know the mailing list you want to work on, specify it as second constructor parameter:
<?php
$mm = new Services_Mailman(
'http://example.org/mailman/admin',
'foo-users',
'adminpass'
);
?>
If something goes wrong, Services_Mailman's methods will throw exceptions
of type
Services_Mailman_Exception.
Using it's getMessage()
method will give you
a human-readable error message.
List all available mailing lists on the server
<?php
require_once 'Services/Mailman.php';
$mm = new Services_Mailman('http://example.org', '', 'adminpass');
try {
$mailinglists = $mm->lists();
foreach ($mailinglists as $list) {
echo $list['name'] . "\n";
}
} catch (Services_Mailman_Exception $e) {
die('Error: ' . $e->getMessage());
}
?>
Subscribing a user to a mailing list
<?php
require_once 'Services/Mailman.php';
$mm = new Services_Mailman('http://example.org', 'foo-users', 'adminpass');
try {
$mm->subscribe('user@example.org');
} catch (Services_Mailman_Exception $e) {
die('Error: ' . $e->getMessage());
}
?>
Unsubscribing a user from a mailing list
<?php
require_once 'Services/Mailman.php';
$mm = new Services_Mailman('http://example.org', 'foo-users', 'adminpass');
try {
$mm->unsubscribe('user@example.org');
} catch (Services_Mailman_Exception $e) {
die('Error: ' . $e->getMessage());
}
?>
Services_ReCaptcha is a PHP5 interface to the two services offered by recaptcha: reCAPTCHA and reCAPTCHA Mailhide.
Services_ReCaptcha is a PHP5 interface to the two services offered by reCAPTCHA: reCAPTCHA and reCAPTCHA Mailhide.
reCAPTCHA is a freely available CAPTCHA implementation. It distinguishes humans from computers. To use reCAPTCHA, you will need a public/private API key pair, available here: http://recaptcha.net/api/getkey.
reCAPTCHA Mailhide helps you protect your inbox by asking people to solve a reCAPTCHA before they can view your email address. The reCAPTCHA can only be solved by humans, so this stops spammers from gaining access to your email address through automated programs. reCAPTCHA Mailhide also requires a public and a private API key, that can be generated here: http://mailhide.recaptcha.net/apikey.
To install the package with pear just do:
$ pear install Services_ReCaptcha
And to uninstall it:
$ pear uninstall Services_ReCaptcha
To instanciate the Services_ReCaptcha class just do:
<?php
require_once 'Services/ReCaptcha.php';
$recaptcha = new Services_ReCaptcha('your_public_key', 'your_private_key');
?>
You can also pass an array of option as third parameter, or pass options later with the Services_ReCaptcha_Base::setOption() or Services_ReCaptcha_Base::setOptions()
Available options are:
Name | Description | Type | Default value |
---|---|---|---|
secure | Whether to force the ssl url or not | boolean | false |
xhtml | Whether the html should be xhtml compliant or not | boolean | true |
theme | The theme to use for the CAPTCHA | string | red |
lang | The language to use for the CAPTCHA (must be one of the reCATCHA supported languages codes) | string | en |
custom_translations | An array of cutom translations to use | array | null |
custom_theme_widget | The id of the HTML element corresponding to the theme widget | string | null |
tabindex | The HTML tabindex attribute for the reCAPTCHA textarea | string | null |
For more information about these options please consult relevant reCAPTCHA API docs.
<?php
/**
* Include the Services_ReCaptcha class
*/
require_once 'Services/ReCaptcha.php';
// you must get your API keys here:
// http://recaptcha.net/api/getkey
$publicKey = 'your_public_key';
$privateKey = 'your_private_key';
// we instanciate our Services_ReCaptcha instance with the public key and the
// private key
$recaptcha = new Services_ReCaptcha($publicKey, $privateKey);
// if the form was submitted and the catpcha challenge response is ok, we
// display a message and exit
if (isset($_POST['submit']) && $recaptcha->validate()) {
echo "Challenge response ok !";
exit(0);
}
// we display the html form
?>
<html>
<head>
<title>recaptcha test</title>
</head>
<body>
<form method="post" action="">
<?php echo $recaptcha; ?>
<hr/>
<input type="submit" name="submit" value="Ok"/>
</form>
</body>
</html>
<?php
/**
* Include the Services_ReCaptcha class
*/
require_once 'Services/ReCaptcha.php';
// you must get your API keys here:
// http://recaptcha.net/api/getkey
$publicKey = 'your_public_key';
$privateKey = 'your_private_key';
// we instanciate our Services_ReCaptcha instance with the public key and the
// private key
$recaptcha = new Services_ReCaptcha($publicKey, $privateKey);
// we are going to customize our Services_ReCaptcha instance
$recaptcha->setOption('secure', true); // we force the secure url
$recaptcha->setOption('theme', 'white'); // use the white theme
$recaptcha->setOption('lang', 'fr'); // set language to french
// alternatively we could have done:
// $recaptcha = new Services_ReCaptcha($publicKey, $privateKey, array(
// 'secure' => true,
// 'theme' => 'white',
// 'lang' => 'fr'
// ));
// or:
// $recaptcha->setOptions(array('theme' => 'white', 'lang' => 'fr'));
// we use a proxy, so we need to configure it
$recaptcha->getRequest()->setConfig(
array('proxy_host' => 'localhost', 'proxy_port' => 8118)
);
// if the form was submitted
if (isset($_POST['submit'])) {
if ($recaptcha->validate()) {
// the catpcha challenge response is ok, we display a message and exit
echo "Challenge response ok !";
exit(0);
} else {
// if the captcha validation failed, instead of letting the captcha
// display the error, we want to echo the error and exit
echo $recaptcha->getError();
exit(1);
}
}
// we display the html form
?>
<html>
<head>
<title>recaptcha test</title>
</head>
<body>
<form method="post" action="">
<?php echo $recaptcha; ?>
<hr/>
<input type="submit" name="submit" value="Ok"/>
</form>
</body>
</html>
To instanciate the Services_ReCaptcha_MailHide class just do:
<?php
require_once 'Services/ReCaptcha/MailHide.php';
$recaptcha = new Services_ReCaptcha_MailHide('your_public_key', 'your_private_key', 'email_to_hide@example.com');
?>
You can also pass an array of option as third parameter, or pass options later with the Services_ReCaptcha_Base::setOption() or Services_ReCaptcha_Base::setOptions()
Available options are:
Name | Description | Type | Default value |
---|---|---|---|
mask_text | The chars that will be displayed in the email address to hide it | string | ... (three dots) |
link_text | An alternate string for the text of the link | string | null |
link_title | Text to display as the title (tooltip) of the link | string | Reveal this e-mail address |
popup_width | The popup width in pixels | integer | 500 |
popup_height | The popup height in pixels | integer | 300 |
<?php
/**
* Include the Services_ReCaptcha_MailHide class
*/
require_once 'Services/ReCaptcha/MailHide.php';
// you must generate your API keys here:
// http://mailhide.recaptcha.net/apikey
$publicKey = 'your_public_key';
$privateKey = 'your_private_key';
// we instanciate our Services_ReCaptcha_MailHide instance with the public key
// and the private key
$mailhide1 = new Services_ReCaptcha_MailHide(
$publicKey,
$privateKey,
'johndoe@example.com'
);
$mailhide2 = new Services_ReCaptcha_MailHide(
$publicKey,
$privateKey,
'johndoe@example.com',
array('link_text' => 'John Doe')
);
$mailhide3 = new Services_ReCaptcha_MailHide(
$publicKey,
$privateKey,
'johndoe@example.com'
);
$mailhide3->setOptions(
array(
'link_text' => 'Click here to display my email',
'link_title' => 'Some help message',
'link_title' => 'Some help message',
'popup_width' => 800,
'popup_height' => 600,
)
);
?>
<html>
<head>
<title>recaptcha test</title>
</head>
<body>
<h2>Hidden emails can be displayed like this:</h2>
<p><?php echo $mailhide1 ?></p>
<h2>Like this:</h2>
<p><?php echo $mailhide2 ?></p>
<h2>And even like this:</h2>
<p><?php echo $mailhide3 ?></p>
</body>
</html>
A class for interacting with the Technorati API
Technorati is a blog search engine. By indexing blogs and exploring links between them, they provide tools for monitoring online conversations taking place on and between blogs. By far the richest source of this information is through their REST-based API, to which this module provides an OO PHP interface. Full documentation for Technorati's API is available on their developers' wiki. To use the API you will need an API key.
To protect against future changes to the underlying API it is accessed using a factory method:
<?php
$tapi =& Services_Technorati::factory($api_key);
?>
The Technorati API limits you to 500 queries per day so those wishing to use it in a high-demand environment will want to employ some caching. Services_Technorati provides support for any caching system with a Cache_Lite-like API. To use a cache, you will need to create it and then pass it to the class in the factory:
<?php
$tapi =& Services_Technorati::factory($api_key, $cache_object);
?>
Once you have instantiated the object you can start passing in queries. Each call takes the form of methodName(keyParameter, options) where options is an array.
Query Name | Key Parameter | Permitted Options |
---|---|---|
cosmos | url | type, limit, start, current, claim, highlight |
search | query keywords | start, limit, claim |
outbound | url | start |
tag | tag | limit, start, format, excerptsize, topexcerptsize |
topTags | - | limit, start |
blogPostTags | url | limit |
getInfo | username | - |
blogInfo | url | - |
(NB. Support for Technorati's attention. XML services is also included in accordance with the spec, but those services are currently in a state of flux so cannot be relied upon.)
The value returned from each query will be either an array representing the unserialized XML or a PEAR Error.
An example return from the blogInfo query (for 'jystewart') is:
<?php
Array
(
[version] => 1.0
[document] => Array
(
[result] => Array
(
[username] => jystewart
[firstname] => James
[lastname] => Stewart
)
[item] => Array
(
[0] => Array
(
[weblog] => Array
(
[name] => little more than a placeholder
[url] => http://james.anthropiccollective.org
[rssurl] => http://james.anthropiccollective.org/index.rdf
[atomurl] => http://james.anthropiccollective.org/atom.xml
[inboundblogs] => 52
[inboundlinks] => 82
[lastupdate] => 2005-08-27 09:01:42 GMT
[rank] => 7483
[lat] => 0
[lon] => 0
[lang] => 26110
[foafurl] => http://jystewart.net/foaf.rdf
)
)
[1] => Array
(
[weblog] => Array
(
[name] => something approaching a photo album
[url] => http://approach.anthropiccollective.org
[rssurl] =>
[atomurl] =>
[inboundblogs] => 1
[inboundlinks] => 1
[lastupdate] => 2004-08-13 17:40:02 GMT
[rank] => 240760
[lat] => 0
[lon] => 0
[lang] => 0
)
)
[2] => Array
(
[weblog] => Array
(
[name] => grwifi.net : wireless internet (wifi) in grand rapids, michigan
[url] => http://grwifi.net
[rssurl] =>
[atomurl] => http://grwifi.net/atom/
[inboundblogs] => 9
[inboundlinks] => 11
[lastupdate] => 2005-04-15 19:44:46 GMT
[rank] => 50688
[lat] => 0
[lon] => 0
[lang] => 26110
)
)
[3] => Array
(
[weblog] => Array
(
[name] => a work on process
[url] => http://jystewart.net/process
[rssurl] => http://jystewart.net/process/feed/
[atomurl] => http://jystewart.net/process/feed/atom/
[inboundblogs] => 3
[inboundlinks] => 3
[lastupdate] => 2005-08-31 20:44:38 GMT
[rank] => 132735
[lat] => 0
[lon] => 0
[lang] => 26110
[foafurl] => http://jystewart.net/foaf.rdf
)
)
)
)
)
?>
Full details of the XML returned by Technorati is included on the developers' wiki
This package provides a programmable interface to the W3's HTML Validation service at http://validator.w3.org/.
With this package you can use PHP to obtain usable validation results for HTML. The package utilizes the SOAP 1.2 output format of validator to populate validation result objects with true | false as well as any errors and warning messages.
See the examples included with the package for usage.
The package provides an object oriented interface to communicate with W3C's HTML validator by using their XML-based output interface. See the examples page for information on how to use this package to get validation results.
The Services_W3C_HTMLValidator package can be installed using the PEAR installer command pear install Services_W3C_HTMLValidator. If the installation fails due to missing dependencies, one either needs to install them manually or can make the installer fetch all dependencies automatically: pear install -a Services_W3C_HTMLValidator.
The following examples demonstrate how to get validation results from an instance of the W3C HTML Validator.
Validate by URI
This is a simple example which shows how to validate a URI and return whether the page is valid or not.
<?php
require_once 'Services/W3C/HTMLValidator.php';
$v = new Services_W3C_HTMLValidator();
$u = 'http://www.unl.edu/';
$r = $v->validate($u);
if ($r->isValid()) {
echo $u.' is valid!';
} else {
echo $u.' is NOT valid!';
}
?>
This class acts as an interface to various online weather-services.
Services_Weather searches for given locations and retrieves current weather data and, dependant on the used service, also forecasts. Up to now, SOAP services from CapeScience and EJSE, XML from weather.com and METAR from noaa.gov are supported, further services will get included, if they become available and are properly documented.
Services_Weather
string
Services_Weather::apiVersion
(
)
This package is not documented yet.
No exceptions thrown.
This function can not be called statically.
bool
Services_Weather::isError
(
PEAR_Error|mixed
$value
)
This package is not documented yet.
$value
No exceptions thrown.
This function can not be called statically.
PEAR_Error|object&
Services_Weather::service
(
string
$service
,
array
$options
= null
)
Usable keys for the options array are:
debug enables debugging output
--- Common Options
cacheType defines what type of cache to use
cacheOptions passes cache options
unitsFormat use (US)-standard, metric or custom units
customUnitsFormat defines the customized units format
httpTimeout sets timeout for HTTP requests
dateFormat string to use for date output
timeFormat string to use for time output
--- EJSE Options
none
--- GlobalWeather Options
none
--- METAR Options
dsn String for defining the DB connection
dbOptions passes DB options
source http, ftp or file - type of data-source
sourcePath where to look for the source, URI or filepath
--- weather.com Options
partnerID You'll receive these keys after registering
licenseKey with the weather.com XML-service
$service
$options
throws PEAR_Error
throws PEAR_Error::SERVICES_WEATHER_ERROR_SERVICE_NOT_FOUND
This function can not be called statically.
Parent class for weather-services. Defines common functions for unit conversions, checks for cache enabling and does other miscellaneous things.
Services_Weather_Common
Class | Summary |
---|---|
Services_Weather_Ejse | PEAR::Services_Weather_Ejse |
Services_Weather_Globalweather | PEAR::Services_Weather_Globalweather |
Services_Weather_Metar | PEAR::Services_Weather_Metar |
Services_Weather_Weatherdotcom | PEAR::Services_Weather_Weatherdotcom |
float
Services_Weather_Common::calculateDewPoint
(
float
$temperature
,
float
$humidity
)
Temperature has to be entered in deg C!
$temperature
$humidity
No exceptions thrown.
This function can not be called statically.
float
Services_Weather_Common::calculateHumidity
(
float
$temperature
,
float
$dewPoint
)
Temperature and dewpoint have to be entered in deg C!
$temperature
$dewPoint
No exceptions thrown.
This function can not be called statically.
float
Services_Weather_Common::calculateWindChill
(
float
$temperature
,
float
$speed
)
Temperature has to be entered in deg F, speed in mph!
$temperature
$speed
No exceptions thrown.
This function can not be called statically.
float
Services_Weather_Common::convertDistance
(
float
$distance
,
string
$from
,
string
$to
)
This package is not documented yet.
$distance
$from
$to
No exceptions thrown.
This function can not be called statically.
float
Services_Weather_Common::convertPressure
(
float
$pressure
,
string
$from
,
string
$to
)
This package is not documented yet.
$pressure
$from
$to
No exceptions thrown.
This function can not be called statically.
float
Services_Weather_Common::convertSpeed
(
float
$speed
,
string
$from
,
string
$to
)
This package is not documented yet.
$speed
$from
$to
No exceptions thrown.
This function can not be called statically.
float
Services_Weather_Common::convertTemperature
(
float
$temperature
,
string
$from
,
string
$to
)
This package is not documented yet.
$temperature
$from
$to
No exceptions thrown.
This function can not be called statically.
array
Services_Weather_Common::getUnitsFormat
(
string
$unitsFormat = ""
)
This package is not documented yet.
$unitsFormat
No exceptions thrown.
This function can not be called statically.
array
Services_Weather_Common::polar2cartesian
(
float
$latitude
,
float
$longitude
)
This package is not documented yet.
$latitude
$longitude
No exceptions thrown.
This function can not be called statically.
PEAR_Error|bool
Services_Weather_Common::setCache
(
string
$cacheType = "file"
,
array
$cacheOptions = array()
)
Requires Cache to be installed
$cacheType
$cacheOptions
throws PEAR_Error::SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED
This function can not be called statically.
void
Services_Weather_Common::setDateTimeFormat
(
string
$dateFormat = ""
,
string
$timeFormat = ""
)
This package is not documented yet.
$dateFormat
$timeFormat
No exceptions thrown.
This function can not be called statically.
void
Services_Weather_Common::setHttpTimeout
(
int
$httpTimeout
)
This package is not documented yet.
$httpTimeout
No exceptions thrown.
This function can not be called statically.
void
Services_Weather_Common::setUnitsFormat
(
string
$unitsFormat
,
array
$customUnitsFormat = array()
)
This package is not documented yet.
$unitsFormat
$customUnitsFormat
No exceptions thrown.
This function can not be called statically.
This class acts as an interface to the soap service of EJSE. It retrieves current weather data and forecasts based on postal codes (ZIP).
Currently this service is only available for US territory.
For a working example, please take a look at docs/Services_Weather/examples/ejse-basic.php
Services_Weather_Ejse
Services_Weather_Ejse Inherited Methods
Method Name | Summary |
---|---|
Services_Weather_Common::calculateDewPoint() | Calculate dewpoint from temperature and humidity. This is only an approximation, there is no exact formula, this one here is called Magnus-Formula |
Services_Weather_Common::calculateHumidity() | Calculate humidity from temperature and dewpoint. This is only an approximation, there is no exact formula, this one here is called Magnus-Formula |
Services_Weather_Common::calculateWindChill() | Calculate windchill from temperature and windspeed (enhanced formula) |
Services_Weather_Common::convertDistance() | Convert distance between km, ft and sm |
Services_Weather_Common::convertPressure() | Convert pressure between in, hpa, mb, mm and atm |
Services_Weather_Common::convertSpeed() | Convert speed between mph, kmh, kt, mps and fps |
Services_Weather_Common::convertTemperature() | Convert temperature between f and c |
Services_Weather_Common::getUnitsFormat() | Returns the selected units format |
Services_Weather_Common::polar2cartesian() | Convert polar coordinates to cartesian coordinates |
Services_Weather_Common::setCache() | Enables caching the data, usage strongly recommended |
Services_Weather_Common::setDateTimeFormat() | Changes the representation of time and dates (see http://www.php.net/date) |
Services_Weather_Common::setHttpTimeout() | Sets the timeout in seconds for HTTP requests |
Services_Weather_Common::setUnitsFormat() | Changes the representation of the units (standard/metric) |
PEAR_Error|array
Services_Weather_Ejse::getForecast
(
mixed
$id = ""
,
int
$days = 2
,
string
$unitsFormat = ""
,
string
$int
)
This package is not documented yet.
$id
$days
Values between 1 and 9
$unitsFormat
$int
throws PEAR_Error
This function can not be called statically.
PEAR_Error|array
Services_Weather_Ejse::getLocation
(
string
$id = ""
)
This package is not documented yet.
$id
throws PEAR_Error
This function can not be called statically.
array
Services_Weather_Ejse::getUnits
(
string
$id
= null
,
string
$unitsFormat = ""
)
This package is not documented yet.
$id
$unitsFormat
No exceptions thrown.
deprecated
This function can not be called statically.
PEAR_Error|array
Services_Weather_Ejse::getWeather
(
string
$id = ""
,
string
$unitsFormat = ""
)
This package is not documented yet.
$id
$unitsFormat
throws PEAR_Error
This function can not be called statically.
bool
Services_Weather_Ejse::searchLocation
(
string
$location
= null
,
bool
$useFirst
= null
)
Maybe this is the place to interface to some online postcode service...
$location
$useFirst
No exceptions thrown.
deprecated
This function can not be called statically.
bool
Services_Weather_Ejse::searchLocationByCountry
(
string
$country
= null
)
Maybe this is the place to interface to some online postcode service...
$country
No exceptions thrown.
deprecated
This function can not be called statically.
This class acts as an interface to the soap service of capescience.com. It searches for given locations and retrieves current weather data.
GlobalWeather is a SOAP frontend for METAR data, provided by CapeScience. If you want to use METAR, you should try this class first, as it is much more comfortable (and also a bit faster) than the native METAR-class provided by this package.
For a working example, please take a look at docs/Services_Weather/examples/globalweather-basic.php
Services_Weather_Globalweather
Services_Weather_Globalweather Inherited Methods
Method Name | Summary |
---|---|
Services_Weather_Common::calculateDewPoint() | Calculate dewpoint from temperature and humidity This is only an approximation, there is no exact formula, this one here is called Magnus-Formula |
Services_Weather_Common::calculateHumidity() | Calculate humidity from temperature and dewpoint This is only an approximation, there is no exact formula, this one here is called Magnus-Formula |
Services_Weather_Common::calculateWindChill() | Calculate windchill from temperature and windspeed (enhanced formula) |
Services_Weather_Common::convertDistance() | Convert distance between km, ft and sm |
Services_Weather_Common::convertPressure() | Convert pressure between in, hpa, mb, mm and atm |
Services_Weather_Common::convertSpeed() | Convert speed between mph, kmh, kt, mps and fps |
Services_Weather_Common::convertTemperature() | Convert temperature between f and c |
Services_Weather_Common::getUnitsFormat() | Returns the selected units format |
Services_Weather_Common::polar2cartesian() | Convert polar coordinates to cartesian coordinates |
Services_Weather_Common::setCache() | Enables caching the data, usage strongly recommended |
Services_Weather_Common::setDateTimeFormat() | Changes the representation of time and dates (see http://www.php.net/date) |
Services_Weather_Common::setHttpTimeout() | Sets the timeout in seconds for HTTP requests |
Services_Weather_Common::setUnitsFormat() | Changes the representation of the units (standard/metric) |
bool
Services_Weather_Globalweather::getForecast
(
mixed
$id
= null
,
int
$days
= null
,
string
$unitsFormat
= null
,
string
$int
)
This package is not documented yet.
$id
$days
$unitsFormat
$int
No exceptions thrown.
deprecated
This function can not be called statically.
PEAR_Error|array
Services_Weather_Globalweather::getLocation
(
string
$id = ""
)
This package is not documented yet.
$id
throws PEAR_Error
This function can not be called statically.
array
Services_Weather_Globalweather::getUnits
(
string
$id
= null
,
string
$unitsFormat = ""
)
This package is not documented yet.
$id
$unitsFormat
No exceptions thrown.
deprecated
This function can not be called statically.
PEAR_Error|array
Services_Weather_Globalweather::getWeather
(
string
$id = ""
,
string
$unitsFormat = ""
)
This package is not documented yet.
$id
$unitsFormat
throws PEAR_Error
This function can not be called statically.
PEAR_Error|array|string
Services_Weather_Globalweather::searchLocation
(
string
$location
,
bool
$useFirst
= false
)
This package is not documented yet.
$location
$useFirst
If set, first ID of result-array is returned
throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION
This function can not be called statically.
PEAR_Error|array
Services_Weather_Globalweather::searchLocationByCountry
(
string
$country = ""
)
This package is not documented yet.
$country
throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION
This function can not be called statically.
This class acts as an interface to the metar service of weather.noaa.gov. It searches for locations given in ICAO notation and retrieves the current weather data.
Of course the parsing of the METAR-data has its limitations, as it follows the Federal Meteorological Handbook No.1 with modifications to accommodate for non-US reports, so if the report deviates from these standards, you won't get it parsed correctly. Anything that is not parsed, is saved in the "noparse" array-entry, returned by getWeather(), so you can do your own parsing afterwards. This limitation is specifically given for remarks, as the class is not processing everything mentioned there, but you will get the most common fields like precipitation and temperature-changes. Again, everything not parsed, goes into "noparse".
If you think, some important field is missing or not correctly parsed, please file a feature-request/bugreport at http://pear.php.net/ and be sure to provide the METAR report with a _detailed_ explanation!
For a working example, please take a look at docs/Services_Weather/examples/metar-basic.php
Services_Weather_Metar
Services_Weather_Metar Inherited Methods
Method Name | Summary |
---|---|
Services_Weather_Common::calculateDewPoint() | Calculate dewpoint from temperature and humidity This is only an approximation, there is no exact formula, this one here is called Magnus-Formula |
Services_Weather_Common::calculateHumidity() | Calculate humidity from temperature and dewpoint This is only an approximation, there is no exact formula, this one here is called Magnus-Formula |
Services_Weather_Common::calculateWindChill() | Calculate windchill from temperature and windspeed (enhanced formula) |
Services_Weather_Common::convertDistance() | Convert distance between km, ft and sm |
Services_Weather_Common::convertPressure() | Convert pressure between in, hpa, mb, mm and atm |
Services_Weather_Common::convertSpeed() | Convert speed between mph, kmh, kt, mps and fps |
Services_Weather_Common::convertTemperature() | Convert temperature between f and c |
Services_Weather_Common::getUnitsFormat() | Returns the selected units format |
Services_Weather_Common::polar2cartesian() | Convert polar coordinates to cartesian coordinates |
Services_Weather_Common::setCache() | Enables caching the data, usage strongly recommended |
Services_Weather_Common::setDateTimeFormat() | Changes the representation of time and dates (see http://www.php.net/date) |
Services_Weather_Common::setHttpTimeout() | Sets the timeout in seconds for HTTP requests |
Services_Weather_Common::setUnitsFormat() | Changes the representation of the units (standard/metric) |
bool
Services_Weather_Metar::getForecast
(
mixed
$id
= null
,
int
$days
= null
,
string
$unitsFormat
= null
,
string
$int
)
This package is not documented yet.
$id
$days
$unitsFormat
$int
No exceptions thrown.
deprecated
This function can not be called statically.
PEAR_Error|array
Services_Weather_Metar::getLocation
(
string
$id = ""
)
This package is not documented yet.
$id
throws PEAR_Error
This function can not be called statically.
array
Services_Weather_Metar::getUnits
(
string
$id
= null
,
string
$unitsFormat = ""
)
This package is not documented yet.
$id
$unitsFormat
No exceptions thrown.
deprecated
This function can not be called statically.
PHP_Error|array
Services_Weather_Metar::getWeather
(
string
$id = ""
,
string
$unitsFormat = ""
)
This package is not documented yet.
$id
$unitsFormat
throws PHP_Error
This function can not be called statically.
PEAR_Error|array|string
Services_Weather_Metar::searchAirport
(
float
$latitude
,
float
$longitude
,
int
$numResults = 1
)
This package is not documented yet.
$latitude
$longitude
$numResults
throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION
throws PEAR_Error::SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED
throws PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION
This function can not be called statically.
PEAR_Error|array|string
Services_Weather_Metar::searchLocation
(
string|array
$location
,
bool
$useFirst
= false
)
This package is not documented yet.
$location
$useFirst
If set, first ID of result-array is returned
throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION
throws PEAR_Error::SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED
throws PEAR_Error::SERVICES_WEATHER_ERROR_INVALID_LOCATION
This function can not be called statically.
PEAR_Error|array
Services_Weather_Metar::searchLocationByCountry
(
string
$country = ""
)
This package is not documented yet.
$country
throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION
throws PEAR_Error::SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED
throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
This function can not be called statically.
DB_Error|bool
Services_Weather_Metar::setMetarDB
(
string
$dsn
,
array
$dbOptions = array()
)
This package is not documented yet.
$dsn
$dbOptions
see DB::parseDSN
throws DB_Error
This function can not be called statically.
void
Services_Weather_Metar::setMetarSource
(
string
$source
,
string
$sourcePath = ""
)
Source can be http, ftp or file. An alternate sourcepath can be provided.
$source
$sourcePath
No exceptions thrown.
This function can not be called statically.
This class acts as an interface to the xml service of weather.com. It searches for given locations and retrieves current weather data as well as forecast for up to 10 days.
For using the weather.com xml-service please visit http://www.weather.com/services/xmloap.html and follow the link to sign up, it's free! You will receive an email where to download the SDK with the needed images and guidelines how to publish live data from weather.com. Unfortunately the guidelines are a bit harsh, that's why there's no actual data-representation in this class, just the raw data. Also weather.com demands active caching, so I'd strongly recommend enabling the caching implemented in this class. It obeys to the times as written down in the guidelines.
For a working example, please take a look at docs/Services_Weather/examples/weather.com-basic.php
Services_Weather_Weatherdotcom
Services_Weather_Weatherdotcom Inherited Methods
Method Name | Summary |
---|---|
Services_Weather_Common::calculateDewPoint() | Calculate dewpoint from temperature and humidity This is only an approximation, there is no exact formula, this one here is called Magnus-Formula |
Services_Weather_Common::calculateHumidity() | Calculate humidity from temperature and dewpoint This is only an approximation, there is no exact formula, this one here is called Magnus-Formula |
Services_Weather_Common::calculateWindChill() | Calculate windchill from temperature and windspeed (enhanced formula) |
Services_Weather_Common::convertDistance() | Convert distance between km, ft and sm |
Services_Weather_Common::convertPressure() | Convert pressure between in, hpa, mb, mm and atm |
Services_Weather_Common::convertSpeed() | Convert speed between mph, kmh, kt, mps and fps |
Services_Weather_Common::convertTemperature() | Convert temperature between f and c |
Services_Weather_Common::getUnitsFormat() | Returns the selected units format |
Services_Weather_Common::polar2cartesian() | Convert polar coordinates to cartesian coordinates |
Services_Weather_Common::setCache() | Enables caching the data, usage strongly recommended |
Services_Weather_Common::setDateTimeFormat() | Changes the representation of time and dates (see http://www.php.net/date) |
Services_Weather_Common::setHttpTimeout() | Sets the timeout in seconds for HTTP requests |
Services_Weather_Common::setUnitsFormat() | Changes the representation of the units (standard/metric) |
PEAR_Error|array
Services_Weather_Weatherdotcom::getForecast
(
string
$id = ""
,
int
$days = 2
,
string
$unitsFormat = ""
)
This package is not documented yet.
$id
$days
Values between 1 and 10
$unitsFormat
throws PEAR_Error
This function can not be called statically.
PEAR_Error|array
Services_Weather_Weatherdotcom::getLocation
(
string
$id = ""
)
This package is not documented yet.
$id
throws PEAR_Error
This function can not be called statically.
array
Services_Weather_Weatherdotcom::getUnits
(
string
$id
= null
,
string
$unitsFormat = ""
)
This package is not documented yet.
$id
$unitsFormat
No exceptions thrown.
deprecated
This function can not be called statically.
PEAR_Error|array
Services_Weather_Weatherdotcom::getWeather
(
string
$id = ""
,
string
$unitsFormat = ""
)
This package is not documented yet.
$id
$unitsFormat
throws PEAR_Error
This function can not be called statically.
PEAR_Error|array|string
Services_Weather_Weatherdotcom::searchLocation
(
string
$location
,
bool
$useFirst
= false
)
This package is not documented yet.
$location
$useFirst
If set, first ID of result-array is returned
throws PEAR_Error::SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA
throws PEAR_Error::SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION
This function can not be called statically.
bool
Services_Weather_Weatherdotcom::searchLocationByCountry
(
string
$country = ""
)
This package is not documented yet.
$country
No exceptions thrown.
deprecated
This function can not be called statically.
void
Services_Weather_Weatherdotcom::setAccountData
(
string
$partnerID
,
string
$licenseKey
)
This package is not documented yet.
$partnerID
$licenseKey
No exceptions thrown.
This function can not be called statically.
Name | Value | Line Number |
---|---|---|
SERVICES_WEATHER_ERROR_CACHE_INIT_FAILED | 13 | 41 |
SERVICES_WEATHER_ERROR_DB_NOT_CONNECTED | 14 | 42 |
SERVICES_WEATHER_ERROR_INVALID_LICENSE_KEY | 102 | 51 |
SERVICES_WEATHER_ERROR_INVALID_LOCATION | 2 | 48 |
SERVICES_WEATHER_ERROR_INVALID_PARTNER_ID | 100 | 49 |
SERVICES_WEATHER_ERROR_INVALID_PRODUCT_CODE | 101 | 50 |
SERVICES_WEATHER_ERROR_NO_LOCATION | 1 | 47 |
SERVICES_WEATHER_ERROR_SERVICE_NOT_FOUND | 10 | 38 |
SERVICES_WEATHER_ERROR_UNKNOWN_ERROR | 46 | |
SERVICES_WEATHER_ERROR_UNKNOWN_LOCATION | 11 | 39 |
SERVICES_WEATHER_ERROR_WRONG_SERVER_DATA | 12 | 40 |
SERVICES_WEATHER_EXPIRES_FORECAST | 7200 | 33 |
SERVICES_WEATHER_EXPIRES_LINKS | 43200 | 34 |
SERVICES_WEATHER_EXPIRES_LOCATION | 900 | 31 |
SERVICES_WEATHER_EXPIRES_UNITS | 900 | 30 |
SERVICES_WEATHER_EXPIRES_WEATHER | 1800 | 32 |
Name | Value | Line Number |
---|---|---|
SERVICES_WEATHER_RADIUS_EARTH | 6378.15 | 32 |
Interface to the Yahoo! Search API.
The package provides an object oriented model to communicate with Yahoo's services via their REST-based XML interface.
The package provides an object oriented model to communicate with Yahoo's web services via their REST-based XML interface.
Currently the package only supports the Yahoo! Search Web Services, which is natural because Yahoo! does not expose other services via their API at the moment. Due to the modular layout of the package it is easy to add support for other services at a later point though. More information about the design of the package is available in a separate section.
Services_Yahoo requires PHP 5 and a bunch of PEAR packages. For a complete list of dependencies please see the download page.
The Services_Yahoo package can be installed using the PEAR installer command pear install Services_Yahoo. If the installation fails due to missing dependencies, one either needs to install them manually or can make the installer fetch all dependencies automatically: pear install -a Services_Yahoo.
Alternative installation methods for situations where one has no access to the pear installer command can be found in the general installation chapter.
Uninstalling the package can be done with pear uninstall Services_Yahoo.
The following sections provide you with examples of using the different feature sets of Services_Yahoo. Currently this includes documentation for the interfaces to Yahoo! Search and Content Analysis.
All public methods in Services_Yahoo have in
common that exceptions
will be raised when something goes wrong. This is why try {
... } catch { }
blocks are wrapped around all examples.
The examples are designed to be run from a command line shell. If you would like to test them in a web browser you should replace
\n
with<br />
for better readability.
The following examples will communicate with Yahoo! Search.
Web search: Listing results
This snippet issues a search query for the term Steve
Fossett
to Yahoo! Search. For each result in the returned
result set the title is printed.
<?php
require_once "Services/Yahoo/Search.php";
try {
$client = Services_Yahoo_Search::factory("web");
$results = $client->searchFor("Steve Fossett");
echo "Number of results: " . $results->getTotalResultsReturned() . "\n\n";
foreach ($results as $result) {
echo $result['Title'] . "\n";
}
} catch (Services_Yahoo_Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
foreach ($e->getErrors() as $error) {
echo "* " . $error . "\n";
}
}
?>
By default 10 results are returned per request. This number can be modified using the method setResultNumber():
<?php
require_once "Services/Yahoo/Search.php";
try {
$client = Services_Yahoo_Search::factory("web");
// Get 20 results per query
$results = $client->withResults(20)->searchFor("Steve Fossett");
/* ... */
?>
Web search: Result details
This code again queries Yahoo! Search for Steve
Fossett
, but this time the details of the first result in
the result set are printed.
<?php
require_once "Services/Yahoo/Search.php";
try {
$client = Services_Yahoo_Search::factory("web");
$results = $client->searchFor("Steve Fossett");
if ($results->getTotalResultsReturned() > 0) {
$info = $results->current();
echo "Title: " . $info['Title'] . "\n";
echo "Summary: " . $info['Summary'] . "\n";
echo "URL: " . $info['Url'] . "\n";
echo "clickable URL: " . $info['ClickUrl'] . "\n";
echo "Modification date: " . $info['ModificationDate'] . "\n";
echo "Mime type: " . $info['MimeType'] . "\n";
}
} catch (Services_Yahoo_Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
foreach ($e->getErrors() as $error) {
echo "* " . $error . "\n";
}
}
?>
Web search: Paginating results
In this example the "paginating" capabilities of Services_Yahoo are shown. Paginating means that the search results are split up into chunks of e.g. 20, which are displayed together with links to jump back and forth to other result chunks. Basically this mimicks the functionality from the bottom of official Yahoo Search Page.
TBD
In order to query the Image, News, Video or Local search, one only needs to replace the argument "web" in the call of the factory() method with one of "image", "news", "video", or "local".
The following examples will show you how to use the Content Analysis Services provided by Yahoo!.
Term Extraction Service
The Term Extraction service provides a list of significant words or phrases extracted from a larger content.
<?php
require_once "Services/Yahoo/ContentAnalysis.php";
try {
$search = Services_Yahoo_ContentAnalysis::factory("termExtraction");
$search->setContext("Italian sculptors and painters of the "
. "renaissance favored the Virgin Mary for inspiration.");
$search->setQuery("madonna");
$results = $search->submit();
foreach ($results as $result) {
echo $result . "\n";
}
} catch (Services_Yahoo_Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
foreach ($e->getErrors() as $error) {
echo "* " . $error . "\n";
}
}
?>
It is possible to skip the call to setQuery(). The parameter set by this method is intended to help the engine with the extraction process, but it is not stricly required.
Spelling Suggestion Service
The Spelling Suggestion service provides a suggested spelling correction for a given term.
The following code queries Yahoo for a spelling suggestion for the
term "madnna". The service will return exactly one
result, but currently there is no way to avoid looping through the
$results
.
<?php
require_once "Services/Yahoo/ContentAnalysis.php";
try {
$search = Services_Yahoo_ContentAnalysis::factory("spellingSuggestion");
$search->setQuery("madnna");
$results = $search->submit();
foreach ($results as $result) {
echo $result . "\n";
}
} catch (Services_Yahoo_Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
foreach ($e->getErrors() as $error) {
echo "* " . $error . "\n";
}
}
?>
Services_Yahoo is split up into chunks that group similar functionality. Currently there are chunks for the interfaces to Yahoo Search and to the Content Analysis functions.
Each chunk basically contains two top-level element in the source
tree: A .php
file and a directory that holds
the implementation of the chunk. The file is a class that contains
only one method which implements that factory
pattern. This method dynamically loads the right file from
the related directory and returns an instance of the class that
implements the desired function.
Due to this modular layout Services_Yahoo can easily be extended when Yahoo adds new functions to their API.
If you would like to study the code of the package, you can do so by either downloading a release from the package homepage or by browsing the code in SVN.
Also the automatically generated API documentation should help you.
Object-oriented abstraction for YouTube API.
Services_YouTube is an abstraction for the webservice of YouTube APIs. Using YouTube APIs, you can easily integrate online videos from YouTube's rapidly growing repository of videos into your application. To use YouTube API and this package, create a YouTube account and create a YouTube developer profile.
YouTube APIs currently allow read-only access to key parts of the YouTube video respository and user community. Services_YouTube provides functions for YouTube APIs using O-O abstraction access.
The following examples show how to use some basic features of Services_YouTube:
Retrieves the public parts of a user profile.
<?php
require_once 'Services/YouTube.php';
$dev_id = "YOUR_DEV_ID";
$user_id = "USER_ID";
$youtube = new Services_YouTube($dev_id, array('useCache' => true));
$user = $youtube->getProfile($user_id);
$profile = $user->user_profile;
print "{$profile->first_name} {$profile->last_name} :({$profile->video_watch_count} Watched.)\n";
?>
Lists a user's favorite videos.
<?php
require_once 'Services/YouTube.php';
$dev_id = "YOUR_DEV_ID";
$user_id = "USER_ID";
$youtube = new Services_YouTube($dev_id, array('useCache' => true));
$videos = $youtube->listFavoriteVideos($user_id);
foreach ($videos->xpath('//video') as $i => $video) {
print "<img src='{$video->thumbnail_url}' alt='{$video->title}' />\n";
print "<a href='{$video->url}'>URL</a><br />\n";
}
?>
Lists a user's friends.
<?php
require_once 'Services/YouTube.php';
$dev_id = "YOUR_DEV_ID";
$user_id = "USER_ID";
$youtube = new Services_YouTube($dev_id, array('useCache' => true));
$users = $youtube->listFriends($user_id);
foreach ($users->xpath('//friend') as $i => $friend) {
print "{$friend->user} : Upload: {$friend->video_upload_count}";
}
?>
Lists all videos that have the specified tag.
<?php
require_once 'Services/YouTube.php';
$dev_id = "YOUR_DEV_ID";
$tag = "test";
$youtube = new Services_YouTube($dev_id, array('useCache' => true));
$videos = $youtube->listByTag($tag);
foreach ($videos->xpath('//video') as $i => $video) {
print "<img src='{$video->thumbnail_url}' alt='{$video->title}' />\n";
print "<a href='{$video->url}'>URL</a><br />\n";
}
?>
Lists all videos that were uploaded by the specified user.
<?php
require_once 'Services/YouTube.php';
$dev_id = "YOUR_DEV_ID";
$user_id = "USER_ID";
$youtube = new Services_YouTube($dev_id, array('useCache' => true));
$videos = $youtube->listByUser($user_id);
foreach ($videos->xpath('//video') as $i => $video) {
print "<img src='{$video->thumbnail_url}' alt='{$video->title}' />\n";
print "<a href='{$video->url}'>URL</a><br />\n";
}
?>
Lists the most recent 25 videos that have been featured on the front page of the YouTube site.
<?php
require_once 'Services/YouTube.php';
$dev_id = "YOUR_DEV_ID";
$youtube = new Services_YouTube($dev_id, array('useCache' => true));
$videos = $youtube->listFeatured();
foreach ($videos->xpath('//video') as $i => $video) {
print "<img src='{$video->thumbnail_url}' alt='{$video->title}' />\n";
print "<a href='{$video->url}'>URL</a><br />\n";
}
?>
Displays the details for a video.
<?php
require_once 'Services/YouTube.php';
$dev_id = "YOUR_DEV_ID";
$video_id = "VIDEO_ID";
$youtube = new Services_YouTube($dev_id, array('useCache' => true));
$video = $youtube->getDetails($video_id);
$details = $video->video_details;
print "{$details->title} : ({$details->tags}) : RATE: {$details->rating_avg} in {$detials->rating_count}";
print "<img src='{$details->thumbnail_url}' alt={$details->title}><hr /><hr />";
foreach ($details->xpath('//comment') as $i => $comment) {
print "{$comment->author} : {$comment->text}<hr />";
}
?>
UDDI implementation for PHP
Implementation of the Universal Description, Discovery and Integration API for locating and publishing Web Services listings in a UBR (UDDI Business Registry)
PHP implementation of the XML-RPC protocol.
This is a PEAR-ified version of Useful inc's XML-RPC for PHP. It has support for HTTP transport, proxies and authentication.
XML-RPC is a format devised by Userland Software for achieving remote procedure call via XML. XML-RPC has its own web site, www.XmlRpc.com.
The most common implementations of XML-RPC available at the moment use HTTP as the transport. A list of implementations for other languages such as Perl and Python can be found on the www.xmlrpc.com.
This collection of PHP classes provides a framework for writing XML-RPC clients and servers in PHP.
This package has been orginially written by Edd Dumbill of Useful Information Company, so he deservers most of the credits. Apart from that following people have contributed to the initial codebase that is still being maintained by Edd:
Jim Winstead <jimw at php dot net>
Peter Kocks <peter.kocks at baygate dot com>
Nicolay Mausz <mausz at flying-dog dot com>
Ben Margolin
<ben at wendy.auctionwatch dot com>
Dan Libby <dan at libby dot com>
Gaetano Giunta <g.giunta at libero dot it>
Idan Sofer <i_sofer at yahoo dot com>
Giancarlo Pinerolo <ping at alt dot it>
Justin Miller <justin at voxel dot net>
the XML-RPC server class. include() this to get server functionality.
the XML-RPC client classes. include() this to get client functionality.
The package is offered "as-is" without any warranty or commitment to support. However, informal advice and help is available via the PEAR user mailing list and XML-RPC.com.
The PEAR user mailing list can be contacted for questions concerning the usage of the package. More details can be found here.
For more general XML-RPC questions, there is a Yahoo! Groups XML-RPC mailing list.
The XML-RPC.com discussion group is a useful place to get help with using XML-RPC. This group is also gatewayed into the Yahoo! Groups mailing list.
Together with Simon St.Laurent and Joe Johnston, Edd Dumbill wrote a book on XML-RPC for O'Reilly and Associates on XML-RPC. It features a rather fetching jellyfish on the cover.
Complete details of the book are available from O'Reilly's web site.
If you've benefitted from the effort he has put into writing this software, then please consider buying the book!
<?php
require_once 'XML/RPC.php';
/*
* Get info about the most recently released PEAR package
*/
$params = array(new XML_RPC_Value(1, 'int'));
$msg = new XML_RPC_Message('release.getRecent', $params);
$cli = new XML_RPC_Client('/xmlrpc.php', 'pear.php.net');
// If you want to turn debugging on...
// $cli->setDebug(1);
// If your payload requires extra lines to stay in tact...
// NOTE: The $remove_extra_lines property was added in Version 1.4.6.
// $cli->remove_extra_lines = false;
// If inspect the XML request sent to the server...
// $msg->createPayload();
// logit($msg->payload); // Hypothetical function.
$resp = $cli->send($msg);
if (!$resp) {
echo 'Communication error: ' . $cli->errstr;
exit;
}
if (!$resp->faultCode()) {
$val = $resp->value();
$data = XML_RPC_decode($val);
echo $data[0]['name'] . ' is at version ' . $data[0]['version'];
} else {
/*
* Display problems that have been gracefully cought and
* reported by the xmlrpc.php script
*/
echo 'Fault Code: ' . $resp->faultCode() . "\n";
echo 'Fault Reason: ' . $resp->faultString() . "\n";
}
// To inspect the XML response from the server...
// NOTE: The $response_payload property was added in Version 1.4.6.
// logit($msg->response_payload); // Hypothetical function.
?>
Here is the server script. It's named xmlrpc.php and located in the document root of the web server at localhost:
<?php
require_once 'XML/RPC/Server.php';
/*
* Declare the functions, etc.
*/
function returnTimes2($params) {
$obj = new some_class_name;
return $obj->returnTimes2($params);
}
class some_class_name {
function returnTimes2($params) {
$param = $params->getParam(0);
// This error checking syntax was added in Release 1.3.0
if (!XML_RPC_Value::isValue($param)) {
return $param;
}
$val = new XML_RPC_Value($param->scalarval() * 2, 'int');
return new XML_RPC_Response($val);
}
}
$some_object = new some_class_name;
/*
* Establish the dispatch map and XML_RPC server instance.
*/
$server = new XML_RPC_Server(
array(
'function_times2' => array(
'function' => 'returnTimes2'
),
'class_paamayim_nekudotayim_times2' => array(
'function' => 'some_class_name::returnTimes2'
),
'class_times2' => array(
'function' => array('some_class_name', 'returnTimes2')
),
'object_times2' => array(
'function' => array($some_object, 'returnTimes2')
),
),
1 // serviceNow
);
?>
And here is the client script:
<?php
require_once 'XML/RPC.php';
$input = 8;
$params = array(new XML_RPC_Value($input, 'int'));
$msg = new XML_RPC_Message('function_times2', $params);
$cli = new XML_RPC_Client('/xmlrpc.php', 'localhost');
// $cli->setDebug(1);
$resp = $cli->send($msg);
if (!$resp) {
echo 'Communication error: ' . $cli->errstr;
exit;
}
if (!$resp->faultCode()) {
$val = $resp->value();
echo $input . ' times 2 is ' . $val->scalarval();
} else {
/*
* Display problems that have been gracefully cought and
* reported by the xmlrpc.php script.
*/
echo 'Fault Code: ' . $resp->faultCode() . "\n";
echo 'Fault Reason: ' . $resp->faultString() . "\n";
}
?>
The XML_RPC_encode() function automatically converts PHP data into the format needed by the XML_RPC library.
<?php
require_once 'XML/RPC.php';
$data = fetch_row_from_db(); // Hypothetical example.
$params = array(XML_RPC_encode($data));
$msg = new XML_RPC_Message('some_function_name', $params);
$cli = new XML_RPC_Client('/xmlrpc.php', 'pear.php.net');
$resp = $cli->send($msg);
// Process the same way as the other examples...
?>
This is the basic class used to represent a client of an XML-RPC server.
The constructor has the following syntax:
$client = new XML_RPC_Client (
string $path
,
string $server
,
integer $port
,
string $proxy
,
integer $proxy_port
,
string $proxy_user
,
string $proxy_pass
)
$path
the path and name of the RPC server script you want the request to go to
$server
the URL of the remote server to connect to. If this parameter doesn't specify a protocol and $port is 443, ssl:// is assumed.
$port
a port for connecting to the remote server. Defaults to 80 for http:// connections and 443 for https:// and ssl:// connections.
$proxy
the URL of the proxy server to use, if any. If this parameter doesn't specify a protocol and $port is 443, ssl:// is assumed.
$proxy_port
a port for connecting to the remote server. Defaults to 8080 for http:// connections and 443 for https:// and ssl:// connections.
$proxy_user
a user name for accessing the proxy server
$proxy_pass
a password for accessing the proxy server
In order to use SSL connections, your PHP installation must have the
openssl
extension enabled.
Here's an example client set up to query Userland's XML-RPC server at betty.userland.com:
<?php
$client = new XML_RPC_Client('/RPC2', 'betty.userland.com');
?>
This class supports the following methods.
This method takes the form:
$response = $client->send (
$xmlrpc_message
, $timeout
)
Where $xmlrpc_message
is an instance of
XML_RPC_Message and
$response
is an instance of
XML_RPC_Response (see API).
The $timeout
is optional, and will be set to
0
(wait forever) if omitted. This timeout value
is passed to fsockopen().
If the value of $response
is
0
rather than an
XML_RPC_Response object, then this signifies
an I/O error has occured. You can find out what the I/O error was
from the values $client->errno
and
$client->errstring
.
In addition to low-level errors, the XML-RPC server you were querying may return an error in the XML_RPC_Response object. See API for details of how to handle these errors.
$client->setCredentials (
$username
, $password
)
This method sets the username and password for authorizing the client to a server. With the default (HTTP) transport, this information is used for HTTP Basic authorization.
$client->setDebug (
$debugOn
)
$debugOn
is either 0
or
1
depending on whether you require the client to
print debugging information to the browser. The default is not to
output this information.
The debugging information includes the raw data returned from the XML-RPC server it was querying, and the PHP value the client attempts to create to represent the value returned by the server. This option can be very useful when debugging servers as it allows you to see exactly what the server returns.
This class provides a representation for a request to an XML-RPC server. A client sends an XML_RPC_Message to a server, and receives back an XML_RPC_Response.
The constructor takes the following form:
$msg = new XML_RPC_Message (
$methodName
, $parameterArray
)
Where $methodName
is a string indicating the
name of the method you wish to invoke, and
$parameterArray
is a simple
Array of XML_RPC_Value
objects. Here's an example message to the US state
name server:
<?php
require_once "XML/RPC.php";
$msg = new XML_RPC_Message("examples.getStateName", array(new XML_RPC_Value(23, "int")));
?>
This example requests the name of state number 23. For more information on XML_RPC_Value objects, see API.
$outString = $msg->serialize (
)
Returns the an XML string representing the XML-RPC message.
$msg->addParam (
$xmlrpcVal
)
Adds the XML_RPC_Value
$xmlrpcVal
to the parameter list for this
method call.
Returns an XML_RPC_Value object. If the parameter doesn't exist, an XML_RPC_Response object is returned.
$xmlrpcVal = $msg->getParam (
$n
)
Gets the $n
th parameter in the message. Use
this method in server implementations. Returns the
undef
value if no such parameter exists.
$n = $msg->getNumParams (
)
Returns the number of parameters attached to this message.
$methName=$msg->method (
)
$msg->method (
$methName
)
Gets or sets the method contained in the XML-RPC message.
$response = $msg->parseResponse (
$xmlString
)
Given an incoming XML-RPC server response contained in the string
$xmlString
, this method constructs an
XML_RPC_Response response object and returns
it, setting error codes as appropriate.
This method processes any HTTP/MIME headers it finds.
$response = $msg->parseResponseFile (
$fileHandle
)
Given an incoming XML-RPC server response on the file handle
$fileHandle
, this method reads the data and
passes it to parseResponse().
This method is useful to construct responses from pre-prepared files. It processes any HTTP headers it finds.
This class is used to contain responses to XML-RPC requests. A server method handler will construct an XML_RPC_Response and pass it as a return value. This same value will be returned by the result of an invocation of the send() method of the XML_RPC_Client class.
$resp = new XML_RPC_Response (
$xmlrpcval
)
$resp = new XML_RPC_Response (
0
, $errcode
, $errstring
)
The first instance is used when execution has happened without
difficulty: $xmlrpcval
is an
XML_RPC_Value value with the result of the
method execution contained in it.
The second type of constructor is used in case of
failure. $errcode
and
$errstring
are used to provide indication of
what has gone wrong. See API for more
information on passing error codes.
$fn = $resp->faultCode (
)
Returns the integer fault code return from the XML-RPC response
$resp
. A zero value indicates success, any
other value indicates a failure response.
$fs = $resp->faultString (
)
Returns the human readable explanation of the fault indicated by
$resp->faultCode
.
$xmlrpcVal = $resp->value (
)
Returns an XML_RPC_Value object containing
the return value sent by the server. If the response's
faultCode
is non-zero then the value returned
by this method should not be used (it may not even be an object).
$outString = $resp->serialize (
)
Returns an XML string representation of the response.
This is where a lot of the hard work gets done. This class enables the creation and encapsulation of values for XML-RPC.
Ensure you've read the XML-RPC spec at http://www.xmlrpc.com/stories/storyReader$7 before reading on as it will make things clearer.
The XML_RPC_Value class can store arbitrarily
complicated values using the following types:
i4
,
int
,
boolean
,
string
,
double
,
dateTime.iso8601
,
base64
,
array
or
struct
.
You
should refer to the spec for
more information on what each of these types mean.
The type i4 is accepted as a synonym for int. The value parsing code will always convert i4 to int: int is regarded by this implementation as the canonical name for this type.
Base 64 encoding is performed transparently to the caller when using this type. Therefore you ought to consider it as a binary data type, for use when you want to pass none 7-bit clean data. Decoding is also transparent.
The values true
and 1
map to
true
. All other values (including the empty
string) are converted to false
.
The characters
<
,
>
,
"
and
&
are converted to their entity equivalents
<
,
>
,
"
and
&
for transport through XML-RPC. The
current XML-RPC spec recommends only encoding
<
and &
but this implementation goes further, for reasons
explained by the XML 1.0
recommendation.
Currently, passing a string "-1" to XML_RPC_Value's constructor creates an empty result since it serves as a special/magic value.
TODO: '
entity is not yet supported
The constructor is the normal way to create an XML_RPC_Value. The constructor can take these forms:
$myVal = new XML_RPC_Value (
)
$myVal = new XML_RPC_Value (
$stringVal
)
$myVal = new XML_RPC_Value (
$scalarVal
, 'int' | 'boolean' | 'string' | 'double' | 'dateTime.iso8601' | 'base64'
)
$myVal = new XML_RPC_Value (
$arrayVal
, 'array' | 'struct'
)
The first constructor creates an empty value, which must be altered using the methods addScalar(), addArray() or addStruct() before it can be used.
The second constructor creates a simple string value.
The third constructor is used to create a scalar value. The second parameter must be a name of an XML-RPC type. Examples:
<?php
$myInt = new XML_RPC_Value(1267, "int");
$myString= new XML_RPC_Value("Hello, World!", "string");
$myBool = new XML_RPC_Value(1, "boolean");
?>
The fourth constructor form can be used to compose complex XML-RPC values. The first argument is either a simple array in the case of an XML-RPC array or an associative array in the case of a struct. The elements of the array must be XML_RPC_Value objects themselves. Examples:
$ok = $val->addScalar (
$stringVal
)
$ok = $val->addScalar (
$scalarVal
, 'int' | 'boolean' | 'string' | 'double' | 'dateTime.iso8601' | 'base64'
)
If $val
is an empty
XML_RPC_Value this method makes it a scalar
value, and sets that value. If $val
is
already a scalar value, then no more scalars can be added and
0
is returned. If all went OK,
1
is returned.
There is a special case if $val
is an
array: the scalar value passedis appended to
the array.
$ok = $val->addArray (
$arrayVal
)
Turns an empty XML_RPC_Value into an
array with contents as specified by
$arrayVal
. See the fourth constructor form
for more information.
$ok = $val->addStruct (
$assocArrayVal
)
Turns an empty XML_RPC_Value into a
struct with contents as specified by
$assocArrayVal
. See the fourth constructor
form for more information.
$kind = $val->kindOf (
)
Returns a string containing struct
, array
or scalar
describing the base type of the value. If it returns undef
it
means that the value hasn't been initialised.
$outString = $val->serialize (
)
Returns a string containing the XML-RPC representation of this value.
$scalarVal = $val->scalarval (
)
If $val->kindOf() == 'scalar', this method returns the actual PHP-language value of the scalar (base 64 decoding is automatically handled here).
$typeName = $val->scalartyp (
)
If $val->kindOf() == 'scalar', this method
returns a string denoting the type of the scalar. As mentioned
before, i4
is always coerced to
int
.
$xmlrpcVal = $val->arraymem (
$n
)
Returns the $n
th element in the array
represented by the value $val
. The value
returned is an XML_RPC_Value object.
$len = $val->arraysize (
)
If $val
is an array,
returns the number of elements in that array.
$xmlrpcVal = $val->structmem (
$memberName
)
Returns the element called $memberName
from
the struct represented by the value
$val
. The value returned is an
XML_RPC_Value object.
list($key,$value) = $val->structeach (
)
Returns the next (key,value) pair from the struct, when
$val
is a struct. See also API.
$val->structreset (
)
Resets the internal pointer for structeach()
to the beginning of the struct, where $val
is a struct.
The current implementation of this class has been kept as simple as possible. The constructor for the server basically does all the work. Here's a minimal example:
<?php
function foo ($params) {
...
}
$s = new XML_RPC_Server(array("examples.myFunc" => array("function" => "foo")));
?>
This performs everything you need to do with a server. The single argument is an associative array from method names to function names. The request is parsed and despatched to the relevant function, which is reponsible for returning a XML_RPC_Response object, which gets serialized back to the caller.
Here is a more detailed look at what the handler function foo() may do:
<?php
function foo ($params) {
global $XML_RPC_erruser; // import user errcode value
// $params is the received XML_RPC_Message object.
if ($err) {
// this is an error condition
return new XML_RPC_Response(0, $XML_RPC_erruser+1, // user error 1
"There's a problem, Captain");
} else {
// this is a successful value being returned
return new XML_RPC_Response(new XML_RPC_Value("All's fine!", "string"));
}
}
?>
The first argument to the XML_RPC_Server() constructor is an array, called the dispatch map. In this array is the information the server needs to service the XML-RPC methods you define.
The dispatch map takes the form of an associative array of associative arrays: the outer array has one entry for each method, the key being the method name. The corresponding value is another associative array, which can have the following members:
function
- this entry is mandatory. It must
be a name of a function in the global scope which services the
XML-RPC method.
It is possible to use regular functions, class names with method names and objects with method names. The Examples page of this manual demonstrates the appropriate syntax for all of these approaches.
signature
- this entry is an array containg
the possible signatures (see API)
for the method. If this entry is present then the server will
check that the correct number and type of parameters have been
sent for this method before dispatching it.
docstring
- this entry is a string containing
documentation for the method. The documentation may contain HTML
markup.
A signature is a description of a method's return type and its parameter types. A method may have more than one signature.
Within a server's dispatch map, each method has an array of possible signatures. Each signature is an array of types. The first entry is the return type.
Let's run through an example. Imagine you wanted to write a regular PHP function like this:
<?php
function is_8($input, $strict = false) {
if ($strict) {
return ($input === 8);
} else {
return ($input == 8);
}
}
?>
To get it to work in an XML_RPC server, you would have to write it like this:
<?php
function is_8($params) {
// This parameter is required.
$param = $params->getParam(0);
if (!XML_RPC_Value::isValue($param)) {
return $param;
}
$input = $param->scalarval();
// This parameter is optional.
$param = $params->getParam(1);
if (!XML_RPC_Value::isValue($param)) {
$strict = false;
} else {
$strict = $param->scalarval();
}
if ($strict) {
$answer = ($input === 8);
} else {
$answer = ($input == 8);
}
$val = new XML_RPC_Value($answer, 'boolean');
return new XML_RPC_Response($val);
}
?>
Here is a signature covering all of the possible permutations:
<?php
array(
array('boolean', 'int'),
array('boolean', 'int', 'boolean'),
array('boolean', 'string'),
array('boolean', 'string', 'boolean'),
)
?>
The server could be instatiated like this:
<?php
$server = new XML_RPC_Server(
array(
'isan8' =>
array(
'function' => 'is_8',
'signature' =>
array(
array('boolean', 'int'),
array('boolean', 'int', 'boolean'),
array('boolean', 'string'),
array('boolean', 'string', 'boolean'),
),
'docstring' => 'Is the value an 8?'
),
),
1,
0
);
?>
The strings representing the XML-RPC types have been encoded as global variables for your convenience:
<?php
$GLOBALS['XML_RPC_I4'] = 'i4';
$GLOBALS['XML_RPC_Int'] = 'int';
$GLOBALS['XML_RPC_Boolean'] = 'boolean';
$GLOBALS['XML_RPC_Double'] = 'double';
$GLOBALS['XML_RPC_String'] = 'string';
$GLOBALS['XML_RPC_DateTime'] = 'dateTime.iso8601';
$GLOBALS['XML_RPC_Base64'] = 'base64';
$GLOBALS['XML_RPC_Array'] = 'array';
$GLOBALS['XML_RPC_Struct'] = 'struct';
?>
You may want to construct the server, but for some reason not
fulfill the request immediately (security verification, for
instance). If you pass the constructor a second argument of
0
this will have the desired effect. You can then
use the service() method of the server class to
service the request. For example:
<?php
$s = new XML_RPC_Server($myDispMap, 0);
// ... some code that does other stuff here
$s->service();
?>
Fault codes for your servers should start at the value indicated by
the global $xmlrpcerruser
+ 1.
Standard errors returned by the server include:
1
Unknown method
Returned if the server was asked to dispatch a method it didn't know about
2
Invalid return payload
This error is actually generated by the client, not server, code, but signifies that a server returned something it couldn't understand.
3
Incorrect parameters
This error is generated when the server has signature(s) defined for a method, and the parameters passed by the client do not match any of signatures.
4
Can't introspect: method
unknown
This error is generated by the builtin system.*() methods when any kind of introspection is attempted on a method undefined by the server.
5
Didn't receive 200 OK from remote
server
This error is generated by the client when a remote server doesn't return HTTP/1.1 200 OK in response to a request. A more detailed error report is added onto the end of the phrase above.
100-
XML parse errors
Returns 100 plus the XML parser error code for the fault that
occurred. The faultString
returned explains
where the parse error was in the incoming XML stream.
This class generates string representations of the data in XML_RPC_Value objects.
Below are functions that lay outside the various XML_RPC objects.
$rpc = XML_RPC_encode (
$php_val
)
Takes native php types and encodes them into XML_RPC PHP object format.
$values = XML_RPC_decode (
$XML_RPC_val
)
Takes a message in PHP XML_RPC object format and translates it into native PHP types.
PHP5 implementation of the XML-RPC protocol.
This is a "PHP5 only" implementation of the XMLRPC protocol. This package provides the client and the server side of the protocol. An optimized cache is also available for both parts.
As a client library, XML_RPC2 is capable of creating a proxy class which exposes the methods exported by the server. As a server library, XML_RPC2 is capable of exposing methods from a class or object instance, seamlessly exporting local methods as remotely callable procedures.
XML_RPC2 is a "PHP5 only" implementation of the XMLRPC protocol. This package provides the client and the server side of the protocol. An optimized cache is also available for both parts.
As a client library, XML_RPC2 is capable of creating a proxy class which exposes the methods exported by the server. So it's very easy and natural to call XMLRPC exported methods. Like in Python language, the classic way to use XML_RPC2 client side is :
We make a XML_RCP2_Client object with server informations as arguments.
In a classic way, we call a method of this object.
Then, the method call is XMLRPC encoded, sent to the server, the response is decoded into PHP native types and we get the result of the call (all this logic is made by the library in a completely transparent way).
As a server library, XML_RPC2 is capable of exposing methods from a class or object instance, seamlessly capable of exposing methods from a class or object instance, seamlessly exporting local methods as remotely callable procedures. Method signatures are automatically determined and checked by using the reflection API and PHPDOC comments. An automatic documentation about XMLRPC exported methods is dynamically built and available at the server URL (with a simple HTTP GET).
For both sides, an optimized cache based on Cache_Lite can be set. It can be really usefull especially on public XMLRPC servers.
XML_RCP2 need PHP5 and the CURL extension. To avoid in next version, the CURL dependency, we are waiting for a PHP5 E_STRICT PEAR module for HTTP_Request.
If you want to use the integrated cache, you will also need the Cache_Lite PEAR module but it's of course an optional dependency.
XML_RPC2 can use two backends for the XMLRPC encoding/decoding :
XMLRPCEXT, which of course need this PHP extension (probably the better choice but it's an additional dependency) ;
PHP, which doesn't need the XMLRPCEXT extension at all (this is full PHP but slower).
Let's start with a XMLRPC call to the pear.php.net XMLRPC server :
<?php
require_once 'XML/RPC2/Client.php';
$options = array(
'prefix' => 'package.'
);
// We make the XML_RPC2_Client object (as the backend is not specified, XMLRPCEXT
// will be used if available (full PHP else))
$client = XML_RPC2_Client::create('http://pear.php.net/xmlrpc.php', $options);
try {
// Because of the prefix specified in the $options array, indeed, we will call
// the package.info() method with a single argument (the string 'XML_RPC2')
$result = $client->info('XML_RPC2');
// $result is a complex PHP type (no XMLRPC decoding needed, it's already done)
print_r($result);
} catch (XML_RPC2_FaultException $e) {
// The XMLRPC server returns a XMLRPC error
die('Exception #' . $e->getFaultCode() . ' : ' . $e->getFaultString());
} catch (Exception $e) {
// Other errors (HTTP or networking problems...)
die('Exception : ' . $e->getMessage());
}
?>
Let's build a XMLRPC "echo server" :
<?php
require_once 'XML/RPC2/Server.php';
// Let's define a class with public static methods
// PHPDOC comments are really important because they are used for automatic
// signature checking
class EchoServer {
/**
* echoes the message received
*
* @param string Message
* @return string The echo
*/
public static function echoecho($string) {
return $string;
}
}
$options = array(
'prefix' => 'test.' // we define a sort of "namespace" for the server
);
// Let's build the server object with the name of the Echo class
$server = XML_RPC2_Server::create('EchoServer', $options);
$server->handleCall();
?>
If you do a simple HTTP GET on the server URL, you will get an automatic HTML documentation about the echoecho function. If you make a XMLRPC client request on the same URL about the "test.echoecho()" method (with one argument), you will get your argument as a response. If you call another method or with a bad arguments number, you will get an error (because of automatic method signature checking).
As the caching process is completely transparent, this is very similar to the standard client side use :
<?php
require_once 'XML/RPC2/CachedClient.php';
$options = array(
'prefix' => 'package.',
'cacheDebug' => false, // with cacheDebug set to true, it's very easy
// to get an indication about the cache using (or not)
'cacheOptions' => array(
'cacheDir' => '/tmp/',
'lifetime' => 3600, // during this lifetime, the local cache will be used
'cacheByDefault' => true // all methods call will be cached
// (but a more precise way is possible)
)
);
// We make the XML_RPC2_CachedClient object (same syntax than XML_RPC2_Client)
$client = XML_RPC2_CachedClient::create('http://pear.php.net/xmlrpc.php', $options);
try {
// First call, the cache won't be used
$result = $client->info('XML_RPC2');
print_r($result);
// Second call, the cache will be used (in a transparent way) and no
// additional HTTP request will be sent to the server
$result = $client->info('XML_RPC2');
print_r($result);
} catch (XML_RPC2_FaultException $e) {
// The XMLRPC server returns a XMLRPC error
die('Exception #' . $e->getFaultCode() . ' : ' . $e->getFaultString());
} catch (Exception $e) {
// Other errors (HTTP or networking problems...)
die('Exception : ' . $e->getMessage());
}
?>
As the caching process is completely transparent, this is very similar to the standard server side use :
<?php
require_once 'XML/RPC2/CachedServer.php';
// Let's define a class with public static methods
// PHPDOC comments are really important because they are used for automatic
// signature checking
// IMPORTANT : note the @xmlrpc.caching PHPDOC tags to indicate
// that the method has to be cached
class EchoServer {
/**
* echoes the message received
*
* @param string Message
* @return string The echo
* @xmlrpc.caching true
*/
public static function echoecho($string) {
return $string;
}
}
$options = array(
'prefix' => 'test..',
'cacheDebug' => false, // with cacheDebug set to true, it's very easy
// to get an indication about the cache using (or not)
'cacheOptions' => array(
'cacheDir' => '/tmp/',
'lifetime' => 3600,
'cacheByDefault' => false // we don't cache by default (only methods with @xmlrpc.caching true)
)
);
$server = XML_RPC2_CachedServer::create('EchoServer', $options);
$server->handleCall();
?>
Thanks to PHP5, it's really easy to do XMLRPC client requests with XML_RPC2. The usage is really straightforward.
First, you include 'XML/RPC2/Client.php' file (only this one).
<?php
require_once 'XML/RPC2/Client.php';
?>
Second, you make an assocative arrays of options to tune XML_RPC2 (prefix, proxy, manual backend choice...).
<?php
$options = array(
'prefix' => 'package.'
);
?>
Third, you make a XML_RPC2_Client object with the server URL and the with the options array.
<?php
$client = XML_RPC2_Client::create('http://pear.php.net/xmlrpc.php', $options);
?>
Then, you send your request by calling the server method as it was a local method of the $client object.
<?php
$result = $client->info('XML_RPC2');
?>
This single line will encode a XMLRPC client request for the package.info() (prefix + method name) method with a single argument (the string 'XML_RPC2'), will send the request over HTTP to the server and will decode the response into PHP native types. With a single line !
Of course, to catch server errors, you have to add a few lines around you client call like for example :
<?php
try {
$result = $client->info('XML_RPC2');
print_r($result);
} catch (XML_RPC2_FaultException $e) {
// The XMLRPC server returns a XMLRPC error
die('Exception #' . $e->getFaultCode() . ' : ' . $e->getFaultString());
} catch (Exception $e) {
// Other errors (HTTP or networking problems...)
die('Exception : ' . $e->getMessage());
}
?>
This array is completely optional but really usefull. The following keys are available :
Option | Data Type | Default Value | Description |
---|---|---|---|
prefix | string | '' | Prefix added before XMLRPC called method name |
proxy | string | '' | Proxy used for the HTTP request (default : no proxy) |
debug | boolean | FALSE | Debug mode ? |
encoding | string | '' | Encoding of the request 'utf-8', 'iso-8859-1' (for now, only these two ones are officialy supported) |
uglyStructHack | boolean | TRUE | ugly hack to circumvent a XMLRPCEXT bug/feature , see this PHP bug for more details. The only (reasonable) counterpart of this hack is that you can't use structs with a key beginning with the string 'xml_rpc2_ugly_struct_hack_' as arguments of the called method. |
It's really easy to make the XML_RPC2_Client object. Use the following syntax :
<?php
// $XMLRPCServerURL is a string : 'http://pear.php.net/xmlrpc.php' (for example)
// $options is an optional array : see previous section for more informations
$client = XML_RPC2_Client::create($XMLRPCServerURL, $options);
?>
Don't try to call the XML_RPC2_Client constructor directly, use the call() static method.
When the XML_RPC2_Client object is created, you can directly call the remote method as it was local. For example :
<?php
// We call the remote foo() method without any arguments
$result1 = $client->foo();
// We call the remote bar() method with two arguments (an integer : 123, a string : 'foo')
$result2 = $client->bar(123, 'foo');
// We call the remote foobar() method with complex data types (2 integer, a string, a structure)
$result3 = $client->foobar(1, 2, 'foo', array('foo' => 1, 'bar' => 2));
?>
Be careful, XMLRPC spec allows some remote method names with some special characters like "." or "/"... which are not available as PHP method names. To deal with them, you have to fix a prefix in a the options array. For example :
<?php
$options = array('prefix' => 'foo.');
$client = XML_RPC2_Client::create('http://...', $options);
// We call the foo.bar() method because of the prefix 'foo.' fixed in $options array
$result = $client->bar();
?>
In most cases, XML_RPC2 transforms automatically PHP native types into XMLRPC types (as described in the SPEC) for the request. In most cases too, XML_RPC2 transforms the XML server response into PHP native types too. Yet, there are two exceptions : 'dateTime.iso8601' and 'base64' which doesn't really exist in PHP.
To manipulate explicitely these two types, you have to use special objects. Let's see a complete example :
<?php
// Classic usage
require_once 'XML/RPC2/Client.php';
// To manipulate these types, we need to include this file too
require_once 'XML/RPC2/Value.php';
// To get a 'dateTime.iso8601' object, you have first to set a string with an iso8601 encoded date :
$tmp = "20060116T19:14:03";
// Then, you call this static method to get your 'dateTime.iso8601' object
$time = XML_RPC2_Value::createFromNative($tmp, 'datetime');
// For 'base64', you call the same static method with your string to get a 'base64' object
$base64 = XML_RPC2_Value::createFromNative('foobar', 'base64');
// Then, you can use XML_RPC2_Client as usual :
$options = array('prefix' => 'validator1.');
$client = XML_RPC2_Client::create('http://phpxmlrpc.sourceforge.net/server.php', $options);
$result = $client->manyTypesTest(1, true, 'foo', 3.14159, $time, $base64);
// The remote validator1.manyTypesTest() method returns an array with the 6 given arguments
$result_datetime = $result[4]; // a 'dateTime.iso8601' object
$result_base64 = $result[5]; // a 'base64' object
// To transform these objects into PHP native types, you have to use public properties of
// these objects as follow :
var_dump($result_datetime->scalar); // will return string(17) "20060116T19:14:03"
var_dump($result_datetime->xmlrpc_type); // will return string(8) "datetime"
var_dump($result_datetime->timestamp); // will return int(1137435243)
var_dump($result_base64->scalar); // will return string(6) "foobar"
var_dump($result_base64->xmlrpc_type); // will return string(6) "base64"
?>
to be written
Thanks to PHP5 and PEAR/Cache_Lite, it's really easy to do XMLRPC "cached client" requests with XML_RPC2. The usage is really straightforward.
First, you include 'XML/RPC2/CachedClient.php' file (only this one).
<?php
require_once 'XML/RPC2/CachedClient.php';
?>
Second, you make an assocative arrays of options to tune XML_RPC2 (prefix, proxy, manual backend choice...).
<?php
$options = array(
'prefix' => 'package.',
'cacheOptions' => array(
'cacheDir' => '/tmp/',
'lifetime' => 3600
)
);
?>
Third, you make a XML_RPC2_CachedClient object with the server URL and the with the options array.
<?php
$client = XML_RPC2_CachedClient::create('http://pear.php.net/xmlrpc.php', $options);
?>
Then, you send your request by calling the server method as it was a local method of the $client object.
<?php
$result = $client->info('XML_RPC2');
?>
This single line will encode a XMLRPC client request for the package.info() (prefix + method name) method with a single argument (the string 'XML_RPC2'), will send the request over HTTP to the server and will decode the response into PHP native types. With a single line ! Moreover, the result will be cached into a file for the given lifetime. So the next call will not send any HTTP request and it will be really fast.
Of course, to catch server errors, you have to add a few lines around you client call like for example :
<?php
try {
$result = $client->info('XML_RPC2');
print_r($result);
} catch (XML_RPC2_FaultException $e) {
// The XMLRPC server returns a XMLRPC error
die('Exception #' . $e->getFaultCode() . ' : ' . $e->getFaultString());
} catch (Exception $e) {
// Other errors (HTTP or networking problems...)
die('Exception : ' . $e->getMessage());
}
?>
This array is completely optional but really usefull. The following keys are available :
Option | Data Type | Default Value | Description |
---|---|---|---|
[...] | [...] | [...] | See "non cached client side" for more options |
cacheOptions | array | array() | See next table for a complete description |
"CacheOptions" entry is an associative array. Available keys are :
Option | Data Type | Default Value | Description |
---|---|---|---|
[...] | [...] | [...] | See "Cache_Lite constructor" for more options |
defaultCacheGroup | string | '' | Name of the default cache group |
cachedMethods | array | array() | Array of method names to be cached (if cacheByDefault is false) |
notCachedMethods | array | array() | Array of method names not to be cached (if cacheByDefault is true) |
cacheByDefault | boolean | TRUE | if true, the cache is "on" by default ; else, only methods listed in cachedMethods will be cached |
It's really easy to make the XML_RPC2_CachedClient object. Use the following syntax :
<?php
// $XMLRPCServerURL is a string : 'http://pear.php.net/xmlrpc.php' (for example)
// $options is an optional array : see previous section for more informations
$client = XML_RPC2_CachedClient::create($XMLRPCServerURL, $options);
?>
Don't try to call the XML_RPC2_CachedClient constructor directly, use the call() static method.
See "non cached client side" documentation, there is no difference.
The caching process is completly embedded and automatic. If a cache is available, result will be get from cache and no HTTP request will be sent. If there is no cache available for this particular call (method name and arguments), the classical XMLRPC communication will be used (and the result will be stored into a cache file for the next use).
to be written
Provides Packages for creating and working with XML
Package to "beautify" XML documents. This package will add line breaks, indentation and other layout elements to an XML document, so it can be easier read by humans.
XML_Beautifier is a package, that helps you making XML documents easier to read for human beings.
It is able to add line-breaks, indentation, sorts attributes, convert tag cases and wraps long comments. It recognizes tags, character data, comments, XML declarations, processing instructions and external entities and is able to format these tokens nicely.
The document is split into these tokens using the XML_Beautifier_Tokenizer class and the expat parser. Then a renderer is used to create the string representation of the document and formats it using the specified options.
Currently only one renderer is available, but as XML_Beautifier uses a driver-based architecture, other renderers (like syntax-highlighting) will follow soon.
Let's assume, you've got an XML document that is really badly formatted:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE page [ <!ENTITY foo SYSTEM "foo.xml"> <!ENTITY bar SYSTEM "bar.xml"> ]> <document title="Current News"> <meta project="none" foo="bar"> <keywords/><description/> <author>Stephan Schmidt</author> <getMetaNav/> </meta> <page label="PHP Application Tools" sublabel="Current News"> <?php for($i = 0; $i < count($_GET); $i++) { echo $_GET[$i]."<br>"; } ?> &foo;&bar; <intro> <!-- This Comment has more than one line. --> <introtitle>Welcome to PHP Application Tools & PEAR!</introtitle> <para> If you're new to pat, and would like <!-- This is a comment in a single line that contains an & --> to know what we do here, take a look at <link url= "/about/project.xml"> "About Pat"</link> or check out the <link url="/about/projectsOverview.xml">"projects overview"</link>. Otherwise, you probably know your way around the site already <smiley type="smile"/> </para> </intro> </page> </document>
All you have to do is to use XML_Beautifier, pass the filename of the original file and the filename for the new file:
<?php
require_once "XML/Beautifier.php";
$fmt = new XML_Beautifier();
$result = $fmt->formatFile('originalFile.xml', 'beautifiedFile.xml');
if (PEAR::isError($result)) {
echo $result->getMessage();
exit();
}
echo "File beautified, awaiting orders!<br />";
?>
And voila, your document looks nice:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE page [ <!ENTITY foo SYSTEM "foo.xml"> <!ENTITY bar SYSTEM "bar.xml"> ]> <document title="Current News"> <meta foo="bar" project="none"> <keywords /> <description /> <author>Stephan Schmidt</author> <getMetaNav /> </meta> <page label="PHP Application Tools" sublabel="Current News"> <?php for($i = 0; $i < count($_GET); $i++) { echo $_GET[$i]."<br>"; } ?> &foo; &bar; <intro> <!-- This Comment has more than one line. --> <introtitle>Welcome to PHP Application Tools & PEAR!</introtitle> <para> If you're new to pat, and would like <!-- This is a comment in a single line that contains an & --> to know what we do here, take a look at <link url="/about/project.xml">"About Pat"</link> or check out the <link url="/about/projectsOverview.xml">"projects overview"</link> . Otherwise, you probably know your way around the site already <smiley type="smile" /> </para> </intro> </page> </document>
Options let you influence the beautifiying process. They are passed to the renderer and thus, you have to check, whether the renderer you are using supports the options you want to use.
As there currently is only one renderer (Plain) available, you should not worry about this too much.
Options can be passed as an associative array to the constructor of XML_Beautifier. You may also use setOption(), or setOptions() to set one or more options after the instance of XML_Beautifier has been created.
Here is a list of all options supported by XML_Beautifier.
Option | Possible values | Default | Description |
---|---|---|---|
removeLineBreaks | TRUE or FALSE | TRUE | Sets, whether linebreaks should be stripped from cdata sections |
indent | any string | " " (4 spaces) | The string passed to this option will be used to indent the tags in one level. |
linebreak | any string | "\n" | The string passed to this option will be used for linebreaks You should use "\n" or "\r\n". |
caseFolding | TRUE or FALSE | FALSE | Disable or enable case folding for tags and attributes |
caseFoldingTo | "uppercase" or "lowercase" | "uppercase" | Can be used, if caseFolding is set to TRUE to define whether tags and attributes should be converted to upper- or lowercase |
normalizeComments | FALSE or TRUE | FALSE | If set to true, all adjacent whitespaces in an XML comment will be converted to one space. This will convert comments with more than one line to a comment with one line. |
maxCommentLine | integer | -1 | Maximum length of comment line. If a comment exceeds this limit, it will be wrapped automatically. If set to -1 the line length is unlimited. |
multilineTags | TRUE or FALSE | FALSE | If set to true, a linebreak will be added inside the tags after each attribute and attributes will be indeted. |
The following example shows how to set options for XML_Beautifier.
Using setOptions() and setOption()
<?php
require_once "XML/Beautifier.php";
$options = array(
"caseFolding" => true,
"caseFoldingTo" => "uppercase",
"normalizeComments" => true
);
$fmt = new XML_Beautifier($options);
$result = $fmt->formatFile('originalFile.xml', 'outputFile.xml');
?>
The following example shows how to set options for XML_Beautifier, if the instance already has been created.
XML_Beautifier options
<?php
require_once "XML/Beautifier.php";
$fmt = new XML_Beautifier();
$options = array(
"caseFolding" => true,
"caseFoldingTo" => "uppercase",
"normalizeComments" => true
);
$fmt->setOptions($options);
$fmt->setOption("indent", "\t");
$result = $fmt->formatFile('originalFile.xml', 'outputFile.xml');
?>
object XML_Beautifier::XML_Beautifier (
array $options = array()
)
Creates a new instance of XML_Beautifier.
array $options
- options are used to influence to beautifying process.
You may set the indent string, specify whether case folding should be enabled, etc...
See XML_Beautifier options for more information.
object XML_Beautifier object
This function can not be called statically.
void XML_Beautifier::setOption (
string $option
, mixed $value
)
Set an option for the beautifying process.
See XML_Beautifier options for more information.
string $option
- name of the option
mixed $value
- value of the option
void
This function can not be called statically.
void XML_Beautifier::setOptions (
array $options
)
Set several options for the beautifying process.
See XML_Beautifier options for more information.
array $options
- associative array containing options and their values, like you would pass it to the constructor.
void
This function can not be called statically.
void XML_Beautifier::resetOptions (
)
Reset all options to their default values.
See XML_Beautifier options for more information.
This method does not accept any parameters.
void
This function can not be called statically.
string XML_Beautifier::apiVersion (
)
Returns API version of XML_Beautifier.
string API version
This function can be called statically.
object XML_Beautifier::formatFile (
string $file
, string $newFile
= null
, string $renderer = "Plain"
)
Beautifies a file.
string $file
- filename of the original file
string $newFile
- filename for the beautified file.
If no file is given, the method will return the resulting XML document.
To overwrite the original file, pass XML_BEAUTIFIER_OVERWRITE.
string $renderer
- renderer to use, currently only a "Plain" XML renderer
can be used.
mixed Either true, if file has been written or the XML document string.
This function can not be called statically.
object XML_Beautifier::formatString (
string $string
, string $renderer = "Plain"
)
Beautifies an XML string. Use this method to beautify an XML document that has been generated on-the-fly.
string $string
- string containing an XML document.
string $renderer
- renderer to use, currently only a "Plain" XML renderer
can be used.
string beautified XML document
This function can not be called statically.
Parses DTD files and does DTD validation of XML documents.
XML_DTD is able to parse a document type definition (DTD) file and validate XML documents according to this DTD. A DTD is the grammar of an XML application, it consist of rules that declare how tags have to be nested and which attributes may be used in the tags.
If you want to learn how to create a DTD, you should take a look at the W3C.
XML_DTD consits of three parts:
XML_DTD_Parser, which will parse a DTD file and return an XML_DTD_Tree object
XML_DTD_XmlValidator, which validates an XML document using a DTD file
XML_DTD_XmlParser, which will parse a XML file to determine its skeleton, that is, which elements it contains and how they are linked together.
If you validate an XML_Document using XML_DTD_XmlValidator, it will automatically parse the DTD file and create an XML_DTD_Tree from it.
You can validate a XML file against a DTD using
isValid()
like in the example below:
<?php
require_once 'XML/DTD/XmlValidator.php';
$dtd_file = realpath('myfile.dtd');
$xml_file = realpath('myfile.xml');
$validator = new XML_DTD_XmlValidator();
if (!$validator->isValid($dtd_file, $xml_file)) {
die($validator->getMessage());
}
?>
isValid()
will return true when the XML
document validates and false otherwise.
This is pretty much the most common usage of XML_DTD and all you need to know to get started.
XML_Feed_Parser is a parser for (the various) RSS and Atom format XML feeds. It provides a somewhat unified API while still allowing access to the full details of each feed type.
XML_Feed_Parser provides a generic interface to a number of the most popular XML-based syndication formats (Atom, RSS1, RSS2, etc). In order to focus on its core competencies, it does not provide any HTTP or feed creation features, but instead parses a wide range of formats quickly and simply.
Presuming the XML for a feed is stored in the variable $xml_source, the simplest way to create an instance of the parser is:
<?php
try {
$feed = new XML_Feed_Parser($xml_source);
} catch (XML_Feed_Parser_Exception $e) {
die('Feed invalid: ' . $e->getMessage());
}
?>
The constructor accepts a number of parameters, as follows:
<?php
/* The file you wish to parse */
$source = 'my_source.xml';
/* Where Relax NG is available, we can force validation of the feed */
$validate = true;
/* Whether or not to suppress non-fatal warnings */
$suppress_warnings = false;
/* If the feed is not valid XML and the tidy extension is installed we can
* attempt to use it to fix the feed */
$use_tidy = true;
$feed = new XML_Feed_Parser($source, $validate, $suppress_warnings, $use_tidy);
?>
Once you have an instance of the parser you can extract feed-level data by querying it directly. eg.
<?php
$title = $feed->title;
?>
You can also access elements by iterating over the feed, or directly by offset or id.
<?php
foreach ($feed as $entry) {
print $entry->title . "\n";
}
$first_entry = $feed->getEntryByOffset(0);
$particular_entry = $feed->getEntryById('http://jystewart.net/entry/1');
?>
Using XML namespaces, the various syndication formats are very easy to extend and the number of extensions in use is enormous.
XML_Feed_Parser focuses on core functionality shared between the various syndication formats. Some of the most common extensions are handled natively—particularly when they provide one format with features to bring it into line with others, such as the content extension for RSS2—but the majority provide specialist content and providing support for them would quickly result in a bloated package.
It is possible that a proper extensions mechanism may be introduced in a future version, but as an alternative the DOM models in use within the classes are publicly accessible, allowing the package to be wrapped with special handlers.
For example, to make use of the 'pheed' extension (namespace http://www.pheed.com/pheed/) we might use:
<?php
$feed = new XML_Feed_Parser($xml_source);
$entry = $feed->getEntryByOffset(0);
$eModel = $entry->model;
$thumbnails = $eModel->getElementsByTagNameNS(
'http://www.pheed.com/pheed/', 'thumbnail');
if ($thumbnails->length) {
$thumbnail_url = $thumbnails->item(0)->nodeValue;
}
?>
Object-oriented abstraction for PHP's xml extension.
XML_Parser provides an object-oriented abstraction to PHP's ext/xml. It helps you processing XML documents by supplying methods that are needed when working with XML documents, like automatic error handling, parsing from a file, URL or string as well as an easy way to register callbacks.
XML_Parser uses SAX-based parsing, which is a simple API to retrieve information from XML documents. While the parser reads the document, it will call methods for the different nodes that are found. These nodes range from opening and closing tags to character data and processing instructions.
You will not be able to use XML_Parser directly to parse your documents, but you have to create a new class that extends XML_Parser and implement handlers for the tags and all other elements you need to process.
Several of PEAR's XML packages use this approach and thus rely on XML_Parser. If you would like to take a look at real-life examples, just install XML_RDDL, XML_Beautifier, XML_Statistics or XML_Serializer.
A tutorial that explains nearly every feature of XML_Parser is available at http://www.schst.net/articles/XML_Parser.
This example shows you how to create a very simple parser that handles start, end elements (i.e. opening and closing tags), and character data (the "content" between the tags).
<?php
require_once 'XML/Parser.php';
class myParser extends XML_Parser
{
function myParser()
{
parent::XML_Parser();
}
/**
* handle start element
*
* @access private
* @param resource xml parser resource
* @param string name of the element
* @param array attributes
*/
function startHandler($xp, $name, $attribs)
{
printf('handle start tag: %s<br />', $name);
}
/**
* handle end element
*
* @access private
* @param resource xml parser resource
* @param string name of the element
*/
function endHandler($xp, $name)
{
printf('handle end tag: %s<br />', $name);
}
/**
* handle character data
*
* @access private
* @param resource xml parser resource
* @param string character data
*/
function cdataHandler($xp, $cdata)
{
// does nothing here, but might e.g. print $cdata
}
}
$p = &new myParser();
$result = $p->setInputFile('xml_parser_file.xml');
$result = $p->parse();
?>
This parser will just output the names of the opening and closing tags that are found while parsing the document.
XML_Parser provides two modes for parsing:
func
event
In the 'event' mode, XML_Parser will always call the methods startHandler() and endHandler(), independent of the tag name. In these methods you'll have to check for the tagname, which is passed as the second parameter and decide what you need to do. In most cases, this is done with a switch/case statement.
In the 'func' mode, XML_Parser will call different methods based on the name of the tag. This allows you to dispatch different tags to different methods. The methods to handle the start tag have to be called xmltag_[tagname](), where [tagname] has to be substituted by the name of the element that you wish to process. That means if you wish to write a method to process all opening <title> tags, it has to be called xmltag_title.
Methods to handle closing tags have to be named xmltag_[tagname]_() (distinguishes from the start element handler using a traling underscore).
As XML tags may contain chars like ".", ":" and "-" and those chars are not allowed in PHP function names, XML_Parser will replace them with "_" when building the name of the callback function.
If a method does not exist, XML_Parser will skip the tag without handling the element, unless you implemented the methods xmltag() and xmltag_(), which will be used as fallback methods.
There are two ways of setting the mode:
Specifying the mode in the constructor as the second parameter.
In the subclass of XML_Parser that you implemented to process the XML documents, you will have to call XML_Parser::XML_Parser() in the constructor. This method accepts the mode as its second parameter.
Setting the mode using XML_Parser::setMode().
XML_Parser is able to parse from several inputs:
filesystem or anything that can be accessed via stream
strings
resources
You will have to set the input using setInputFile(), setInputString() or setInput() and then call parse() to start the XML processing.
To parse from a file or URL, you'll have to set the name or URI using XML_Parser::setInputFile(). You may not only use files located on the filesystem, but any location that can be opened using fopen(). You may combine this functionality with the stream functions or PEAR's Streams packages to parse documents from shared memory, databases or anything else.
You may also use any of PHP's built-in wrappers to open HTTP or FTP locations as well as STDIN.
To parse an XML string, pass the string to XML_Parser (or your subclass of XML_Parser) using XML_Parser::setInputString(). A big advantage of SAX-based parsing is that the parser does not have to read the whole XML document to process it. If you are parsing very large documents, you should aboid setInputString() and use setInput() or setInputFile() instead.
There's a third, but seldomly used way to specify XML_Parser's input. XML_Parser allows you to pass a PHP resource, like a file pointer that has been returned by fopen(). This can be useful, if you are generating the document on-the-fly and already opened it before. Instead of closing the file and passing its name to XML_Parser, you may simply use XML_Parser::setInput() to pass the resource. Make sure that the file pointer is at the correct location in your file using fseek().
Allows you to transform records retrieved with one or more SQL SELECT queries into XML data.
XML_Query2XML allows you to transform the records retrieved with one or more SQL SELECT queries into XML data. Very simple to highly complex transformations are supported. Is was written with performance in mind and can handle large amounts of data. No XSLT needed!
Class to read RDDL (Resource Directory Description Language) documents.
XML_RDDL is a class that allows the extraction of RDDL resources from an XML document.
The Resource Directory Description Language is an extension of XHTML Basic 1.0 with an added element named resource. This element serves as an XLink to the referenced resource, and contains a human-readable description of the resource and machine readable links which describe the purpose of the link and the nature of the resource being linked to. The nature of the resource being linked to is indicated by the xlink:role attribute and the purpose of the link is indicated by the xlink:arcrole attribute.
For more information on RDDL, visit the RDDL website.
As the RDDL website contains RDDL resources embedded in the XHMTL code of the home page, it can be used in the following example.
Fetching resources from www.rddl.org
<?php
require_once "XML/RDDL.php";
// create new RDDL parser
$rddl = &new XML_RDDL();
// parse a document that contains RDDL resources
$result = $rddl->parseRDDL('http://www.rddl.org');
// check for error
if (PEAR::isError($result)) {
echo sprintf( "ERROR: %s (code %d)", $result->getMessage(), $result->getCode());
exit;
}
// get all resources
$resources = $rddl->getAllResources();
echo "<pre>";
print_r($resources);
echo "</pre>";
// get one resource by its Id
$test = $rddl->getResourceById('CSS');
echo "<pre>";
print_r($test);
echo "</pre>";
// get all stylesheets
$test = $rddl->getResourcesByNature('http://www.w3.org/1999/XSL/Transform');
echo "<pre>";
print_r($test);
echo "</pre>";
// get all normative references
$test = $rddl->getResourcesByPurpose('http://www.rddl.org/purposes#normative-reference');
echo "<pre>";
print_r($test);
echo "</pre>";
?>
object XML_RDDL::XML_RDDL (
string $rddlNamespace = "rddl"
, string $xlinkNamespace = "xlink"
)
Creates a new instance of XML_RDDL that can be used to parse RDDL documents.
When instantiating you may specifiy RDDL and XLINK namespaces. This may change, when XML_Parser supports namespaces and XML_RDDL will be able to extract the correct namespace abbrevations.
string $rddlNamespace
- abbrevation for the RDDL namespace.
string $xlinkNamespace
- abbrevation for the XLink namespace.
This function can not be called statically.
boolean XML_RDDL::parseRDDL (
string $input
, boolean $isFile = true
)
Parses an XML or XHTML document that contains RDDL resources and stores information about them for retrieval by the other methods.
string $input
- input to parse, either a string or a file. If it's a file, you have to set $isFile to true.
boolean $isFile
- flag to indicate whether the $input parameter should be treated as a filename.
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
array XML_RDDL::getAllResources (
)
Returns an array containing all reources that have been found. You have to call XML_RDDL::parseRDDL() first.
array array containing all resources
This function can not be called statically.
array XML_RDDL::getResourceById (
string $id
)
Gets one resource from a document. You have to call XML_RDDL::parseRDDL() first.
string $id
- ID of the resource you want to get.
array array containing all information about the specified resource or a PEAR_Error if the resource does not exist.
This function can not be called statically.
array XML_RDDL::getResourcesByNature (
string $nature
)
Gets all resources of a given nature from an RDDL document. You have to call XML_RDDL::parseRDDL() first. The nature of a resource is specified by the 'xlink:role' attribute. A nature can be a stylesheet, a DTD, a HTML document, etc. You can find a list of well known natures at http://www.rddl.org/natures/.
string $nature
- nature of the resources you want to get.
array array containing all resources with the specified nature.
This function can not be called statically.
Getting all stylesheets from a document
<?php
require_once "XML/RDDL.php";
// create new parser
$rddl = &new XML_RDDL();
// parse a document that contains RDDL resources
$result = $rddl->parseRDDL('http://www.rddl.org');
// check for error
if (PEAR::isError($result)) {
echo sprintf( "ERROR: %s (code %d)", $result->getMessage(), $result->getCode());
exit;
}
// get all stylesheets
$xslt = $rddl->getResourcesByNature('http://www.w3.org/1999/XSL/Transform');
echo "<pre>";
print_r($xslt);
echo "</pre>";
?>
array XML_RDDL::getResourcesByPurpose (
string $purpose
)
Gets all resources of a given purpose from an RDDL document. You have to call XML_RDDL::parseRDDL() first. The purpose of a resource is specified by the 'xlink:arcrole' attribute. The purpose of a resource link determines what the link will be used for. Frequently the purpose of a link can be determined from the nature of the referenced resource. For example the purpose of an XML Schema is typically schema validation, yet a schema may be comprised of a number of included modules and even when included modules are themselves an XML Schema, the purpose is as a module. You can find a list of well known purposes at http://www.rddl.org/purposes/.
string $purpose
- purpose of the resources you want to get.
array array containing all resources with the specified purpose.
This function can not be called statically.
Getting all normative references
<?php
require_once "XML/RDDL.php";
// create new parser
$rddl = &new XML_RDDL();
// parse a document that contains RDDL resources
$result = $rddl->parseRDDL('http://www.rddl.org');
// check for error
if (PEAR::isError($result)) {
echo sprintf( "ERROR: %s (code %d)", $result->getMessage(), $result->getCode());
exit;
}
// get all normative references
$ref = $rddl->getResourcesByPurpose('http://www.rddl.org/purposes#normative-reference');
echo "<pre>";
print_r($ref);
echo "</pre>";
?>
array XML_RDDL::getResourcesByLanguage (
string $language
)
Gets all resources of a given language from an RDDL document. You have to call XML_RDDL::parseRDDL() first. If a resource has no xml:lang attribute, the xml:lang attribute of the root of the document is used.
string $language
- language of the resources you want to get.
array array containing all resources with the specified language.
This function can not be called statically.
Getting all english resource
<?php
require_once "XML/RDDL.php";
// create new parser
$rddl = &new XML_RDDL();
// parse a document that contains RDDL resources
$result = $rddl->parseRDDL('http://www.rddl.org');
// check for error
if (PEAR::isError($result)) {
echo sprintf( "ERROR: %s (code %d)", $result->getMessage(), $result->getCode());
exit;
}
// get all english resources
$en = $rddl->getResourcesByLanguage('en');
echo "<pre>";
print_r($en);
echo "</pre>";
?>
string XML_RDDL::apiVersion (
)
Returns API version of XML_RDDL.
string API version
This function can be called statically.
Parser for Resource Description Framework (RDF) Site Summary (RSS) documents.
Resource Description Framework (RDF) Site Summary (RSS) documents are XML documents, that provide a lightweight multipurpose extensible metadata description and syndication format. RSS files are often used to syndicate news or headlines from portal sites (e.g. Slashdot or freshmeat.net) or weblogs.
For more information on RSS see the website of the RSS working group (http://tech.groups.yahoo.com/group/rss-dev/files/specification.html).
RSS file
<?xml version="1.0"?> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://my.netscape.com/rdf/simple/0.9/"> <channel> <title>FooBar Inc.</title> <link>http://example.com/</link> <description>abcd, xyz, 123</description> </channel> <image> <title>FooBar</title> <url>http://example.com/images/rssicon.gif</url> <link>http://example.com/</link> </image> <item> <title>Headline 1</title> <link>http://example.com/news.php?h=1</link> </item> <item> <title>Headline 2</title> <link>http://example.com/news.php?h=2</link> </item> <textinput> <title>Search FooBar Inc.</title> <description>Search FooBar Inc. headlines</description> <name>q</name> <link>http://example.com/search.php</link> </textinput> </rdf:RDF>
To parse the XML files that contain the RSS feeds, XML_RSS requires that the package XML_Parser is installed. However this dependency will be automatically handled by the installer during the installation phase of XML_RSS.
Since most RSS feeds are located on remote hosts, one has to ensure that the PHP configuration value "allow_url_fopen" is set to "On" before running XML_RSS.
To find out the current value of "allow_url_fopen", one
can use the function ini_get(). To set a new value
of "allow_url_fopen", you must modify the
php.ini
configuration file of PHP.
The following script fetches the latest headlines from Slashdot.org via RSS.
Fetching headlines from Slashdot
<?php
require_once "XML/RSS.php";
$rss =& new XML_RSS("http://rss.slashdot.org/Slashdot/slashdot");
$rss->parse();
echo "<h1>Headlines from <a href=\"http://slashdot.org\">Slashdot</a></h1>\n";
echo "<ul>\n";
foreach ($rss->getItems() as $item) {
echo "<li><a href=\"" . $item['link'] . "\">" . $item['title'] . "</a></li>\n";
}
echo "</ul>\n";
?>
void XML_RSS::XML_RSS (
mixed $handle=''
)
Constructor
mixed $handle
-
file pointer or name of the RDF file.
This function can not be called statically.
Using XML_RSS()
<?php
$r =& new XML_RSS("test.rss");
?>
array XML_RSS::getStructure (
)
Get complete structure of RSS file
array
- the array depends on the structure
of the RSS document.
This function can not be called statically.
array XML_RSS::getChannelInfo (
)
Get general information about current channel. This function returns an array containing the information that has been extracted from the <channel>-tag while parsing the RSS document.
array
- an array of strings. The keys
of the array are
'title'
- name of the channel
'link'
- URL to the channel site
'description'
- description of the
channel
This function can not be called statically.
array XML_RSS::getItems (
)
Get items from RSS document. This function returns an array containing the set of items that are provided by the RSS document.
array
- a two-dimensional array.
Every inner array contains information about a
site. Use the array key 'title'
to get the article title and the key
'link'
for the URL
of the site.
This function can not be called statically.
Using getItems()
<?php
foreach ($r->getItems() as $value) {
echo $value['title'] . ": " . $value['link'] . "\n";
}
?>
array XML_RSS::getImages (
)
Get images from RSS document. This function returns an array containing the set of images that are provided by the RSS document.
array
- a two-dimensional array.
Every inner array contains information about an image of a
site.
'title'
- name of the channel
'link'
- URL to the site
'url'
- URL of the image
This function can not be called statically.
array XML_RSS::getTextinputs (
)
Get text input fields from RSS document.
array
- an hash containing the data of
an input field
'title'
- name of the input field
'description'
- description
of the inout field
'link'
- URL for the request
'name'
- name of the
request parameter
This function can not be called statically.
XML_Serializer serializes complex data structures like arrays or object as XML documents. This class helps you generating any XML document you require without the need for DOM.
Currently there are two ways in which XML_Serializer can be used in your applications:
use XML_Serializer's functionality to create XML documents in a specific XML application (e.g. RDF) that is being processed by an existing script)
use XML_Serializer's functionality to serialize data structures that have to be unserialized at a later point. This is possible by adding type information to all XML elements.
The package not only contains a serializer class but also a matching XML_Unserializer, which is able to virtually read any XML document and return an array or object structure that represents the data stored in the document.
There are several tutorials on XML_Serializer available, that help you get started.
Create a RSS newsfeed with XML_Serializer (local) by cnb
Instant XML with PHP and PEAR::XML_Serializer by Harry Fuecks
Serializing XML With PHP by Vikram Vaswani
Name | Value | Notes |
---|---|---|
XML_SERIALIZER_ERROR_NO_SERIALIZATION | 51 | error code returned by serialize() when it did not succeed |
XML_SERIALIZER_MODE_DEFAULT | default | use for default serializer mode behavior |
XML_SERIALIZER_MODE_SIMPLEXML | simplexml | use for SimpleXML behavior in serializer |
XML_SERIALIZER_ENTITIES_NONE | XML_UTIL_ENTITIES_NONE | maps to XML_Util's Entity replacement behavior of "don't replace entities" |
XML_SERIALIZER_ENTITIES_XML | XML_UTIL_ENTITIES_XML | maps to XML_Util's Entity replacement behavior of "only replace XML entities" |
XML_SERIALIZER_ENTITIES_XML_REQUIRED | XML_UTIL_ENTITIES_XML_REQUIRED | maps to XML_Util's Entity replacement behavior of "only replace required XML entities" |
XML_SERIALIZER_ENTITIES_HTML | XML_UTIL_ENTITIES_HTML | maps to XML_Util's Entity replacement behavior of "only replace HTML entities" |
XML_SERIALIZER_OPTION_INDENT | indent | OPTION constant for 'indent' option |
XML_SERIALIZER_OPTION_LINEBREAKS | linebreak | OPTION constant for 'linebreak' option |
XML_SERIALIZER_OPTION_TYPEHINTS | typeHints | OPTION constant for 'typeHints' option |
XML_SERIALIZER_OPTION_XML_DECL_ENABLED | addDecl | OPTION constant for 'addDecl' option |
XML_SERIALIZER_OPTION_XML_ENCODING | encoding | OPTION constant for 'encoding' option |
XML_SERIALIZER_OPTION_DEFAULT_TAG | defaultTagName | OPTION constant for 'defaultTagName' option |
XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME | classAsTagName | OPTION constant for 'classAsTagName' option |
XML_SERIALIZER_OPTION_ATTRIBUTE_KEY | keyAttribute | OPTION constant for 'keyAttribute' option |
XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE | typeAttribute | OPTION constant for 'typeAttribute' option |
XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS | classAttribute | OPTION constant for 'classAttribute' option |
XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES | scalarAsAttributes | OPTION constant for 'scalarAsAttributes' option |
XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES | prependAttributes | OPTION constant for 'prependAttributes' option |
XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES | indentAttributes | OPTION constant for 'indentAttributes' option |
XML_SERIALIZER_OPTION_MODE | mode | OPTION constant for 'mode' option |
XML_SERIALIZER_OPTION_DOCTYPE_ENABLED | addDoctype | OPTION constant for 'addDoctype' option |
XML_SERIALIZER_OPTION_DOCTYPE | doctype | OPTION constant for 'doctype' option |
XML_SERIALIZER_OPTION_ROOT_NAME | rootName | OPTION constant for 'rootName' option |
XML_SERIALIZER_OPTION_ROOT_ATTRIBS | rootAttributes | OPTION constant for 'rootAttributes' option |
XML_SERIALIZER_OPTION_ATTRIBUTES_KEY | attributesArray | OPTION constant for 'attributesArray' option |
XML_SERIALIZER_OPTION_CONTENT_KEY | contentName | OPTION constant for 'contentName' option |
XML_SERIALIZER_OPTION_COMMENT_KEY | commentName | OPTION constant for 'commentName' option |
XML_SERIALIZER_OPTION_TAGMAP | tagMap | OPTION constant for 'tagMap' option |
XML_SERIALIZER_OPTION_ENCODE_FUNC | encodeFunction | OPTION constant for 'encodeFunction' option |
XML_SERIALIZER_OPTION_NAMESPACE | namespace | OPTION constant for 'namespace' option |
XML_SERIALIZER_OPTION_ENTITIES | replaceEntities | OPTION constant for 'replaceEntities' option |
XML_SERIALIZER_OPTION_RETURN_RESULT | returnResult | OPTION constant for 'returnResult' option |
XML_SERIALIZER_OPTION_IGNORE_NULL | ignoreNull | OPTION constant for 'ignoreNull' option |
XML_SERIALIZER_OPTION_CDATA_SECTIONS | cdata | OPTION constant for 'cdata' option |
XML_SERIALIZER_OPTION_FALSE_AS_STRING | falseAsString | OPTION constant for 'falseAsString' option |
Name | Value | Notes |
---|---|---|
XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION | 151 | error code returned by unserialize() when it did not succeed |
XML_UNSERIALIZER_OPTION_COMPLEXTYPE | complexType | OPTION constant for 'complexType' option |
XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY | keyAttribute | OPTION constant for 'keyAttribute' option |
XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE | typeAttribute | OPTION constant for 'typeAttribute' option |
XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS | classAttribute | OPTION constant for 'classAttribute' option |
XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME | tagAsClass | OPTION constant for 'tagAsClass' option |
XML_UNSERIALIZER_OPTION_DEFAULT_CLASS | defaultClass | OPTION constant for 'defaultClass' option |
XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE | parseAttributes | OPTION constant for 'parseAttributes' option |
XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY | attributesArray | OPTION constant for 'attributesArray' option |
XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND | prependAttributes | OPTION constant for 'prependAttributes' option |
XML_UNSERIALIZER_OPTION_CONTENT_KEY | contentName | OPTION constant for 'contentName' option |
XML_UNSERIALIZER_OPTION_TAG_MAP | tagMap | OPTION constant for 'tagMap' option |
XML_UNSERIALIZER_OPTION_FORCE_ENUM | forceEnum | OPTION constant for 'forceEnum' option |
XML_UNSERIALIZER_OPTION_ENCODING_SOURCE | encoding | OPTION constant for 'encoding' option |
XML_UNSERIALIZER_OPTION_ENCODING_TARGET | targetEncoding | OPTION constant for 'targetEncoding' option |
XML_UNSERIALIZER_OPTION_DECODE_FUNC | decodeFunction | OPTION constant for 'decodeFunction' option |
XML_UNSERIALIZER_OPTION_RETURN_RESULT | returnResult | OPTION constant for 'returnResult' option |
XML_UNSERIALIZER_OPTION_WHITESPACE | whitespace | OPTION constant for 'whitespace' option |
XML_UNSERIALIZER_WHITESPACE_KEEP | keep | OPTION constant for 'keep' option |
XML_UNSERIALIZER_WHITESPACE_TRIM | trim | OPTION constant for 'trim' option |
XML_UNSERIALIZER_WHITESPACE_NORMALIZE | normalize | OPTION constant for 'normalize' option |
XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS | overrideOptions | OPTION constant for 'overrideOptions' option |
XML_UNSERIALIZER_OPTION_IGNORE_KEYS | ignoreKeys | OPTION constant for 'ignoreKeys' option |
XML_UNSERIALIZER_OPTION_GUESS_TYPES | guessTypes | OPTION constant for 'guessTypes' option |
This class can be used in two modes:
Create an XML document from an array or object that is processed by other applications.
This classes can be used to serialize any data structure in a way that it can later be unserialized again. XML_Serializer will store the type of the value and additional meta information in attributes of the surrounding tag. This meta information can later be used to restore the original data structure in PHP.
void constructor XML_Serializer::XML_Serializer (
mixed $options
= null
)
This package is not documented yet.
$options
array containing options for the serialization
throws no exceptions thrown
This function can not be called statically.
string XML_Serializer::apiVersion (
)
This package is not documented yet.
returns API version
throws no exceptions thrown
static
This function can not be called statically.
boolean XML_Serializer::serialize (
mixed $data
, mixed $options
= null
)
This package is not documented yet.
$data
data to serialize
$options
returns true on success, pear error on failure
throws no exceptions thrown
This function can not be called statically.
string XML_Serializer::getSerializedData (
)
This package is not documented yet.
throws no exceptions thrown
This function can not be called statically.
void XML_Serializer::resetOptions (
)
This package is not documented yet.
throws no exceptions thrown
see XML_Serializer::setOption(), XML_Unserializer()
This function can not be called statically.
void XML_Serializer::setOption (
mixed $name
, mixed $value
)
You can use this method if you do not want to set all options in the constructor
$name
$value
throws no exceptions thrown
see resetOption(), XML_Serializer::XML_Serializer()
This function can not be called statically.
Example that uses the returnResult option to directly return the serialized XML document in the serialize() method.
In this example look at theses 3 lines.
<?php
$serializer = &new XML_Serializer($options);
$foo = PEAR::raiseError('Just a test', 1234);
$result = $serializer->serialize($foo);
?>
<?php
error_reporting(E_ALL);
require_once 'XML/Serializer.php';
$options = array(
XML_SERIALIZER_OPTION_INDENT => ' ',
XML_SERIALIZER_OPTION_RETURN_RESULT => true
);
$serializer = &new XML_Serializer($options);
$foo = PEAR::raiseError('Just a test', 1234);
$result = $serializer->serialize($foo);
echo '<pre>';
echo htmlspecialchars($result);
echo '</pre>';
?>
And this is the result
<pear_error> <error_message_prefix /> <mode>1</mode> <level>1024</level> <code>1234</code> <message>Just a test</message> <userinfo /> <backtrace> <XML_Serializer_Tag> <file>pathToMypear\PEAR.php</file> <line>566</line> <function>pear_error</function> <class>pear_error</class> <type>-></type> <args> <XML_Serializer_Tag>Just a test</XML_Serializer_Tag> <XML_Serializer_Tag>1234</XML_Serializer_Tag> <XML_Serializer_Tag>1</XML_Serializer_Tag> <XML_Serializer_Tag>1024</XML_Serializer_Tag> <XML_Serializer_Tag /> </args> </XML_Serializer_Tag> <XML_Serializer_Tag> <file>pathToMyDocRoot\cvs.php.net\pear\xml_serializer\examples\serializeandreturn.php</file> <line>19</line> <function>raiseerror</function> <class>pear</class> <type>::</type> <args> <XML_Serializer_Tag>Just a test</XML_Serializer_Tag> <XML_Serializer_Tag>1234</XML_Serializer_Tag> </args> </XML_Serializer_Tag> </backtrace> <callback /> </pear_error>
You can find the last version of this source code in the package : serializeAndReturn.php
This example shows how to create an RDF document with a few lines of code. This can also be done with mode => simplexml.
In this example look at theses 3 lines.
<?php
$serializer = new XML_Serializer($options);
$result = $serializer->serialize($rdf);
echo htmlentities($serializer->getSerializedData());
?>
<?php
/**
* @see serializeIndexedArray.php
*/
error_reporting(E_ALL);
require_once 'XML/Serializer.php';
$options = array(
"indent" => " ",
"linebreak" => "\n",
"typeHints" => false,
"addDecl" => true,
"encoding" => "UTF-8",
"rootName" => "rdf:RDF",
"rootAttributes" => array("version" => "0.91"),
"defaultTagName" => "item",
"attributesArray" => "_attributes"
);
$serializer = new XML_Serializer($options);
$rdf = array(
"channel" => array(
"title" => "Example RDF channel",
"link" => "http://www.php-tools.de",
"image" => array(
"title" => "Example image",
"url" => "http://www.php-tools.de/image.gif",
"link" => "http://www.php-tools.de"
),
"_attributes" => array( "rdf:about" => "http://example.com/foobar.html" ),
array(
"title" => "Example item",
"link" => "http://example.com",
"_attributes" => array( "rdf:about" => "http://example.com/foobar.html" )
),
array(
"title" => "Another item",
"link" => "http://example.com",
"_attributes" => array( "rdf:about" => "http://example.com/foobar.html" )
),
array(
"title" => "I think you get it...",
"link" => "http://example.com",
"_attributes" => array( "rdf:about" => "http://example.com/foobar.html" )
)
)
);
$result = $serializer->serialize($rdf);
if( $result === true ) {
echo "<pre>";
echo htmlentities($serializer->getSerializedData());
echo "</pre>";
}
?>
And this is the result
<?xml version="1.0" encoding="UTF-8"?> <rdf:RDF version="0.91"> <channel rdf:about="http://example.com/foobar.html"> <title>Example RDF channel</title> <link>http://www.php-tools.de</link> <image> <title>Example image</title> <url>http://www.php-tools.de/image.gif</url> <link>http://www.php-tools.de</link> </image> <item rdf:about="http://example.com/foobar.html"> <title>Example item</title> <link>http://example.com</link> </item> <item rdf:about="http://example.com/foobar.html"> <title>Another item</title> <link>http://example.com</link> </item> <item rdf:about="http://example.com/foobar.html"> <title>I think you get it...</title> <link>http://example.com</link> </item> </channel> </rdf:RDF>
You can find the last version of this source code in the package : serializeRDF.php
Note : if you search how to parse (read) an RDF/RSS document, look at XML_RSS package.
This example shows how to add a DocType Declaration to the XML document
In this example look at theses 3 lines.
<?php
$serializer = new XML_Serializer($options);
$result = $serializer->serialize($rdf);
echo htmlentities($serializer->getSerializedData());
?>
<?php
error_reporting(E_ALL);
require_once 'XML/Serializer.php';
$options = array(
"indent" => " ",
"linebreak" => "\n",
"addDecl" => true,
"addDoctype" => true,
"doctype" => array(
'uri' => 'http://pear.php.net/dtd/package-1.0',
'id' => '-//PHP//PEAR/DTD PACKAGE 0.1'
)
);
$serializer = new XML_Serializer($options);
$foo = PEAR::raiseError("Just a test", 1234);
$result = $serializer->serialize($foo);
if( $result === true ) {
echo '<pre>';
echo htmlentities($serializer->getSerializedData());
echo '</pre>';
}
?>
And this is the result
<?xml version="1.0"?> <!DOCTYPE pear_error PUBLIC "-//PHP//PEAR/DTD PACKAGE 0.1" "http://pear.php.net/dtd/package-1.0"> <pear_error> <error_message_prefix /> <mode>1</mode> <level>1024</level> <code>1234</code> <message>Just a test</message> <userinfo /> <backtrace> <XML_Serializer_Tag> <file>pathToMyPear\PEAR.php</file> <line>566</line> <function>pear_error</function> <class>pear_error</class> <type>-></type> <args> <XML_Serializer_Tag>Just a test</XML_Serializer_Tag> <XML_Serializer_Tag>1234</XML_Serializer_Tag> <XML_Serializer_Tag>1</XML_Serializer_Tag> <XML_Serializer_Tag>1024</XML_Serializer_Tag> <XML_Serializer_Tag /> </args> </XML_Serializer_Tag> <XML_Serializer_Tag> <file>pathToMyDocumentRoot\cvs.php.net\pear\xml_serializer\examples\serializewithdtd.php</file> <line>24</line> <function>raiseerror</function> <class>pear</class> <type>::</type> <args> <XML_Serializer_Tag>Just a test</XML_Serializer_Tag> <XML_Serializer_Tag>1234</XML_Serializer_Tag> </args> </XML_Serializer_Tag> </backtrace> <callback /> </pear_error>
You can find the last version of this source code in the package : serializeWithDTD.php
This tutorial has been written by cnb and been published on http://freedomink.org/node/62. The site is offline since 2005 and has been rescued using the Internet Archive's Wayback Machine.
Here's how you can create an XML news feed in just five minutes using PHP, PEAR and the PEAR XML_Serializer package by Stephan Schmidt.
What a news feed consists of in it's barest form is your "site name", "site url" and a "listing of stories" from your site. To create a news feed what we need to do is create a publically accessible (web) document which can show this information in a format other sites can understand. This is possible by creating this document in the standard RDF Site Summary (RSS) format. A format used by many sites.
What we are aiming at is creating a document which looks like the following:
This isn't "well formed" XML but a stripped version making it as simple as possible for illustrative purposes. Once we understand the basics we can move on to create well formed XML.
<?xml version="1.0" encoding="UTF-8"?> <rdf:RDF> <channel> <title>Freedom Ink</title> <link>http://freedomink.org/</link> <item> <item> <title>First Article</title> <link>http://freedomink.org/node/view/55</link> <description>Short blurb about article........</description> </item> <item> <title>Second Article</title> <link>http://freedomink.org/node/view/11</link> <description>This article shows you how ......</description> </item> </item> </channel> </rdf:RDF>
Here's an easy way to do it in PHP.
Prerequisite: Install PEAR and the XML_Serializer package.
Include the following default options at the top of your php page.
<?php
require_once 'XML/Serializer.php';
$options = array(
"indent" => " ",
"linebreak" => "\n",
"typeHints" => false,
"addDecl" => true,
"encoding" => "UTF-8",
"rootName" => "rdf:RDF",
"defaultTagName" => "item"
);
?>
Let us first create an array containing a listing of the stories on your site.
We create array $stories
and add one story.
<?php
$stories[] = array(
'title' => 'First Article',
'link' => 'http://freedomink.org/node/view/55',
'description' => 'Short blurb about article........'
);
?>
You can add more stories to the array in the same manner. Ideally this should be done in a "for" or "foreach" loop.
<?php
$stories[] = array(
'title' => 'Second Article',
'link' => 'http://freedomink.org/node/view/11',
'description' => 'This article shows you how ......'
);
?>
Finally we specify the channel details and add the stories to it.
<?php
$data['channel'] = array(
"title" => "Freedom Ink",
"link" => "http://freedomink.org/",
$stories
);
?>
Now we generate the XML using the PEAR XML_Serializer package.
<?php
$serializer = new XML_Serializer($options);
if ($serializer->serialize($data)) {
header('Content-type: text/xml');
echo $serializer->getSerializedData();
}
?>
And that's it! When a person visits the page containing this code an XML file like the one we saw above will be generated and served.
To learn how to parse XML/RSS files you can read this related article which has by far the easiest method of parsing XML/RSS documents.
Class to unserialize XML documents that have been created with XML_Serializer. To unserialize an XML document you have to add type hints to the XML_Serializer options.
If no type hints are available, XML_Unserializer will guess how the tags should be treated, that means complex structures will be arrays and tags with only CData in them will be strings.
Options let you influence how XML_Unserializer treats the parsed XML. It allows you to define whether attributes should be parsed, whether to use associative arrays or objects for complex data types, and more.
Options can be passed as an associative array to the constructor of XML_Unserializer. You may also use setOption(), or setOptions() to set one or more options after the instance of XML_Unserializer has been created.
Here is a list of all options supported by XML_Unserializer.
XML_UNSERIALIZER_OPTION_ Name | Alternate Key for $options Array | Possible values | Default | Description |
---|---|---|---|---|
_COMPLEXTYPE | complexType | 'array' or 'object' | 'array' | Defines whether nested tags should be returned as associative arrays or objects. |
_TAG_AS_CLASSNAME | tagAsClass | TRUE or FALSE | TRUE | Defines whether the tag name should be used as the class name if complexType is set to 'object'. If no class with the name of the tag exists, the class defined by 'defaultClass' is used. |
_DEFAULT_CLASS | defaultClass | any string | stdClass | Defines the class that is used to create objects, if the complexType option is set to 'object'. |
_ATTRIBUTE_KEY | keyAttribute | any string or array | '_originalKey' | If the attribute specified in this option exists for a tag, the value will be used as key or property name in the parent object or array. You may also specify an associative array if you want to use different key attributes for different tags. In this case, the array key contains the tag name and the array value the corresponding attribute name. |
_ATTRIBUTE_TYPE | typeAttribute | any string | '_type' | If this attribute exists for a tag, the tag content will be converted to the specified type. Possible types are: string, integer, float, boolean, array, and object. |
_ATTRIBUTE_CLASS | classAttribute | any string | '_class' | If XML_Unserializer creates an object, it will be an instance of the value specified with the defaultClass option, unless the tag has the attribute specified in this option. If it is set, the classname stored in the attribute value will be used. |
_ATTRIBUTES_PARSE | parseAttributes | TRUE or FALSE | FALSE | With this option, you may tell XML_Unserializer to also parse the attributes of the tags. The next two options define how to treat the parsed attributes. |
_ATTRIBUTES_ARRAYKEY | attributesArray | FALSE or any string | FALSE | If set to false, the attributes will be treated like nested tags. If you set it to a string, a new aray will be created and stored in the parent structure using key you specified in this option. |
_ATTRIBUTES_PREPEND | prependAttributes | any string | '' | Allows you to specify a prefix for attribute names. |
_CONTENT_KEY | contentName | any string | '_content' | If you decide to parse attributes or a tag contains cdata and tags, then the cdata will be stored in the index specified here. |
_TAG_MAP | tagMap | associative array | array() | This allows you to map tag names to PHP classes. The names of the tags have to be in the keys and the values contain the class names to use for each tag. |
_FORCE_ENUM | forceEnum | indexed array | array() | This allows you to specify a list of tags which will automatically be converted to an indexed array, independent of the number of repetitions of the tag. This can save you some IF conditions in your code. |
_ENCODING_SOURCE | encoding | any valid encoding string (accepted by XML_Parser) | null | Defines the encoding of the original document. |
_ENCODING_TARGET | targetEncoding | any valid encoding string (accepted by XML_Parser) | null | Defines the target encoding of the resulting data. |
_DECODE_FUNC | decodeFunction | any valid PHP callback | null | This option allows you to define a callback function or method, that will be applied to all character data and attributes in the document before they are stored in the result. This allows you to decode any decoded data in the XML or convert all content to lowercase. |
_RETURN_RESULT | returnResult | TRUE or FALSE | FALSE | If set to TRUE XML_Unserializer::unserialize() will return the result if the document could be unserialized instead of just TRUE. |
_WHITESPACE | whitespace | OPTIONs _WHITESPACE_KEEP, _WHITESPACE_TRIM, or _WHITESPACE_NORMALIZE | _WHITESPACE_TRIM | Defines how whitespace in the document will be treated: keep it all, trim it, or normalize it. |
_IGNORE_KEYS | ignoreKeys | any array | array() | List of tags whose contents will be passed to the parent tag instead of creating a new tag. |
_GUESS_TYPES | guessTypes | TRUE or FALSE | FALSE | Whether or not to enable automatic type guessing for character data and attributes. |
_OVERRIDE_OPTIONS | overrideOptions | TRUE or FALSE | FALSE | Allows you to override the options already set on this unserializer object. |
The following examples shows how to set options for XML_Unserializer, using the available XML_UNSERIALIZER_OPTION constants.
Using the constructor
<?php
require_once "XML/Unserializer.php";
$options = array(
XML_UNSERIALIZER_OPTION_COMPLEXTYPE => 'array'
);
$us = new XML_Unserializer($options);
$result = $us->unserialize('example.xml', true);
?>
The following example shows how to set options for XML_Unserializer, using the available XML_UNSERIALIZER_OPTION constants, if the instance already has been created.
Using setOption() and setOptions()
<?php
require_once "XML/Unserializer.php";
$us = new XML_Unserializer();
$options = array(
XML_UNSERIALIZER_OPTION_TAG_MAP => array( 'util' => 'XML_Util' ),
XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS => '_classname'
);
$us->setOptions($options);
$us->setOption(XML_UNSERIALIZER_OPTION_COMPLEXTYPE, 'object');
$result = $us->unserialize('example.xml', true);
?>
The following example shows how to set options for XML_Unserializer, using an $options array.
Using the constructor
<?php
require_once "XML/Unserializer.php";
$options = array(
'complexType' => 'array'
);
$us = new XML_Unserializer($options);
$result = $us->unserialize('example.xml', true);
?>
The following example shows how to set options for XML_Unserializer, using an $options array, if the instance already has been created.
Using setOption() and setOptions()
<?php
require_once "XML/Unserializer.php";
$us = new XML_Unserializer();
$options = array(
'tagMap' => array( 'util' => 'XML_Util' ),
'classAttribute' => '_classname'
);
$us->setOptions($options);
$us->setOption('complexType', 'object');
$result = $us->unserialize('example.xml', true);
?>
XML_Unserializer::XML_Unserializer (
array $options
= null
)
Creates a new instance of XML_Unserializer.
array $options
- Options for the instance.
Can also be set using XML_Unserializer::setOption()
This function can not be called statically.
string XML_Unserializer::apiVersion (
)
Returns API version of XML_Unserializer. This may be useful to check if the needed functionality exists.
string API version
This function can be called statically.
boolean XML_Unserializer::unserialize (
string $data
, boolean $isFile
= false
, array $options
= null
)
Unserialize an XML document from a string or a file.
The way the document is unserialized is influenced by the options you set in the constructor or with setOptions().
string $data
- either the file name of an XML document or a string containing
the XML document.
boolean $isFile
- indicates whether the first parameter should
is a filename (TRUE) or an XML string (FALSE).
array $options
- Options to override the options that have been set previously.
The options will only be used for this unserialization and then reset the options you set before.
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
string XML_Unserializer::getUnserializedData (
)
This method returns the result of the last unserialization. You have to call XML_Unserializer::unserialize() first.
mixed result of the unserialization. May either be an array or an object.
throws PEAR_Error object, if not document has been parsed.
This function can not be called statically.
string XML_Unserializer::getRootName (
)
This method will return the name of the root tag, after a document has been unserialized. This can be extremely useful when returning an array. You have to call XML_Unserializer::unserialize() first.
string name of the root tag
throws PEAR_Error object, if not document has been parsed.
This function can not be called statically.
void XML_Unserializer::setOption (
string $name
, mixed $value
)
Options influence the behaviour of XML_Unserializer and are the most important part of XML_Unserializer.
You can use this method if you do not want to set all options in the constructor.
string $option
- name of the option
mixed $value
- value of the option
This function can not be called statically.
void XML_Unserializer::setOptions (
array $options
)
Options influence the behaviour of XML_Unserializer and are the most important part of XML_Unserializer.
You can use this method if you do not want to set all options in the constructor.
array $options
- assoc array containing all options
This function can not be called statically.
void XML_Unserializer::resetOptions (
)
Resets all XML_Unserializer options to the default options.
see XML_Unserializer::setOption() and XML_Unserializer::XML_Unserializer() on how to set options.
This function can not be called statically.
Translates SQL-Queries to XML-Resultsets.
XML_sql2xml takes an existing result set or a SQL statement and transforms it to an XML representation
In this tutorial, the examples refers to this database tables:
Let's start with an example using the default options.
The new instance is bind to an DSN, so you
have only to provide an SQL query. The instance
fetches the result automatically; in
$xmlstring
you found the
XML representation of the result set.
The simplest example
<?php
require_once "XML/sql2xml.php";
$sql2xmlclass = new xml_sql2xml("mysql://username:password@localhost/xmltest");
$xmlstring = $sql2xmlclass->getxml("select * from bands");
?>
The content of $xmlstring
based on the
DB tables above is:
If your query result base on joined tables, a nested XML
data structure can represent how the DBMS joins the
tables. To enable or to disable this behavoir
use
setOptions() with the
option key 'nested'
. The default
value is TRUE - the nesting is enabled.
Nested result set
<?php
$sql2xmlclass = new xml_sql2xml("mysql://username:password@localhost/xmltest");
$xmlstring = $sql2xmlclass->getxml("select * from bands left join albums on bands.id = bandsID");
?>
The generated XML output in $xmlstring
:
If you disable the nesting, the XML structure of rows is flat.
Unnested result sets
<?php
$sql2xmlclass = new xml_sql2xml("mysql://username:password@localhost/xmltest");
$options = array('nested' => false);
$sql2xmlclass->setOptions($options);
$xmlstring = $sql2xmlclass->getxml("select * from bands left join albums on bands.id = bandsID");
?>
XML output:
There are three ways to pass the data for transforming to the object:
with a direct query
This way you see in the first part of the introduction already.
with an existing DB_result object
If you need the full power of the PEAR::DB or PEAR::MDB API, you should choose this way.
with an array
This is not directly an SQL to XML transformation; if you pass an indice array to the instance, the keys of the array are transformed into XML tags, their values into the tag content. This feature his helpful for adding data to an XML transformed result set.
This behavoir does connecting to the DBMS, quering and fetching he result automatically. XML_sql2xml requires an valid DSN as construtor parameter. The query has to be passed to the getXML() or add() method.
Take a look into the first part of the introduction for examples.
PEAR::DB and PEAR::MDB return the result set of a query as DB_result object. You have to provide a DB_common instance to the construtor and the DB_result instance to getXML() or add().
Passing a DB_Result object
<?php
require_once "XML/sql2xml.php";
require_once "DB.php";
$db = db::connect("mysql://username:password@localhost/xmltest");
$result = $db->query("select * from bands");
$sql2xmlclass = new xml_sql2xml($db);
$xmlstring = $sql2xmlclass->getxml($result);
?>
The XML output in $xmlstring
is equal to the
example in
"The typical using".
This way is the only one, if you want to benefit from all the features of the database APIs.
If you pass an nested array to getXML() or add(), it will be transformed into an XML document.
Passing an array
<?php
require_once "XML/sql2xml.php";
$sql2xmlclass = new xml_sql2xml();
$array = array (
array("name"=>"The Blabbers",
"birth_year"=>"1998",
"birth_place"=>"London",
"genre"=>"Rock'n'Roll"),
array("name"=>"Only Stupids",
"birth_year"=>"1997",
"birth_place"=>"New York",
"genre"=>"hiphop")
);
$xmlstring = $sql2xmlclass->getXML($array);
?>
The XML output in $xmlstring
:
<?xml version="1.0"?>
<root>
<result>
<row>
<name>The Blabbers</name>
<birth_year>1998</birth_year>
<birth_place>London</birth_place>
<genre>Rock'n'Roll</genre>
</row>
<row>
<name>Only Stupids</name>
<birth_year>1997</birth_year>
<birth_place>New York</birth_place>
<genre>hiphop</genre>
</row>
</result>
</root>
You can call add() several result sets to XML document. Just call the method for every result set; if you have added all result sets, call getXml() without any arguments to get the XML document.
Adding result sets
<?php
$sql2xmlclass->add("select * from bands");
$sql2xmlclass->add("select * from albums");
$xmlstring= $sql2xmlclass->getxml();
?>
The XPath support was introduced to provide an access to the result set after doing the SQL query. This allows further proccessing of the result set without quering the database again.
XPath is a W3-Standard and for further information about that, ask your preferred XSL/XML book, or http://www.w3.org/.
XML_sql2xml provides two functions for querying the result set: getXpathValue() and getXpathChildValues(). Both expect a XPath query and return the result as string or array.
getXpathValue
<?php
include_once("XML/sql2xml.php");
$sql2xmlclass = new xml_sql2xml("mysql://username:password@localhost/xmltest");
$sql2xmlclass->add("select * from bands");
$xmlstring = $sql2xmlclass->getXpathValue("/root/result/row[id = '2']/name");
?>
$xmlstring
contains:
getXpathChildValues
<?php
include_once("XML/sql2xml.php");
$sql2xmlclass = new xml_sql2xml("mysql://username:password@localhost/xmltest");
$sql2xmlclass->add("select * from bands");
$xmlstring = $sql2xmlclass->getXpathChildValues("/root/result/row[id = '2']");
?>
$xmlstring
contains:
You can insert an XPath query into an SQL query. In the example, first
a SQL query is done, in the second a SQL query done again - but the
parameter for bandsID
is taken from
the XPath expression in the curly braces. This expression is processed on
the result set of the first SQL query.
Mixed query
<?php
include_once("XML/sql2xml.php");
$sql2xmlclass = new xml_sql2xml("mysql://username:password@localhost/xmltest");
$sql2xmlclass->add("select * from bands");
$sql2xmlclass->add("select * from albums where bandsID = {/root/result/row[name = 'The Blabbers']/id}");
$xmlstring = $sql2xmlclass->getxml();
?>
void XML_sql2xml::XML_sql2xml (
mixed $dsn
= null
,
string $root="root"
)
The Constructor can take a Pear::DB Data Source Name (DSN) and will then connect to the database; or a PEAR::DB object handle, if you already connected the database before. Providing sql-strings will not work.
If you provide only a DSN, you have to add a DB_result object, SQL statement or an array later.
The $root
parameter is used, if you want to provide another name for your
root-tag than <root>. If you give an empty string (""), there will be no
root element created here, but only when you add a resultset, array or
SQL Statement.
And the first tag of this result is used as the root tag.
mixed $dsn
-
PEAR::DB "data source name" or DB_common object
string $root
-
the name of the XML-root element.
This function can be called statically.
Using XML_sql2xml()
<?php
$sql2xml = new xml_sql2xml("mysql://root@localhost/xmltest");
$sql2xml->Add("select * from bands");
?>
void XML_sql2xml::add (
string $resultset
,
mixed $params
= null
)
General method for adding new result sets to the object. Give a SQL statement, a PEAR::DB_result object or an array as input parameter and the method calls the appropriate method for this input.
string $resultset
-
SQL statement, an DB_result object or an array
mixed $params
-
parameters for the following functions
This function can not be called statically.
XML_sql2xml::addResult(), XML_sql2xml::addSql(), XML_sql2xml::addArray(), XML_sql2xml::addXmlFile()
Using add()
<?php
$sql2xml->Add("select * from bands");
?>
void XML_sql2xml::addXmlFile (
string $file
,
mixed $xpath
= null
)
Adds the content of a XML file on the same level as a normal result set (mostly just below <root>).
string $file
-
file name
mixed $xpath
-
either a string with the XPath expression or an array with the keys
"xpath"=>XPath expression and "root"=>tag/subtag/etc,
which are the tags to be inserted before the result.
This function can not be called statically.
void XML_sql2xml::addXmlString (
string $string
,
mixed $xpath
= null
)
Adds the content of a xml-string on the same level as a normal result set (mostly just below <root>)
string $string
-
XML string
mixed $xpath
-
either a string with the XPath expression or an array with the keys
"xpath"=>XPath expression
and "root"=>tag/subtag/etc, which are the tags to be inserted
before the result.
This function can not be called statically.
void XML_sql2xml::addResult (
Object $result
)
Adds an additional PEAR::DB_result result set
This function can not be called statically.
void XML_sql2xml::addSql (
string $sql
)
Adds an aditional result set generated from an SQL statement. The function executes the statement and transform it into XML. You have to pass an DSN to the constructor.
string $sql
-
a string containing an SQL statement.
This function can not be called statically.
void XML_sql2xml::addArray (
array $array
)
Adds an aditional result set generated from an array. The XML represents the nesting of the array.
array $array
-
data array
This function can not be called statically.
string XML_sql2xml::getXML (
mixed $result
= null
)
Returns an XML string with a XML representation of the result set. The result set can be directly provided here, or if you need more than one in your XML, then you have to provide each of them with add() before you call getXML(), but the last one can also be provided here.
string
- the XML string
This function can not be called statically.
Object XML_sql2xml::getXMLObject (
mixed $result
= null
)
Returns an XML DomDocument object with a XML representation of the result sets. The result set can be directly provided here, or if you need more than one in your XML, then you have to provide each of them with add() before you call getXMLObject(), but the last one can also be provided here.
Object
- DomDocument object
This function can not be called statically.
void XML_sql2xml::setOptions (
array $options
,
constant $delete
= false
)
This method sets the options for the class instance.
array $options
-
array of options. The array key defines the option to set.
Avaible options are:
boolean $options['nested']
-
if TRUE the result of an join query will be nested. The default
value is FALSE.
boolean $options['tagNameRow']
-
name for the row mark up tag. Default is >row<
boolean $options['tagNameResult']
-
name for the result-set mark up tag. Default is >result<
constant $delete
-
the old sub options should be deleted
This function can not be called statically.
void XML_sql2xml::setEncoding (
string $encoding_from="ISO-8859-1"
,
string $encoding_to="UTF-8"
)
Sets the encoding for the db2xml transformation. The
$encoding_from
value depends on
your database system; check the DBMS manual which encoding
your system use to deliever result sets.
The $encoding_to
sets the
charset for the created XML document.
string $encoding_from
-
encoding to transform from
string $encoding_to
-
encoding to transform to
This function can not be called statically.
mixed XML_sql2xml::getXpathValue (
string $expr
)
Returns the content of the first match of the XPath expression.
string $expr
-
XPath expression
mixed
- content of the evaluated XPath expression
This function can not be called statically.
array XML_sql2xml::getXpathChildValues (
string $expr
)
Returns the values from the child tags from the first match of the XPath.
string $expr
-
XPath expression
array
- the content of the child tags.
The child tag name is assigned as key, the content as value.
This function can not be called statically.
Package to statistically analyze an XML document.
XML_Statistics is a package that allows you to analyze XML documents, which can be files or strings.
XML_Statistics is able to count tags, attributes, processing instructions, external entities and cdata blocks. Furthermore you can apply filters so you are able to count only the tags in a specific depth or only the attributes of a specific tag.
The following examples shows how XML_Statistics can be used to analyze a document.
Basic example
<?php
require_once "XML/Statistics.php";
$stat = new XML_Statistics(array("ignoreWhitespace" => true));
$result = $stat->analyzeFile("example.xml");
if ($stat->isError($result)) {
die("Error: " . $result->getMessage());
}
// total amount of tags:
echo "Total tags: " . $stat->countTag() . "<br />";
// count amount of 'title' attribute, in all tags
echo "Occurences of attribute title: " . $stat->countAttribute("title") . "<br />";
// count amount of 'title' attribute, only in <section> tags
echo "Occurences of attribute title in tag section: " . $stat->countAttribute("title", "section") . "<br />";
// count total number of tags in depth 4
echo "Amount of Tags in depth 4: " . $stat->countTagsInDepth(4) . "<br />";
echo "Occurences of PHP Blocks: " . $stat->countPI("PHP") . "<br />";
echo "Occurences of external entity 'bar': " . $stat->countExternalEntity("bar") . "<br />";
echo "Data chunks: " . $stat->countDataChunks() . "<br />";
echo "Length of all data chunks: " . $stat->getCDataLength() . "<br />";
?>
string XML_Statistics::apiVersion (
)
Returns API version of XML_Statistics.
string API version
This function can be called statically.
string XML_Statistics::analyzeFile (
string $filename
)
Analyzes an XML document by reading from a file. You have to analyze a document before you can extract any statistical information.
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
string XML_Statistics::analyzeString (
string $string
)
Analyzes an XML document directly from a string. This can be useful if your documents are created on the fly by any other application. You have to analyze a document before you can extract any statistical information.
Returns TRUE on success, PEAR_Error on failure.
This function can not be called statically.
integer XML_Statistics::countTag (
string $tagname = null
)
Counts how often a certain tag is used in the document. If no tagname is specified ot will count the total number of tags.
string $tagname
- name of the tag to count
integer occurences of the tag
This function can not be called statically.
integer XML_Statistics::counttagsindepth (
integer $depth
)
Thru nesting a tag can be at a certain depth. The root tag is at depth zero. By counting the amount of tags in a depth you can count the number of recordsets in an XML document.
integer $depth
- tag depth (0 is root)
integer number of tags in this depth
This function can not be called statically.
integer XML_Statistics::countPI (
string $target = null
)
Counts how often a certain processing instruction (e.g. <?PHP) is used in the document. The target is the 'language' of the processing instruction. You only need to specify the name, without the leading question mark. If no target is specified it will count the total number of processing instructions.
string $target
- target of the processing instruction
integer occurences of the processing instruction
This function can not be called statically.
integer XML_Statistics::countExternalEntity (
string $name = null
)
Counts how often a certain external is used in the document. If no name is specified ot will count the total number of external entities.
string $name
- name of the external entity, without '&' and ';'
integer occurences of the external entity
This function can not be called statically.
integer XML_Statistics::getMaxDepth (
)
The nesting level of a document tells you the maximum depth of the tags in the document.
This method does not accept any parameters.
integer maximum nesting level.
This function can not be called statically.
integer XML_Statistics::countAttribute (
string $attribute = null
, string $tagname = null
)
Counts how often a certain attribute is used in the document. If no attribute is specified ot will count the total number of tags.
With the second parameter you may limit the search to a special tag.
string $attribute
- if no attribute name is supplied, all attributes are counted
string $tagname
- this allows you to limit your search to a tag
integer occurences of the attribute or a PEAR_Error if the attribute could not be found.
This function can not be called statically.
integer XML_Statistics::countDataChunks (
)
This is influenced by the 'ignoreWhitespace' option. Furthermore a new chunk is counted when the parser find a linebreak or internal entity.
This method does not accept any parameters.
integer number of data chunks in the document
This function can not be called statically.
integer XML_Statistics::getCDataLength (
)
If you need to know how many chars you have between your tags, this is the method that gets it.
This method does not accept any parameters.
integer total length of all CData blocks in the XML document
This function can not be called statically.
XML Transformations in PHP.
With the XML Transformer class one can easily bind PHP functionality to XML tags, thus transforming the input XML tree into an output XML tree without the need for XSLT. Single XML elements can be overloaded with PHP functions, methods and static method calls, XML namespaces can be registered to be handled by PHP classes.
void XML_Transformer::XML_Transformer (
array parameters
)
Constructor of the XML_Transformer class. It can be passed an associative array that may have the following keys:
void XML_Transformer::overloadElement (
string element
, string startHandler
, string endHandler
, boolean recursiveOperation
)
Overloads an XML element and binds its opening and closing tags to a PHP callback. A PHP callback can be one of the following:
The optional fourth parameter can be used to override the global setting for recursive operation.
void XML_Transformer::overloadNamespace (
string namespacePrefix
, object object
, boolean recursiveOperation
)
Overloads an XML Namespace and binds all its elements to a PHP object, that must provide startElement($element, $attributes)() and endElement($element, $cdata)() methods.
If the PHP object provides a initObserver($namespacePrefix, $transformer)() method it is automatically called with the registered namespace prefix and a reference to the XML_Transformer object.
boolean XML_Transformer::isOverloadedElement (
string element
)
Returns TRUE if the given element is overloaded, FALSE otherwise.
boolean XML_Transformer::isOverloadedNamespace (
string namespacePrefix
)
Returns TRUE if the given namespace is overloaded, FALSE otherwise.
boolean XML_Transformer::unOverloadElement (
string element
)
Reverts overloading of the given element.
boolean XML_Transformer::unOverloadNamespace (
string namespacePrefix
)
Reverts overloading of the given namespace.
void XML_Transformer::start (
)
Starts the transformation, if it has not yet been started, for instance by the constructor.
string XML_Transformer::transform (
string xml
)
Transforms a given XML string.
void XML_Transformer::setCaseFolding (
boolean caseFolding
, integer caseFoldingTo
)
Sets the XML parser's case-folding option.
void XML_Transformer::setDebug (
mixed debug
)
Enables or disables debugging to error.log.
The parameter may be either TRUE or FALSE, thus enabling or disabling complete debugging information. May also be an array containing the names of elements to which the generated debugging information shall be limited. The special keywords "&CDATA" and "&RECURSE" may be used to enable debugging information for CDATA and recursion events.
void XML_Transformer::setRecursiveOperation (
boolean recursiveOperation
)
Enables or disables the recursive operation.
string XML_Transformer::stackdump (
)
Returns a stack dump as a debugging aid.
string XML_Transformer::attributesToString (
array attributes
)
Returns string representation of attributes array.
mixed XML_Transformer::canonicalName (
mixed target
)
Canonicalizes XML attribute arrays and element names.
Build XML documents using a tree representation
The XML_Tree package allows one to build XML data structures using a tree representation, without the need for an extension like DOMXML.
void XML_Tree::XML_Tree (
string $filename=''
,
string $version='1.0'
)
Constructor
string $filename
-
name of the file to parse
string $version
-
XML version to use
This function can not be called statically.
object XML_Tree_Node XML_Tree::&addRoot (
string $name
,
string $content=''
,
array $attributes=array()
)
Adds a new root node to the XML datastructure.
string $name
-
name of root element
string $content
-
data between the opening and closing tag
array $attributes
-
additional attributes for the tag. The attribute name
is represented as the array key and the attribute value
as entry for the key.
object XML_Tree_Node
- reference to root node
This function can not be called statically.
Using &addRoot()
<?php
$tree = new XML_Tree;
$root =& $tree->addRoot("root");
?>
object XML_Tree_Node XML_Tree::&insertChild (
array $path
,
integer $pos
,
mixed $child
,
string $content=''
,
array $attributes=array()
)
Inserts a child or tree into the tree in the path
$path
on position $pos
and maintains namespace integrity
array $path
-
path to parent of child to insert
integer $pos
-
position of child to be inserted in its parent's children-list
mixed $child
-
child-node (by XML_Tree,
XML_Node or Name)
string $content
-
content (text) for new node
array $attributes
-
attribute-hash for new node
object XML_Tree_Node
- inserted child (node)
This function can not be called statically.
object XML_Tree_Node XML_Tree::&removeChild (
array $path
,
integer $pos
)
Removes a child ($path
,$pos
) from tree and maintains namespace integrity
array $path
-
path to parent of child to remove
integer $pos
-
position of child in parents children-list
object XML_Tree_Node
- parent whichs child was removed
This function can not be called statically.
object XML_Tree XML_Tree::&getTreeFromFile (
)
Maps a XML file to an object tree
object XML_Tree
- the object tree
This function can not be called statically.
object XML_Tree XML_Tree::clone (
)
Get a copy of this tree instance.
object XML_Tree
-
the copied object
This function can not be called statically.
void XML_Tree::dump (
)
Print text representation of XML tree.
This function can not be called statically.
string XML_Tree::&get (
)
Get text representation of XML tree.
string
- XML document
This function can not be called statically.
string XML_Tree::&getName (
string $name
)
Get the current namespace.
string $name
-
namespace
string
- namespace
This function can not be called statically.
void XML_Tree::registerName (
string $name
,
string $path
)
Registers a namespace.
string $name
-
namespace
string $path
-
path
This function can not be called statically.
Collection of often needed methods that help you creating XML documents.
XML_Util is a utility class that helps you working with (and especially creating) XML documents.
All methods of XML_Util can be called statically, that means you do not have to instantiate an XML_Util object to use the provided methods.
The funcionality of XML_Util ranges from validating an XML tag name (as there are strict rules for tag and attribute names) to the creation of namespaced XML tags.
The following examples shows how some of the methods of XML_Util have to be used.
Some basic examples
<?php
require_once "XML/Util.php";
// creating an XML tag
$tag = XML_Util::createTag("xsl:stylesheet", array("version" => "1.0"), "Place your content here", "http://www.w3.org/1999/XSL/Transform");
// verify tag name
$result = XML_Util::isValidName("my Tag");
if (PEAR::isError($result)) {
print "Invalid XML name: " . $result->getMessage();
} else {
print "Tagname is valid.";
}
?>
string XML_Util::apiVersion (
)
Returns API version of XML_Util.
string API version
This function should be called statically.
string XML_Util::attributesToString (
array $attributes
, boolean $sort
= true
, boolean $multiline
= false
, string $indent = ' '
, string $linebreak = "\n"
, integer $entities = XML_UTIL_ENTITIES_XML
)
create string representation of an attribute list
array $attributes
- assoc array containg attributes
boolean $sort
- whether to sort the attributes alphabetically
boolean $multiline
- whether to display the attributes on more than one line (makes it easier to read)
string $indent
- indentation characters, only used when multiline is set to TRUE
string $linebreak
- linebreak character, only used when multiline is set to TRUE
integer $entities
- define, which entities should be replaced in the attribute values. One of
XML_UTIL_ENTITIES_NONE, XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED or XML_UTIL_ENTITIES_HTML
string string representation of the attributes
This function should be called statically.
string XML_Util::collapseEmptyTags (
string $string
, integer $mode = XML_UTIL_COLLAPSE_ALL
)
This method collapses empty tags like <foo></foo> with the short version <foo/> by applying a regular expression. This is especially helpful when dealing with XHTML-documents, as there is an important difference in rendering these tags in the browser.
This method has been added in XML_Util 1.1.0.
string $string
- string, in which empty tags should be collapsed
integer $mode
- collapse all empty tags (XML_UTIL_COLLAPSE_ALL) or only
XHTML tags (XML_UTIL_COLLAPSE_XHTML_ONLY).
string string with collapsed empty tags
This function should be called statically.
string XML_Util::createTag (
string $qname
, array $attributes = array()
, string $content
= null
, string $namespaceUri
= null
, integer $replaceEntities = XML_UTIL_REPLACE_ENTITIES
)
create a tag with attributes, namespace and adds 'xmlns' if needed.
string $qname
- qualified tag name
array $attributes
- assoc array with attributes
string $content
- string content of the tag
string $namespaceUri
- URI of the namespace if xmlns attribute should be added
integer $replaceEntities
- whether to replace XML entities in content, embedd it in a CData section or leave it untouched.
Possible values are FALSE, XML_UTIL_REPLACE_ENTITIES or XML_UTIL_CDATA_SECTION.
string xml tag
This function should be called statically.
string XML_Util::createTagFromArray (
array $tag
, integer $replaceEntities = XML_UTIL_REPLACE_ENTITIES
)
create a tag from an array. This is similar to XML_Util::createTag(), but more flexible.
array $tag
- array containing information about the tag
integer $replaceEntities
- whether to replace XML entities in content, embedd it in a CData section or leave it untouched.
Possible values are FALSE, XML_UTIL_REPLACE_ENTITIES or XML_UTIL_CDATA_SECTION.
string xml tag
This function should be called statically.
Creating a tag with XML_Util::createTagFromArray()
<?php
require_once "XML/Util.php";
$tag = array(
"namespace" => "xslt",
"localPart" => "variable",
"namespaceUri" => "http://www.w3.org/1999/XSL/Transform",
"attributes" => array( "name" => "myVar" ),
"content" => "myValue"
);
$string = XML_Util::createTagFromArray($tag);
?>
string XML_Util::createStartElement (
string $qname
, array $attributes = array()
, string $namespaceUri
= null
)
create a start element with attributes, namespace and adds 'xmlns' if needed. (<pear foo="bar">)
string $qname
- qualified tag name
array $attributes
- assoc array with attributes
string $namespaceUri
- URI of the namespace if xmlns attribute should be added
string opening xml tag
This function should be called statically.
string XML_Util::createEndElement (
string $qname
)
create an end element (</foo>)
string $qname
- qualified tag name
string closing xml tag
This function should be called statically.
string XML_Util::createCDataSection (
string $data
)
Creates a CData section, by embedding the data in <!CDATA[ and ]]>. If you use a CData section inside an XML document, entities do not have to be replaced in this sections.
string $data
- data for the CData section.
string CData section
This function should be called statically.
string XML_Util::createComment (
string $data
)
Creates an XML comment, by embedding the supplied data in <!-- and -->.
string $data
- data for the comment.
string comment
This function should be called statically.
string XML_Util::getDocTypeDeclaration (
string $root
, mixed $uri
= null
, mixed $internalDtd
= null
)
Returns a document type declaration based on parameters.
string $root
- root tag
mixed $uri
- URI of the document type definition or array containing the public id and the URI of the DTD
mixed $internalDtd
- internal definitions
string document type declaration
This function should be called statically.
string XML_Util::getXMLDeclaration (
string $version = "1.0"
, string $encoding
= null
, boolean $standalone
= null
)
Returns an XML declaration based on parameters.
string $version
- XML version
string $encoding
- XML encoding
boolean $standalone
- whether document is standalone
string XML declaration
This function should be called statically.
boolean XML_Util::isValidName (
string $string
)
Checks, whether a string is a valid XML name. In XML the names of tags and attributes must follow strict rules. This method can be used to validate the names you are using.
string $string
- string to be checked
Returns TRUE on success, PEAR_Error on failure.
This function should be called statically.
string XML_Util::replaceEntities (
string $string
)
In XML documents you are not allowed to use & and <. The have to be replaced with their respective entities. This method does this for you and furthermore replaces all other predefined XML entities.
string $string
- string, in which entities have to be replaced
string string with entities replaced
This function should be called statically.
string XML_Util::reverseEntities (
string $string
)
This is the reverse function to XML_Util::replaceEntities()(). It replaces all XML (or HTML) entities in a string by their corresponding characters. This can be useful when working with XML documents without using an XML parser.
This method has been added in XML_Util 1.0.0.
string $string
- string, in which entities have to be removed
string string with entities removed
This function should be called statically.
array XML_Util::splitQualifiedName (
string $qname
, string $defaultNs
= null
)
Splits a qualified name and returns array with namespace and local part.
string $qname
- qualified tag name (e.g. xsl:stylsheet)
string $defaultNs
- default namespace, will be used in return value, if qualified name contains only a local part
array assoc array containing namespace and local part of the tag
This function should be called statically.
XML_XRD is a PHP library to parse and generate Extensible Resource Descriptor (XRD) Version 1.0 and JRD (JSON-based) files.
XRD files are used for .well-known/host-meta
files as standardized in
RFC 6415: Web Host Metadata
as well as in the
LRDD (Link-based Resource Descriptor Discovery)
files linked from it.
JRD files are used by WebFinger, which lets people use their e-mail address to do OpenID sign in.
The XRD format supercedes the XRDS format defined in XRI 2.0, which is used in the Yadis communications protocol.
At first, you need to include the main XML_XRD
file and create a new
XML_XRD object:
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
?>
When that's done, you load the XRD file or string, then verify if the resource is really the resource you wanted to load and then access the links and the properties of the XRD object.
Also read the sections about error handling and XRD creation.
Fetching LRDD URI from host-meta
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
try {
$xrd->loadFile('http://cweiske.de/.well-known/host-meta');
} catch (XML_XRD_Exception $e) {
die('Loading XRD file failed: ' . $e->getMessage());
}
$link = $xrd->get('lrdd', 'application/xrd+xml');
if ($link === null) {
die('No LRDD link found');
}
$template = $link->template;
$lrddUri = str_replace('{uri}', urlencode('acct:cweiske@cweiske.de'), $template);
echo 'URL with infos about cweiske@cweiske.de is ' . $lrddUri . "\n";
?>
XRD .xml
files may be loaded directly from a
file,
or from a
string:
Loading an XRD file
<?php
try {
$xrd->loadFile('/path/to/the/file.xrd', 'xml');
} catch (XML_XRD_Exception $e) {
die('Loading XRD file failed: ' . $e->getMessage() . "\n");
}
?>
Loading an XRD string
<?php
try {
$xrd->loadString($fullXrdXmlHere, 'xml');
} catch (XML_XRD_Exception $e) {
die('Loading XRD failed: ' . $e->getMessage() . "\n");
}
?>
XML_XRD tries to autodetect if the file is XML or JSON (JRD)
and use the correct loader.
You can make its life easier (and skip autodetection, and reduce network load)
by passing the type
parameter:
Loading a JRD file
<?php
try {
$xrd->loadFile('/path/to/the/file.jrd', 'json');
} catch (XML_XRD_Exception $e) {
die('Loading JRD file failed: ' . $e->getMessage() . "\n");
}
?>
After loading a XRD file, you should make sure that it really describes
the resource/URL you are looking for.
describes()
checks both the subject
and alias
tags:
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
$xrd->loadFile('http://example.org/.well-known/host-meta');
if (!$xrd->describes('http://example.org/')) {
die('XRD document is not the correct one for http://example.org/');
}
?>
The XML_XRD object offers three ways to access links:
Use foreach
to iterate over the object and get all links
get() to fetch a single link with certain properties
getAll() to fetch all links that have certain properties
The returned links are objects of type XML_XRD_Element_Link which has several properties, e.g. rel, href, type and template .
Get all links
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
$xrd->loadFile('http://example.org/.well-known/host-meta');
foreach ($xrd as $link) {
echo $link->rel . ': ' . $link->href . "\n";
}
?>
Get link by relation
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
$xrd->loadFile('http://example.org/.well-known/host-meta');
$idpLink = $xrd->get('lrdd');
echo $idpLink->rel . ': ' . $idpLink->href . "\n";
?>
Get link by relation + optional type
If no link with the given type
is found, the first link with the correct
relation
and an empty
type
will be returned
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
$xrd->loadFile('http://example.org/.well-known/host-meta');
$link = $xrd->get('lrdd', 'application/xrd+xml');
echo $link->rel . ': ' . $link->href . "\n";
?>
Get link by relation + type
The relation
and the type
both need to match exact:
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
$xrd->loadFile('http://example.org/.well-known/host-meta');
$link = $xrd->get('lrdd', 'application/xrd+xml', false);
echo $link->rel . ': ' . $link->href . "\n";
?>
Get all links by relation
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
$xrd->loadFile('http://example.org/.well-known/host-meta');
foreach ($xrd->getAll('lrdd') as $link) {
echo $link->rel . ': ' . $link->href . "\n";
}
?>
Accessing all link attributes
<?php
$link = $xrd->get('http://specs.openid.net/auth/2.0/provider');
$title = $link->getTitle('de');
$url = $link->href;
$urlTemplate = $link->template;
$mimetype = $link->type;
?>
Additional link properties
Works just like properties in the XRD document.
<?php
$link = $xrd->get('http://specs.openid.net/auth/2.0/provider');
$prop = $link['foo'];
?>
Properties of XRD files and link elements can be accessed by using its ArrayAccess interface or the getProperties() method. The returned properties are objects of type XML_XRD_Element_Property.
Get a single property
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
$xrd->loadFile('http://example.org/.well-known/host-meta');
if (isset($xrd['http://spec.example.net/type/person'])) {
echo $xrd['http://spec.example.net/type/person'] . "\n";
}
?>
Get all properties
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
$xrd->loadFile('http://example.org/.well-known/host-meta');
foreach ($xrd->getProperties() as $property) {
echo $property->type . ': ' . $property->value . "\n";
}
?>
Get all properties of a type
<?php
require_once 'XML/XRD.php';
$xrd = new XML_XRD();
$xrd->loadFile('http://example.org/.well-known/host-meta');
foreach ($xrd->getProperties('http://spec.example.net/type/person') as $property) {
echo $property->type . ': ' . $property->value . "\n";
}
?>
When loading a file, exceptions of type
XML_XRD_Exception
may be thrown.
All other parts of the code do not throw exceptions but fail gracefully by returning
null
, e.g. when a property does not exist.
Using loadFile()
may result in PHP warnings like:
Warning: simplexml_load_file(https://example.org/) failed to open stream: Connection refused
This cannot be prevented properly, so you either have to silence it with
@
or fetch the file manually and use
loadString()
.
Generating XRD files is made by creating an XML_XRD object, setting
the properties and calling the generation method
to()
with either xml
or json
as only
parameter.
<?php
require_once 'XML/XRD.php';
$x = new XML_XRD();
$x->subject = 'example.org';
$x->aliases[] = 'example.com';
$x->links[] = new XML_XRD_Element_Link(
'lrdd', 'http://example.org/gen-lrdd.php?a={uri}',
'application/xrd+xml', true
);
echo $x->to('xml');
?>
Generate a LRDD file
<?php
require_once 'XML/XRD.php';
$x = new XML_XRD();
$x->subject = 'user@example.org';
//add link to the user's OpenID
$x->links[] = new XML_XRD_Element_Link(
'http://specs.openid.net/auth/2.0/provider',
'http://id.example.org/user'
);
//add link to user's home page
$x->links[] = new XML_XRD_Element_Link(
'http://xmlns.com/foaf/0.1/homepage',
'http://example.org/~user/'
);
echo $x->to('xml');
?>