64
You want to maintain information about a user as she moves through your site.
8.6.2 Solution
Use the session module. The
session_start( )
function initializes a session, and accessing
an element in the global
$_SESSION
array tells PHP to keep track of the corresponding
variable.
session_start();
$_SESSION['visits']++;
print 'You have visited here '.$_SESSION['visits'].' times.';
8.6.3 Discussion
To start a session automatically on each request, set
session.auto_start
to
1
in php.ini.
With
session.auto_start
, there's no need to call
session_start( )
.
The session functions keep track of users by issuing them cookies with a randomly generated
session IDs. If PHP detects that a user doesn't accept the session ID cookie, it automatically
adds the session ID to URLs and forms.
[1]
For example, consider this code that prints a URL:
[1]
Before PHP 4.2.0, this behavior had to be explicitly enabled by building PHP
with the
--enable-trans-sid
configuration setting.
print '<a href="train.php">Take the A Train</a>';
If sessions are enabled, but a user doesn't accept cookies, what's sent to the browser is
something like:
<a href="train.php?PHPSESSID=2eb89f3344520d11969a79aea6bd2fdd">Take the A
Train</a>
In this example, the session name is
PHPSESSID
and the session ID is
2eb89f3344520d11969a79aea6bd2fdd
. PHP adds those to the URL so they are passed
along to the next page. Forms are modified to include a hidden element that passes the
session ID. Redirects with the
Location
header aren't automatically modified, so you have to
add a session ID to them yourself using the
SID
constant:
$redirect_url = 'http://www.example.com/airplane.php';
if (defined('SID') && (! isset($_COOKIE[session_name()]))) {
$redirect_url .= '?' . SID;
}
header("Location: $redirect_url");
The
session_name( )
function returns the name of the cookie that the session ID is stored
in, so this code appends the
SID
constant only to
$redirect_url
if the constant is defined,
and the session cookie isn't set.
57
By default, PHP stores session data in files in the /tmp directory on your server. Each session
is stored in its own file. To change the directory in which the files are saved, set the
session.save_path
configuration directive in php.ini to the new directory. You can also call
session_save_path( )
with the new directory to change directories, but you need to do
this before accessing any session variables.
8.6.4 See Also
Documentation on
session_start( )
at http://www.php.net/session-start
,
session_save_path( )
at http://www.php.net/session-save-path
; the session module has
a number of configuration directives that help you do things like manage how long sessions
can last and how they are cached; these are detailed in the "Sessions" section of the online
manual at http://www.php.net/session
.
Recipe 8.7 Storing Sessions in a Database
8.7.1 Problem
You want to store session data in a database instead of in files. If multiple web servers all
have access to the same database, the session data is then mirrored across all the web
servers.
8.7.2 Solution
Set
session.save_handler
to
user
in php.ini and use the
pc_DB_Session
class shown in
Example 8-1
. For example:
$s = new pc_DB_Session('mysql://user:password@localhost/db');
ini_get('session.auto_start') or session_start();
8.7.3 Discussion
One of the most powerful aspects of the session module is its abstraction of how sessions get
saved. The
session_set_save_handler( )
function tells PHP to use different functions for
the various session operations such as saving a session and reading session data. The
pc_DB_Session
class stores the session data in a database. If this database is shared
between multiple web servers, users' session information is portable across all those web
servers. So, if you have a bunch of web servers behind a load balancer, you don't need any
fancy tricks to ensure that a user's session data is accurate no matter which web server they
get sent to.
To use
pc_DB_Session
, pass a data source name (DSN) to the class when you instantiate it.
The session data is stored in a table called
php_session
whose structure is:
CREATE TABLE php_session (
id CHAR(32) NOT NULL,
data MEDIUMBLOB,
How to C#: Basic SDK Concept of XDoc.PDF for .NET You can generate thumbnail image(s) from PDF file for quick viewing and further to load a PDF document from file or query data and save the PDF document.
pdf add signature field; change font size pdf form reader
54
last_access INT UNSIGNED NOT NULL,
PRIMARY KEY(id)
)
If you want the table name to be different than
php_session
, set
session.save_path
in
php.ini to your new table name. Example 8-1
shows the
pc_DB_Session
class.
Example 8-1. pc_DB_Session class
require 'PEAR.php';
require 'DB.php';
class pc_DB_Session extends PEAR {
var $_dbh;
var $_table;
var $_connected = false;
var $_gc_maxlifetime;
var $_prh_read;
var $error = null;
/**
* Constructor
*/
function pc_DB_Session($dsn = null) {
if (is_null($dsn)) {
$this->error = PEAR::raiseError('No DSN specified');
return;
}
$this->_gc_maxlifetime = ini_get('session.gc_maxlifetime');
// Sessions last for a day unless otherwise specified.
if (! $this->_gc_maxlifetime) {
$this->_gc_maxlifetime = 86400;
}
$this->_table = ini_get('session.save_path');
if ((! $this->_table) || ('/tmp' == $this->_table)) {
$this->_table = 'php_session';
}
$this->_dbh = DB::connect($dsn);
if (DB::isError($this->_dbh)) {
$this->error = $this->_dbh;
return;
}
$this->_prh_read = $this->_dbh->prepare(
"SELECT data FROM $this->_table WHERE id LIKE ? AND last_access
>= ?");
if (DB::isError($this->_prh_read)) {
$this->error = $this->_prh_read;
return;
}
if (! session_set_save_handler(array(&$this,'_open'),
array(&$this,'_close'),
array(&$this,'_read'),
VB.NET PDF: Basic SDK Concept of XDoc.PDF You can generate thumbnail image(s) from PDF file for quick viewing and further to load a PDF document from file or query data and save the PDF document.
create a pdf form that can be filled out; change font in pdf fillable form
54
array(&$this,'_write'),
array(&$this,'_destroy'),
array(&$this,'_gc'))) {
$this->error = PEAR::raiseError('session_set_save_handler()
failed');
return;
}
return $this->_connected = true;
}
function _open() {
return $this->_connected;
}
function _close() {
return $this->_connected;
}
function _read($id) {
if (! $this->_connected) { return false; }
$sth =
$this->_dbh->execute($this->_prh_read,
array($id,time() - $this-
>_gc_maxlifetime));
if (DB::isError($sth)) {
$this->error = $sth;
return '';
} else {
if (($sth->numRows() == 1) &&
($ar = $sth->fetchRow(DB_FETCHMODE_ORDERED))) {
return $ar[0];
} else {
return '';
}
}
}
function _write($id,$data) {
$sth = $this->_dbh->query(
"REPLACE INTO $this->_table (id,data,last_access) VALUES
(?,?,?)",
array($id,$data,time()));
if (DB::isError($sth)) {
$this->error = $sth;
return false;
} else {
return true;
}
}
function _destroy($id) {
$sth = $this->_dbh->query("DELETE FROM $this->_table WHERE id LIKE
?",
array($id));
if (DB::isError($sth)) {
$this->error = $sth;
return false;
} else {
return true;
59
}
}
function _gc($maxlifetime) {
$sth = $this->_dbh->query("DELETE FROM $this->_table WHERE
last_access < ?",
array(time() - $maxlifetime));
if (DB::isError($sth)) {
$this->error = $sth;
return false;
} else {
return true;
}
}
}
The
pc_DB_Session::_write( )
method uses a MySQL-specific SQL command,
REPLACE
INTO
, which updates an existing record or inserts a new one, depending on whether there is
already a record in the database with the given
id
field. If you use a different database,
modify the
_write( )
function to accomplish the same task. For instance, delete the existing
row (if any), and insert a new one, all inside a transaction:
function _write($id,$data) {
$sth = $this->_dbh->query('BEGIN WORK');
if (DB::isError($sth)) {
$this->error = $sth;
return false;
}
$sth = $this->_dbh->query("DELETE FROM $this->_table WHERE id LIKE
?",
array($id));
if (DB::isError($sth)) {
$this->error = $sth;
$this->_dbh->query('ROLLBACK');
return false;
}
$sth = $this->_dbh->query(
"INSERT INTO $this->_table (id,data,last_access) VALUES
(?,?,?)",
array($id,$data,time()));
if (DB::isError($sth)) {
$this->error = $sth;
$this->_dbh->query('ROLLBACK');
return false;
}
$sth = $this->_dbh->query('COMMIT');
if (DB::isError($sth)) {
$this->error = $sth;
$this->_dbh->query('ROLLBACK');
return false;
}
return true;
}
8.7.4 See Also
59
Documentation on
session_set_save_handler( )
at http://www.php.net/session-set-
save-handler
; a handler using PostgreSQL is available at
http://www.zend.com/codex.php?id=456&single=1
; the format for data source names is
discussed in Recipe 10.4
.
Recipe 8.8 Detecting Different Browsers
8.8.1 Problem
You want to generate content based on the capabilities of a user's browser.
8.8.2 Solution
Use the object returned by
get_browser( )
to determine a browser's capabilities:
$browser = get_browser( );
if ($browser->frames) {
// print out a frame-based layout
} elseif ($browser->tables) {
// print out a table-based layout
} else {
// print out a boring layout
}
8.8.3 Discussion
The
get_browser( )
function examines the environment variable
$_ENV['HTTP_USER_AGENT']
(set by the web server) and compares it to browsers listed in
an external browser capability file. Due to licensing issues, PHP isn't distributed with a browser
capability file. The "Obtaining PHP" section of the PHP FAQ (http://www.php.net/faq.obtaining
)
lists http://www.cyscape.com/asp/browscap/
and http://www.amrein.
com/apps/page.asp?Q=InowDownload
as sources for a browser capabilities file, and there is
also one at http://asp.net.do/browscap.zip
.
Once you download a browser capability file, you need to tell PHP where to find it by setting
the
browscap
configuration directive to the pathname of the file. If you use PHP as a CGI, set
the directive in the php.ini file:
browscap=/usr/local/lib/browscap.txt
If you use Apache, you need to set the directive in your Apache configuration file:
php_value browscap "/usr/local/lib/browscap.txt"
Many of the capabilities
get_browser( )
finds are shown in Table 8-1
. For user-configurable
capabilities such as
javascript
or
cookies
though,
get_browser( )
just tells you if the
browser can support those functions. It doesn't tell you if the user has disabled the functions.
59
If JavaScript is turned off in a JavaScript-capable browser or a user refuses to accept cookies
when the browser prompts him,
get_browser( )
still indicates that the browser supports
those functions.
Table 8-1. Browser capability object properties
Property
Description
platform
Operating system the browser is running on (e.g., Windows, Macintosh,
UNIX, Win32, Linux, MacPPC)
version
Full browser version (e.g., 5.0, 3.5, 6.0b2)
majorver
Major browser version (e.g., 5, 3, 6)
minorver
Minor browser version (e.g., 0, 5, 02)
frames
1 if the browser supports frames
tables
1 if the browser supports tables
cookies
1 if the browser supports cookies
backgroundsounds
1 if the browser supports background sounds with
<embed>
or
<bgsound>
vbscript
1 if the browser supports VBScript
javascript
1 if the browser supports JavaScript
javaapplets
1 if the browser can run Java applets
activexcontrols
1 if the browser can run ActiveX controls
8.8.4 See Also
Documentation on
get_browser( )
at http://www.php.net/get-browser
.
Recipe 8.9 Building a GET Query String
8.9.1 Problem
You need to construct a link that includes name/value pairs in a query string.
8.9.2 Solution
Encode the names and values with
urlencode( )
and use
join( )
to create the query
string:
$vars = array('name' => 'Oscar the Grouch',
'color' => 'green',
'favorite_punctuation' => '#');
$safe_vars = array( );
foreach ($vars as $name => $value) {
$safe_vars[ ] = urlencode($name).'='.urlencode($value);
}
$url = '/muppet/select.php?' . join('&',$safe_vars);
64
8.9.3 Discussion
The URL built in the solution is:
/muppet/select.php?name=Oscar+the+Grouch&color=green&favorite_punctuation=%
23
The query string has spaces encoded as
+
. Special characters such as
#
are hex-encoded as
%23
because the ASCII value of
#
is 35, which is 23 in hexadecimal.
Although
urlencode( )
prevents any special characters in the variable names or values
from disrupting the constructed URL, you may have problems if your variable names begin
with the names of HTML entities. Consider this partial URL for retrieving information about a
stereo system:
/stereo.php?speakers=12&cdplayer=52&=10
The HTML entity for ampersand (
&
) is
&
so a browser may interpret that URL as:
/stereo.php?speakers=12&cdplayer=52&=10
To prevent embedded entities from corrupting your URLs, you have three choices. The first is
to choose variable names that can't be confused with entities, such as
_amp
instead of
amp
.
The second is to convert characters with HTML entity equivalents to those entities before
printing out the URL. Use
htmlentities( )
:
$url = '/muppet/select.php?' . htmlentities(join('&',$safe_vars));
The resulting URL is:
/muppet/select.php?name=Oscar+the+Grouch&color=green&favorite_punctuation=%
23
Your third choice is to change the argument separator from
&
to
;
by setting the configuration
directive
arg_separator.input
to
;
. You then join name-value pairs with
;
to produce a
query string:
/muppet/select.php?name=Oscar+the+Grouch;color=green;favorite_punctuation=%
23
You may run into trouble with any GET method URLs that you can't explicitly construct with
semicolons, such as a form with its method set to GET, because your users' browsers use
&
as
the argument separator.
Because many browsers don't support using
;
as an argument separator, the easiest way to
avoid problems with entities in URLs is to choose variable names that don't overlap with entity
54
names. If you don't have complete control over variable names, however, use
htmlentities( )
to protect your URLs from entity decoding.
8.9.4 See Also
Documentation on
urlencode( )
at http://www.php.net/urlencode
and
htmlentities( )
at http://www.php.net/htmlentities
.
Recipe 8.10 Using HTTP Basic Authentication
8.10.1 Problem
You want to use PHP to protect parts of your web site with passwords. Instead of storing the
passwords in an external file and letting the web server handle the authentication, you want
the password verification logic to be in a PHP program.
8.10.2 Solution
The
$_SERVER['PHP_AUTH_USER']
and
$_SERVER['PHP_AUTH_PW']
global variables
contain the username and password supplied by the user, if any. To deny access to a page,
send a
WWW-Authenticate
header identifying the authentication realm as part of a response
with status code 401:
header('WWW-Authenticate: Basic realm="My Website"');
header('HTTP/1.0 401 Unauthorized');
echo "You need to enter a valid username and password.";
exit;
8.10.3 Discussion
When a browser sees a 401 header, it pops up a dialog box for a username and password.
Those authentication credentials (the username and password), if accepted by the server, are
associated with the realm in the
WWW-Authenticate
header. Code that checks authentication
credentials needs to be executed before any output is sent to the browser, since it might send
headers. For example, you can use a function such as
pc_validate( )
, shown in Example
8-2
.
Example 8-2. pc_validate( )
function pc_validate($user,$pass) {
/* replace with appropriate username and password checking,
such as checking a database */
$users = array('david' => 'fadj&32',
'adam' => '8HEj838');
if (isset($users[$user]) && ($users[$user] == $pass)) {
return true;
} else {
return false;
}
Documents you may be interested
Documents you may be interested