/trunk/jrest/lib/.directory |
---|
New file |
0,0 → 1,2 |
[Dolphin] |
Timestamp=2010,6,10,16,46,25 |
/trunk/jrest/lib/WdHTMLParser.php |
---|
New file |
0,0 → 1,144 |
<?php |
/** |
* |
* @author Olivier Laviale |
* @see http://www.weirdog.com/blog/php/un-parser-html-des-plus-leger.html |
* |
*/ |
class WdHTMLParser { |
private $encoding; |
private $matches; |
private $escaped; |
private $opened = array(); |
public $malformed; |
public function parse($html, $namespace=NULL, $encoding='utf-8') { |
$this->malformed = false; |
$this->encoding = $encoding; |
// we take care of escaping comments and processing options. they will not be parsed |
// and will end as text nodes |
$html = $this->escapeSpecials($html); |
// in order to create a tree, we first need to split the HTML using the markups, |
// creating a nice flat array of texts and opening and closing markups. |
// |
// the array can be read as follows : |
// |
// i+0 => some text |
// i+1 => '/' for closing markups, nothing otherwise |
// i+2 => the markup it self, without the '<' '>' |
// |
// note that i+2 might end with a '/' indicating an auto-closing markup |
$this->matches = preg_split('#<(/?)' . $namespace . '([^>]*)>#', $html, -1, PREG_SPLIT_DELIM_CAPTURE); |
// the flat representation is now ready, we can create our tree |
$tree = $this->buildTree(); |
// if comments or processing options where escaped, we can |
// safely unescape them now |
if ($this->escaped) { |
$tree = $this->unescapeSpecials($tree); |
} |
return $tree; |
} |
private function escapeSpecials($html) { |
// here we escape comments |
$html = preg_replace_callback('#<\!--.+-->#sU', array($this, 'escapeSpecials_callback'), $html); |
// and processing options |
$html = preg_replace_callback('#<\?.+\?>#sU', array($this, 'escapeSpecials_callback'), $html); |
return $html; |
} |
private function escapeSpecials_callback($m) { |
$this->escaped = true; |
$text = $m[0]; |
$text = str_replace(array('<', '>'), array("\x01", "\x02"), $text); |
return $text; |
} |
private function unescapeSpecials($tree) { |
return is_array($tree) ? array_map(array($this, 'unescapeSpecials'), $tree) : str_replace(array("\x01", "\x02"), array('<', '>'), $tree); |
} |
private function buildTree() { |
$nodes = array(); |
$i = 0; |
$text = NULL; |
while (($value = array_shift($this->matches)) !== NULL) { |
switch ($i++ % 3) { |
case 0: |
// if the trimed value is not empty we preserve the value, |
// otherwise we discard it. |
if (trim($value)){ |
$nodes[] = $value; |
} |
break; |
case 1: |
$closing = ($value == '/'); |
break; |
case 2: |
if (substr($value, -1, 1) == '/') { |
// auto closing |
$nodes[] = $this->parseMarkup(substr($value, 0, -1)); |
} else if ($closing) { |
// closing markup |
$open = array_pop($this->opened); |
if ($value != $open) { |
$this->error($value, $open); |
} |
return $nodes; |
} else { |
// this is an open markup with possible children |
$node = $this->parseMarkup($value); |
// push the markup name into the opened markups |
$this->opened[] = $node['name']; |
// create the node and parse its children |
$node['children'] = $this->buildTree($this->matches); |
$nodes[] = $node; |
} |
break; |
} |
} |
return $nodes; |
} |
public function parseMarkup($markup) { |
// get markup's name |
preg_match('#^[^\s]+#', $markup, $matches); |
$name = $matches[0]; |
// get markup's arguments |
preg_match_all('#\s+([^=]+)\s*=\s*"([^"]+)"#', $markup, $matches, PREG_SET_ORDER); |
// transform the matches into a nice key/value array |
$args = array(); |
foreach ($matches as $m) { |
// we unescape the html entities of the argument's value |
$args[$m[1]] = html_entity_decode($m[2], ENT_QUOTES, $this->encoding); |
} |
return array('name' => $name, 'args' => $args); |
} |
public function error($markup, $expected) { |
$this->malformed = true; |
printf('unexpected closing markup "%s", should be "%s"', $markup, $expected); |
} |
} |
?> |
/trunk/jrest/lib/Writer.php |
---|
New file |
0,0 → 1,104 |
<?php |
/* |
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com> |
* |
* PERL Spreadsheet::WriteExcel module. |
* |
* The author of the Spreadsheet::WriteExcel module is John McNamara |
* <jmcnamara@cpan.org> |
* |
* I _DO_ maintain this code, and John McNamara has nothing to do with the |
* porting of this code to PHP. Any questions directly related to this |
* class library should be directed to me. |
* |
* License Information: |
* |
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets |
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation; either |
* version 2.1 of the License, or (at your option) any later version. |
* |
* This library is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
* Lesser General Public License for more details. |
* |
* You should have received a copy of the GNU Lesser General Public |
* License along with this library; if not, write to the Free Software |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
require_once 'PEAR.php'; |
require_once 'Spreadsheet/Excel/Writer/Workbook.php'; |
/** |
* Class for writing Excel Spreadsheets. This class should change COMPLETELY. |
* |
* @author Xavier Noguer <xnoguer@rezebra.com> |
* @category FileFormats |
* @package Spreadsheet_Excel_Writer |
*/ |
class Spreadsheet_Excel_Writer extends Spreadsheet_Excel_Writer_Workbook |
{ |
/** |
* The constructor. It just creates a Workbook |
* |
* @param string $filename The optional filename for the Workbook. |
* @return Spreadsheet_Excel_Writer_Workbook The Workbook created |
*/ |
function Spreadsheet_Excel_Writer($filename = '') |
{ |
$this->_filename = $filename; |
$this->Spreadsheet_Excel_Writer_Workbook($filename); |
} |
/** |
* Send HTTP headers for the Excel file. |
* |
* @param string $filename The filename to use for HTTP headers |
* @access public |
*/ |
function send($filename) |
{ |
header("Content-type: application/vnd.ms-excel"); |
header("Content-Disposition: attachment; filename=\"$filename\""); |
header("Expires: 0"); |
header("Cache-Control: must-revalidate, post-check=0,pre-check=0"); |
header("Pragma: public"); |
} |
/** |
* Utility function for writing formulas |
* Converts a cell's coordinates to the A1 format. |
* |
* @access public |
* @static |
* @param integer $row Row for the cell to convert (0-indexed). |
* @param integer $col Column for the cell to convert (0-indexed). |
* @return string The cell identifier in A1 format |
*/ |
function rowcolToCell($row, $col) |
{ |
if ($col > 255) { //maximum column value exceeded |
return new PEAR_Error("Maximum column value exceeded: $col"); |
} |
$int = (int)($col / 26); |
$frac = $col % 26; |
$chr1 = ''; |
if ($int > 0) { |
$chr1 = chr(ord('A') + $int - 1); |
} |
$chr2 = chr(ord('A') + $frac); |
$row++; |
return $chr1 . $chr2 . $row; |
} |
} |
?> |
/trunk/jrest/lib/DB/mysql.php |
---|
New file |
0,0 → 1,916 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */ |
// +----------------------------------------------------------------------+ |
// | PHP Version 4 | |
// +----------------------------------------------------------------------+ |
// | Copyright (c) 1997-2004 The PHP Group | |
// +----------------------------------------------------------------------+ |
// | This source file is subject to version 2.02 of the PHP license, | |
// | that is bundled with this package in the file LICENSE, and is | |
// | available at through the world-wide-web at | |
// | http://www.php.net/license/2_02.txt. | |
// | If you did not receive a copy of the PHP license and are unable to | |
// | obtain it through the world-wide-web, please send a note to | |
// | license@php.net so we can mail you a copy immediately. | |
// +----------------------------------------------------------------------+ |
// | Author: Stig Bakken <ssb@php.net> | |
// | Maintainer: Daniel Convissor <danielc@php.net> | |
// +----------------------------------------------------------------------+ |
// |
// $Id$ |
// XXX legend: |
// |
// XXX ERRORMSG: The error message from the mysql function should |
// be registered here. |
// |
// TODO/wishlist: |
// longReadlen |
// binmode |
require_once 'DB/common.php'; |
/** |
* Database independent query interface definition for PHP's MySQL |
* extension. |
* |
* This is for MySQL versions 4.0 and below. |
* |
* @package DB |
* @version $Id$ |
* @category Database |
* @author Stig Bakken <ssb@php.net> |
*/ |
class DB_mysql extends DB_common |
{ |
// {{{ properties |
var $connection; |
var $phptype, $dbsyntax; |
var $prepare_tokens = array(); |
var $prepare_types = array(); |
var $num_rows = array(); |
var $transaction_opcount = 0; |
var $autocommit = true; |
var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */ |
var $_db = false; |
// }}} |
// {{{ constructor |
/** |
* DB_mysql constructor. |
* |
* @access public |
*/ |
function DB_mysql() |
{ |
$this->DB_common(); |
$this->phptype = 'mysql'; |
$this->dbsyntax = 'mysql'; |
$this->features = array( |
'prepare' => false, |
'pconnect' => true, |
'transactions' => true, |
'limit' => 'alter' |
); |
$this->errorcode_map = array( |
1004 => DB_ERROR_CANNOT_CREATE, |
1005 => DB_ERROR_CANNOT_CREATE, |
1006 => DB_ERROR_CANNOT_CREATE, |
1007 => DB_ERROR_ALREADY_EXISTS, |
1008 => DB_ERROR_CANNOT_DROP, |
1022 => DB_ERROR_ALREADY_EXISTS, |
1046 => DB_ERROR_NODBSELECTED, |
1048 => DB_ERROR_CONSTRAINT, |
1050 => DB_ERROR_ALREADY_EXISTS, |
1051 => DB_ERROR_NOSUCHTABLE, |
1054 => DB_ERROR_NOSUCHFIELD, |
1062 => DB_ERROR_ALREADY_EXISTS, |
1064 => DB_ERROR_SYNTAX, |
1100 => DB_ERROR_NOT_LOCKED, |
1136 => DB_ERROR_VALUE_COUNT_ON_ROW, |
1146 => DB_ERROR_NOSUCHTABLE, |
1216 => DB_ERROR_CONSTRAINT, |
1217 => DB_ERROR_CONSTRAINT, |
); |
} |
// }}} |
// {{{ connect() |
/** |
* Connect to a database and log in as the specified user. |
* |
* @param $dsn the data source name (see DB::parseDSN for syntax) |
* @param $persistent (optional) whether the connection should |
* be persistent |
* @access public |
* @return int DB_OK on success, a DB error on failure |
*/ |
function connect($dsninfo, $persistent = false) |
{ |
if (!DB::assertExtension('mysql')) { |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsninfo; |
if ($dsninfo['protocol'] && $dsninfo['protocol'] == 'unix') { |
$dbhost = ':' . $dsninfo['socket']; |
} else { |
$dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; |
if ($dsninfo['port']) { |
$dbhost .= ':' . $dsninfo['port']; |
} |
} |
$connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect'; |
if ($dbhost && $dsninfo['username'] && isset($dsninfo['password'])) { |
$conn = @$connect_function($dbhost, $dsninfo['username'], |
$dsninfo['password']); |
} elseif ($dbhost && $dsninfo['username']) { |
$conn = @$connect_function($dbhost, $dsninfo['username']); |
} elseif ($dbhost) { |
$conn = @$connect_function($dbhost); |
} else { |
$conn = false; |
} |
if (!$conn) { |
if (($err = @mysql_error()) != '') { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, |
null, $err); |
} elseif (empty($php_errormsg)) { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED); |
} else { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, |
null, $php_errormsg); |
} |
} |
if ($dsninfo['database']) { |
if (!@mysql_select_db($dsninfo['database'], $conn)) { |
switch(mysql_errno($conn)) { |
case 1049: |
return $this->raiseError(DB_ERROR_NOSUCHDB, null, null, |
null, @mysql_error($conn)); |
case 1044: |
return $this->raiseError(DB_ERROR_ACCESS_VIOLATION, null, null, |
null, @mysql_error($conn)); |
default: |
return $this->raiseError(DB_ERROR, null, null, |
null, @mysql_error($conn)); |
} |
} |
// fix to allow calls to different databases in the same script |
$this->_db = $dsninfo['database']; |
} |
$this->connection = $conn; |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Log out and disconnect from the database. |
* |
* @access public |
* |
* @return bool true on success, false if not connected. |
*/ |
function disconnect() |
{ |
$ret = @mysql_close($this->connection); |
$this->connection = null; |
return $ret; |
} |
// }}} |
// {{{ simpleQuery() |
/** |
* Send a query to MySQL and return the results as a MySQL resource |
* identifier. |
* |
* @param the SQL query |
* |
* @access public |
* |
* @return mixed returns a valid MySQL result for successful SELECT |
* queries, DB_OK for other successful queries. A DB error is |
* returned on failure. |
*/ |
function simpleQuery($query) |
{ |
$ismanip = DB::isManip($query); |
$this->last_query = $query; |
$query = $this->modifyQuery($query); |
if ($this->_db) { |
if (!@mysql_select_db($this->_db, $this->connection)) { |
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); |
} |
} |
if (!$this->autocommit && $ismanip) { |
if ($this->transaction_opcount == 0) { |
$result = @mysql_query('SET AUTOCOMMIT=0', $this->connection); |
$result = @mysql_query('BEGIN', $this->connection); |
if (!$result) { |
return $this->mysqlRaiseError(); |
} |
} |
$this->transaction_opcount++; |
} |
$result = @mysql_query($query, $this->connection); |
if (!$result) { |
return $this->mysqlRaiseError(); |
} |
if (is_resource($result)) { |
$numrows = $this->numrows($result); |
if (is_object($numrows)) { |
return $numrows; |
} |
$this->num_rows[(int)$result] = $numrows; |
return $result; |
} |
return DB_OK; |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal mysql result pointer to the next available result |
* |
* This method has not been implemented yet. |
* |
* @param a valid sql result resource |
* |
* @access public |
* |
* @return false |
*/ |
function nextResult($result) |
{ |
return false; |
} |
// }}} |
// {{{ fetchInto() |
/** |
* Fetch a row and insert the data into an existing array. |
* |
* Formating of the array and the data therein are configurable. |
* See DB_result::fetchInto() for more information. |
* |
* @param resource $result query result identifier |
* @param array $arr (reference) array where data from the row |
* should be placed |
* @param int $fetchmode how the resulting array should be indexed |
* @param int $rownum the row number to fetch |
* |
* @return mixed DB_OK on success, null when end of result set is |
* reached or on failure |
* |
* @see DB_result::fetchInto() |
* @access private |
*/ |
function fetchInto($result, &$arr, $fetchmode, $rownum=null) |
{ |
if ($rownum !== null) { |
if (!@mysql_data_seek($result, $rownum)) { |
return null; |
} |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
$arr = @mysql_fetch_array($result, MYSQL_ASSOC); |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$arr = @mysql_fetch_row($result); |
} |
if (!$arr) { |
// See: http://bugs.php.net/bug.php?id=22328 |
// for why we can't check errors on fetching |
return null; |
/* |
$errno = @mysql_errno($this->connection); |
if (!$errno) { |
return null; |
} |
return $this->mysqlRaiseError($errno); |
*/ |
} |
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
/* |
* Even though this DBMS already trims output, we do this because |
* a field might have intentional whitespace at the end that |
* gets removed by DB_PORTABILITY_RTRIM under another driver. |
*/ |
$this->_rtrimArrayValues($arr); |
} |
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
$this->_convertNullArrayValuesToEmpty($arr); |
} |
return DB_OK; |
} |
// }}} |
// {{{ freeResult() |
/** |
* Free the internal resources associated with $result. |
* |
* @param $result MySQL result identifier |
* |
* @access public |
* |
* @return bool true on success, false if $result is invalid |
*/ |
function freeResult($result) |
{ |
unset($this->num_rows[(int)$result]); |
return @mysql_free_result($result); |
} |
// }}} |
// {{{ numCols() |
/** |
* Get the number of columns in a result set. |
* |
* @param $result MySQL result identifier |
* |
* @access public |
* |
* @return int the number of columns per row in $result |
*/ |
function numCols($result) |
{ |
$cols = @mysql_num_fields($result); |
if (!$cols) { |
return $this->mysqlRaiseError(); |
} |
return $cols; |
} |
// }}} |
// {{{ numRows() |
/** |
* Get the number of rows in a result set. |
* |
* @param $result MySQL result identifier |
* |
* @access public |
* |
* @return int the number of rows in $result |
*/ |
function numRows($result) |
{ |
$rows = @mysql_num_rows($result); |
if ($rows === null) { |
return $this->mysqlRaiseError(); |
} |
return $rows; |
} |
// }}} |
// {{{ autoCommit() |
/** |
* Enable/disable automatic commits |
*/ |
function autoCommit($onoff = false) |
{ |
// XXX if $this->transaction_opcount > 0, we should probably |
// issue a warning here. |
$this->autocommit = $onoff ? true : false; |
return DB_OK; |
} |
// }}} |
// {{{ commit() |
/** |
* Commit the current transaction. |
*/ |
function commit() |
{ |
if ($this->transaction_opcount > 0) { |
if ($this->_db) { |
if (!@mysql_select_db($this->_db, $this->connection)) { |
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); |
} |
} |
$result = @mysql_query('COMMIT', $this->connection); |
$result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->mysqlRaiseError(); |
} |
} |
return DB_OK; |
} |
// }}} |
// {{{ rollback() |
/** |
* Roll back (undo) the current transaction. |
*/ |
function rollback() |
{ |
if ($this->transaction_opcount > 0) { |
if ($this->_db) { |
if (!@mysql_select_db($this->_db, $this->connection)) { |
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); |
} |
} |
$result = @mysql_query('ROLLBACK', $this->connection); |
$result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->mysqlRaiseError(); |
} |
} |
return DB_OK; |
} |
// }}} |
// {{{ affectedRows() |
/** |
* Gets the number of rows affected by the data manipulation |
* query. For other queries, this function returns 0. |
* |
* @return number of rows affected by the last query |
*/ |
function affectedRows() |
{ |
if (DB::isManip($this->last_query)) { |
return @mysql_affected_rows($this->connection); |
} else { |
return 0; |
} |
} |
// }}} |
// {{{ errorNative() |
/** |
* Get the native error code of the last error (if any) that |
* occured on the current connection. |
* |
* @access public |
* |
* @return int native MySQL error code |
*/ |
function errorNative() |
{ |
return @mysql_errno($this->connection); |
} |
// }}} |
// {{{ nextId() |
/** |
* Returns the next free id in a sequence |
* |
* @param string $seq_name name of the sequence |
* @param boolean $ondemand when true, the seqence is automatically |
* created if it does not exist |
* |
* @return int the next id number in the sequence. DB_Error if problem. |
* |
* @internal |
* @see DB_common::nextID() |
* @access public |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$seqname = $this->getSequenceName($seq_name); |
do { |
$repeat = 0; |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result = $this->query("UPDATE ${seqname} ". |
'SET id=LAST_INSERT_ID(id+1)'); |
$this->popErrorHandling(); |
if ($result === DB_OK) { |
/** COMMON CASE **/ |
$id = @mysql_insert_id($this->connection); |
if ($id != 0) { |
return $id; |
} |
/** EMPTY SEQ TABLE **/ |
// Sequence table must be empty for some reason, so fill it and return 1 |
// Obtain a user-level lock |
$result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
if ($result == 0) { |
// Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error |
return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); |
} |
// add the default value |
$result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)"); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
// Release the lock |
$result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
// We know what the result will be, so no need to try again |
return 1; |
/** ONDEMAND TABLE CREATION **/ |
} elseif ($ondemand && DB::isError($result) && |
$result->getCode() == DB_ERROR_NOSUCHTABLE) |
{ |
$result = $this->createSequence($seq_name); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} else { |
$repeat = 1; |
} |
/** BACKWARDS COMPAT **/ |
} elseif (DB::isError($result) && |
$result->getCode() == DB_ERROR_ALREADY_EXISTS) |
{ |
// see _BCsequence() comment |
$result = $this->_BCsequence($seqname); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
$repeat = 1; |
} |
} while ($repeat); |
return $this->raiseError($result); |
} |
// }}} |
// {{{ createSequence() |
/** |
* Creates a new sequence |
* |
* @param string $seq_name name of the new sequence |
* |
* @return int DB_OK on success. A DB_Error object is returned if |
* problems arise. |
* |
* @internal |
* @see DB_common::createSequence() |
* @access public |
*/ |
function createSequence($seq_name) |
{ |
$seqname = $this->getSequenceName($seq_name); |
$res = $this->query("CREATE TABLE ${seqname} ". |
'(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'. |
' PRIMARY KEY(id))'); |
if (DB::isError($res)) { |
return $res; |
} |
// insert yields value 1, nextId call will generate ID 2 |
$res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)"); |
if (DB::isError($res)) { |
return $res; |
} |
// so reset to zero |
return $this->query("UPDATE ${seqname} SET id = 0;"); |
} |
// }}} |
// {{{ dropSequence() |
/** |
* Deletes a sequence |
* |
* @param string $seq_name name of the sequence to be deleted |
* |
* @return int DB_OK on success. DB_Error if problems. |
* |
* @internal |
* @see DB_common::dropSequence() |
* @access public |
*/ |
function dropSequence($seq_name) |
{ |
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
} |
// }}} |
// {{{ _BCsequence() |
/** |
* Backwards compatibility with old sequence emulation implementation |
* (clean up the dupes) |
* |
* @param string $seqname The sequence name to clean up |
* @return mixed DB_Error or true |
*/ |
function _BCsequence($seqname) |
{ |
// Obtain a user-level lock... this will release any previous |
// application locks, but unlike LOCK TABLES, it does not abort |
// the current transaction and is much less frequently used. |
$result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); |
if (DB::isError($result)) { |
return $result; |
} |
if ($result == 0) { |
// Failed to get the lock, can't do the conversion, bail |
// with a DB_ERROR_NOT_LOCKED error |
return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); |
} |
$highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); |
if (DB::isError($highest_id)) { |
return $highest_id; |
} |
// This should kill all rows except the highest |
// We should probably do something if $highest_id isn't |
// numeric, but I'm at a loss as how to handle that... |
$result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id"); |
if (DB::isError($result)) { |
return $result; |
} |
// If another thread has been waiting for this lock, |
// it will go thru the above procedure, but will have no |
// real effect |
$result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); |
if (DB::isError($result)) { |
return $result; |
} |
return true; |
} |
// }}} |
// {{{ quoteIdentifier() |
/** |
* Quote a string so it can be safely used as a table or column name |
* |
* Quoting style depends on which database driver is being used. |
* |
* MySQL can't handle the backtick character (<kbd>`</kbd>) in |
* table or column names. |
* |
* @param string $str identifier name to be quoted |
* |
* @return string quoted identifier string |
* |
* @since 1.6.0 |
* @access public |
* @internal |
*/ |
function quoteIdentifier($str) |
{ |
return '`' . $str . '`'; |
} |
// }}} |
// {{{ quote() |
/** |
* @deprecated Deprecated in release 1.6.0 |
* @internal |
*/ |
function quote($str) { |
return $this->quoteSmart($str); |
} |
// }}} |
// {{{ escapeSimple() |
/** |
* Escape a string according to the current DBMS's standards |
* |
* @param string $str the string to be escaped |
* |
* @return string the escaped string |
* |
* @internal |
*/ |
function escapeSimple($str) { |
if (function_exists('mysql_real_escape_string')) { |
return @mysql_real_escape_string($str, $this->connection); |
} else { |
return @mysql_escape_string($str); |
} |
} |
// }}} |
// {{{ modifyQuery() |
function modifyQuery($query) |
{ |
if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { |
// "DELETE FROM table" gives 0 affected rows in MySQL. |
// This little hack lets you know how many rows were deleted. |
if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { |
$query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', |
'DELETE FROM \1 WHERE 1=1', $query); |
} |
} |
return $query; |
} |
// }}} |
// {{{ modifyLimitQuery() |
function modifyLimitQuery($query, $from, $count, $params = array()) |
{ |
if (DB::isManip($query)) { |
return $query . " LIMIT $count"; |
} else { |
return $query . " LIMIT $from, $count"; |
} |
} |
// }}} |
// {{{ mysqlRaiseError() |
/** |
* Gather information about an error, then use that info to create a |
* DB error object and finally return that object. |
* |
* @param integer $errno PEAR error number (usually a DB constant) if |
* manually raising an error |
* @return object DB error object |
* @see DB_common::errorCode() |
* @see DB_common::raiseError() |
*/ |
function mysqlRaiseError($errno = null) |
{ |
if ($errno === null) { |
if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { |
$this->errorcode_map[1022] = DB_ERROR_CONSTRAINT; |
$this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL; |
$this->errorcode_map[1062] = DB_ERROR_CONSTRAINT; |
} else { |
// Doing this in case mode changes during runtime. |
$this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS; |
$this->errorcode_map[1048] = DB_ERROR_CONSTRAINT; |
$this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS; |
} |
$errno = $this->errorCode(mysql_errno($this->connection)); |
} |
return $this->raiseError($errno, null, null, null, |
@mysql_errno($this->connection) . ' ** ' . |
@mysql_error($this->connection)); |
} |
// }}} |
// {{{ tableInfo() |
/** |
* Returns information about a table or a result set. |
* |
* @param object|string $result DB_result object from a query or a |
* string containing the name of a table |
* @param int $mode a valid tableInfo mode |
* @return array an associative array with the information requested |
* or an error object if something is wrong |
* @access public |
* @internal |
* @see DB_common::tableInfo() |
*/ |
function tableInfo($result, $mode = null) { |
if (isset($result->result)) { |
/* |
* Probably received a result object. |
* Extract the result resource identifier. |
*/ |
$id = $result->result; |
$got_string = false; |
} elseif (is_string($result)) { |
/* |
* Probably received a table name. |
* Create a result resource identifier. |
*/ |
$id = @mysql_list_fields($this->dsn['database'], |
$result, $this->connection); |
$got_string = true; |
} else { |
/* |
* Probably received a result resource identifier. |
* Copy it. |
* Deprecated. Here for compatibility only. |
*/ |
$id = $result; |
$got_string = false; |
} |
if (!is_resource($id)) { |
return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$count = @mysql_num_fields($id); |
// made this IF due to performance (one if is faster than $count if's) |
if (!$mode) { |
for ($i=0; $i<$count; $i++) { |
$res[$i]['table'] = $case_func(@mysql_field_table($id, $i)); |
$res[$i]['name'] = $case_func(@mysql_field_name($id, $i)); |
$res[$i]['type'] = @mysql_field_type($id, $i); |
$res[$i]['len'] = @mysql_field_len($id, $i); |
$res[$i]['flags'] = @mysql_field_flags($id, $i); |
} |
} else { // full |
$res['num_fields']= $count; |
for ($i=0; $i<$count; $i++) { |
$res[$i]['table'] = $case_func(@mysql_field_table($id, $i)); |
$res[$i]['name'] = $case_func(@mysql_field_name($id, $i)); |
$res[$i]['type'] = @mysql_field_type($id, $i); |
$res[$i]['len'] = @mysql_field_len($id, $i); |
$res[$i]['flags'] = @mysql_field_flags($id, $i); |
if ($mode & DB_TABLEINFO_ORDER) { |
$res['order'][$res[$i]['name']] = $i; |
} |
if ($mode & DB_TABLEINFO_ORDERTABLE) { |
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
} |
} |
} |
// free the result only if we were called on a table |
if ($got_string) { |
@mysql_free_result($id); |
} |
return $res; |
} |
// }}} |
// {{{ getSpecialQuery() |
/** |
* Returns the query needed to get some backend info |
* @param string $type What kind of info you want to retrieve |
* @return string The SQL query string |
*/ |
function getSpecialQuery($type) |
{ |
switch ($type) { |
case 'tables': |
return 'SHOW TABLES'; |
case 'views': |
return DB_ERROR_NOT_CAPABLE; |
case 'users': |
$sql = 'select distinct User from user'; |
if ($this->dsn['database'] != 'mysql') { |
$dsn = $this->dsn; |
$dsn['database'] = 'mysql'; |
if (DB::isError($db = DB::connect($dsn))) { |
return $db; |
} |
$sql = $db->getCol($sql); |
$db->disconnect(); |
// XXX Fixme the mysql driver should take care of this |
if (!@mysql_select_db($this->dsn['database'], $this->connection)) { |
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); |
} |
} |
return $sql; |
case 'databases': |
return 'SHOW DATABASES'; |
default: |
return null; |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/trunk/jrest/lib/DB/common.php |
---|
New file |
0,0 → 1,2040 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */ |
// +----------------------------------------------------------------------+ |
// | PHP Version 4 | |
// +----------------------------------------------------------------------+ |
// | Copyright (c) 1997-2004 The PHP Group | |
// +----------------------------------------------------------------------+ |
// | This source file is subject to version 2.02 of the PHP license, | |
// | that is bundled with this package in the file LICENSE, and is | |
// | available at through the world-wide-web at | |
// | http://www.php.net/license/2_02.txt. | |
// | If you did not receive a copy of the PHP license and are unable to | |
// | obtain it through the world-wide-web, please send a note to | |
// | license@php.net so we can mail you a copy immediately. | |
// +----------------------------------------------------------------------+ |
// | Author: Stig Bakken <ssb@php.net> | |
// | Tomas V.V.Cox <cox@idecnet.com> | |
// | Maintainer: Daniel Convissor <danielc@php.net> | |
// +----------------------------------------------------------------------+ |
// |
// $Id$ |
require_once 'PEAR.php'; |
/** |
* DB_common is a base class for DB implementations, and must be |
* inherited by all such |
* |
* @package DB |
* @version $Id$ |
* @category Database |
* @author Stig Bakken <ssb@php.net> |
* @author Tomas V.V.Cox <cox@idecnet.com> |
*/ |
class DB_common extends PEAR |
{ |
// {{{ properties |
/** |
* assoc of capabilities for this DB implementation |
* $features['limit'] => 'emulate' => emulate with fetch row by number |
* 'alter' => alter the query |
* false => skip rows |
* @var array |
*/ |
var $features = array(); |
/** |
* assoc mapping native error codes to DB ones |
* @var array |
*/ |
var $errorcode_map = array(); |
/** |
* DB type (mysql, oci8, odbc etc.) |
* @var string |
*/ |
var $phptype; |
/** |
* @var string |
*/ |
var $prepare_tokens; |
/** |
* @var string |
*/ |
var $prepare_types; |
/** |
* @var string |
*/ |
var $prepared_queries; |
/** |
* @var integer |
*/ |
var $prepare_maxstmt = 0; |
/** |
* @var string |
*/ |
var $last_query = ''; |
/** |
* @var integer |
*/ |
var $fetchmode = DB_FETCHMODE_ORDERED; |
/** |
* @var string |
*/ |
var $fetchmode_object_class = 'stdClass'; |
/** |
* Run-time configuration options. |
* |
* The 'optimize' option has been deprecated. Use the 'portability' |
* option instead. |
* |
* @see DB_common::setOption() |
* @var array |
*/ |
var $options = array( |
'persistent' => false, |
'ssl' => false, |
'debug' => 0, |
'seqname_format' => '%s_seq', |
'autofree' => false, |
'portability' => DB_PORTABILITY_NONE, |
'optimize' => 'performance', // Deprecated. Use 'portability'. |
); |
/** |
* DB handle |
* @var resource |
*/ |
var $dbh; |
// }}} |
// {{{ toString() |
/** |
* String conversation |
* |
* @return string |
* @access private |
*/ |
function toString() |
{ |
$info = strtolower(get_class($this)); |
$info .= ': (phptype=' . $this->phptype . |
', dbsyntax=' . $this->dbsyntax . |
')'; |
if ($this->connection) { |
$info .= ' [connected]'; |
} |
return $info; |
} |
// }}} |
// {{{ constructor |
/** |
* Constructor |
*/ |
function DB_common() |
{ |
$this->PEAR('DB_Error'); |
} |
// }}} |
// {{{ quoteString() |
/** |
* DEPRECATED: Quotes a string so it can be safely used within string |
* delimiters in a query |
* |
* @return string quoted string |
* |
* @see DB_common::quoteSmart(), DB_common::escapeSimple() |
* @deprecated Deprecated in release 1.2 or lower |
* @internal |
*/ |
function quoteString($string) |
{ |
$string = $this->quote($string); |
if ($string{0} == "'") { |
return substr($string, 1, -1); |
} |
return $string; |
} |
// }}} |
// {{{ quote() |
/** |
* DEPRECATED: Quotes a string so it can be safely used in a query |
* |
* @param string $string the input string to quote |
* |
* @return string The NULL string or the string quotes |
* in magic_quote_sybase style |
* |
* @see DB_common::quoteSmart(), DB_common::escapeSimple() |
* @deprecated Deprecated in release 1.6.0 |
* @internal |
*/ |
function quote($string = null) |
{ |
return ($string === null) ? 'NULL' : "'".str_replace("'", "''", $string)."'"; |
} |
// }}} |
// {{{ quoteIdentifier() |
/** |
* Quote a string so it can be safely used as a table or column name |
* |
* Delimiting style depends on which database driver is being used. |
* |
* NOTE: 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 (<kbd>`</kbd>) -- due to MySQL |
* + double quote (<kbd>"</kbd>) -- due to Oracle |
* + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access |
* |
* Delimited identifiers are known to generally work correctly under |
* the following drivers: |
* + mssql |
* + mysql |
* + mysqli |
* + oci8 |
* + odbc(access) |
* + odbc(db2) |
* + pgsql |
* + sqlite |
* + sybase |
* |
* InterBase doesn't seem to be able to use delimited identifiers |
* via PHP 4. They work fine under PHP 5. |
* |
* @param string $str identifier name to be quoted |
* |
* @return string quoted identifier string |
* |
* @since 1.6.0 |
* @access public |
*/ |
function quoteIdentifier($str) |
{ |
return '"' . str_replace('"', '""', $str) . '"'; |
} |
// }}} |
// {{{ quoteSmart() |
/** |
* Format input so it can be safely used in a query |
* |
* The output depends on the PHP data type of input and the database |
* type being used. |
* |
* @param mixed $in data to be quoted |
* |
* @return mixed the format of the results depends on the input's |
* PHP type: |
* |
* <ul> |
* <li> |
* <kbd>input</kbd> -> <samp>returns</samp> |
* </li> |
* <li> |
* <kbd>null</kbd> -> the string <samp>NULL</samp> |
* </li> |
* <li> |
* <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number |
* </li> |
* <li> |
* &type.bool; -> output depends on the driver in use |
* Most drivers return integers: <samp>1</samp> if |
* <kbd>true</kbd> or <samp>0</samp> if |
* <kbd>false</kbd>. |
* Some return strings: <samp>TRUE</samp> if |
* <kbd>true</kbd> or <samp>FALSE</samp> if |
* <kbd>false</kbd>. |
* Finally one returns strings: <samp>T</samp> if |
* <kbd>true</kbd> or <samp>F</samp> if |
* <kbd>false</kbd>. Here is a list of each DBMS, |
* the values returned and the suggested column type: |
* <ul> |
* <li> |
* <kbd>dbase</kbd> -> <samp>T/F</samp> |
* (<kbd>Logical</kbd>) |
* </li> |
* <li> |
* <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp> |
* (<kbd>BOOLEAN</kbd>) |
* </li> |
* <li> |
* <kbd>ibase</kbd> -> <samp>1/0</samp> |
* (<kbd>SMALLINT</kbd>) [1] |
* </li> |
* <li> |
* <kbd>ifx</kbd> -> <samp>1/0</samp> |
* (<kbd>SMALLINT</kbd>) [1] |
* </li> |
* <li> |
* <kbd>msql</kbd> -> <samp>1/0</samp> |
* (<kbd>INTEGER</kbd>) |
* </li> |
* <li> |
* <kbd>mssql</kbd> -> <samp>1/0</samp> |
* (<kbd>BIT</kbd>) |
* </li> |
* <li> |
* <kbd>mysql</kbd> -> <samp>1/0</samp> |
* (<kbd>TINYINT(1)</kbd>) |
* </li> |
* <li> |
* <kbd>mysqli</kbd> -> <samp>1/0</samp> |
* (<kbd>TINYINT(1)</kbd>) |
* </li> |
* <li> |
* <kbd>oci8</kbd> -> <samp>1/0</samp> |
* (<kbd>NUMBER(1)</kbd>) |
* </li> |
* <li> |
* <kbd>odbc</kbd> -> <samp>1/0</samp> |
* (<kbd>SMALLINT</kbd>) [1] |
* </li> |
* <li> |
* <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp> |
* (<kbd>BOOLEAN</kbd>) |
* </li> |
* <li> |
* <kbd>sqlite</kbd> -> <samp>1/0</samp> |
* (<kbd>INTEGER</kbd>) |
* </li> |
* <li> |
* <kbd>sybase</kbd> -> <samp>1/0</samp> |
* (<kbd>TINYINT(1)</kbd>) |
* </li> |
* </ul> |
* [1] Accommodate the lowest common denominator because not all |
* versions of have <kbd>BOOLEAN</kbd>. |
* </li> |
* <li> |
* other (including strings and numeric strings) -> |
* the data with single quotes escaped by preceeding |
* single quotes, backslashes are escaped by preceeding |
* backslashes, then the whole string is encapsulated |
* between single quotes |
* </li> |
* </ul> |
* |
* @since 1.6.0 |
* @see DB_common::escapeSimple() |
* @access public |
*/ |
function quoteSmart($in) |
{ |
if (is_int($in) || is_double($in)) { |
return $in; |
} elseif (is_bool($in)) { |
return $in ? 1 : 0; |
} elseif (is_null($in)) { |
return 'NULL'; |
} else { |
return "'" . $this->escapeSimple($in) . "'"; |
} |
} |
// }}} |
// {{{ escapeSimple() |
/** |
* Escape a string according to the current DBMS's standards |
* |
* In SQLite, this makes things safe for inserts/updates, but may |
* cause problems when performing text comparisons against columns |
* containing binary data. See the |
* {@link http://php.net/sqlite_escape_string PHP manual} for more info. |
* |
* @param string $str the string to be escaped |
* |
* @return string the escaped string |
* |
* @since 1.6.0 |
* @see DB_common::quoteSmart() |
* @access public |
*/ |
function escapeSimple($str) { |
return str_replace("'", "''", $str); |
} |
// }}} |
// {{{ provides() |
/** |
* Tell whether a DB implementation or its backend extension |
* supports a given feature |
* |
* @param array $feature name of the feature (see the DB class doc) |
* @return bool whether this DB implementation supports $feature |
* @access public |
*/ |
function provides($feature) |
{ |
return $this->features[$feature]; |
} |
// }}} |
// {{{ errorCode() |
/** |
* Map native error codes to DB's portable ones |
* |
* Requires that the DB implementation's constructor fills |
* in the <var>$errorcode_map</var> property. |
* |
* @param mixed $nativecode the native error code, as returned by the |
* backend database extension (string or integer) |
* |
* @return int a portable DB error code, or DB_ERROR if this DB |
* implementation has no mapping for the given error code. |
* |
* @access public |
*/ |
function errorCode($nativecode) |
{ |
if (isset($this->errorcode_map[$nativecode])) { |
return $this->errorcode_map[$nativecode]; |
} |
// Fall back to DB_ERROR if there was no mapping. |
return DB_ERROR; |
} |
// }}} |
// {{{ errorMessage() |
/** |
* Map a DB error code to a textual message. This is actually |
* just a wrapper for DB::errorMessage() |
* |
* @param integer $dbcode the DB error code |
* |
* @return string the corresponding error message, of false |
* if the error code was unknown |
* |
* @access public |
*/ |
function errorMessage($dbcode) |
{ |
return DB::errorMessage($this->errorcode_map[$dbcode]); |
} |
// }}} |
// {{{ raiseError() |
/** |
* Communicate an error and invoke error callbacks, etc |
* |
* Basically a wrapper for PEAR::raiseError without the message string. |
* |
* @param mixed integer error code, or a PEAR error object (all |
* other parameters are ignored if this parameter is |
* an object |
* |
* @param int error mode, see PEAR_Error docs |
* |
* @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the |
* error level (E_USER_NOTICE etc). If error mode is |
* PEAR_ERROR_CALLBACK, this is the callback function, |
* either as a function name, or as an array of an |
* object and method name. For other error modes this |
* parameter is ignored. |
* |
* @param string Extra debug information. Defaults to the last |
* query and native error code. |
* |
* @param mixed Native error code, integer or string depending the |
* backend. |
* |
* @return object a PEAR error object |
* |
* @access public |
* @see PEAR_Error |
*/ |
function &raiseError($code = DB_ERROR, $mode = null, $options = null, |
$userinfo = null, $nativecode = null) |
{ |
// The error is yet a DB error object |
if (is_object($code)) { |
// because we the static PEAR::raiseError, our global |
// handler should be used if it is set |
if ($mode === null && !empty($this->_default_error_mode)) { |
$mode = $this->_default_error_mode; |
$options = $this->_default_error_options; |
} |
$tmp = PEAR::raiseError($code, null, $mode, $options, null, null, true); |
return $tmp; |
} |
if ($userinfo === null) { |
$userinfo = $this->last_query; |
} |
if ($nativecode) { |
$userinfo .= ' [nativecode=' . trim($nativecode) . ']'; |
} |
$tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo, |
'DB_Error', true); |
return $tmp; |
} |
// }}} |
// {{{ setFetchMode() |
/** |
* Sets which fetch mode should be used by default on queries |
* on this connection |
* |
* @param integer $fetchmode DB_FETCHMODE_ORDERED or |
* DB_FETCHMODE_ASSOC, possibly bit-wise OR'ed with |
* DB_FETCHMODE_FLIPPED. |
* |
* @param string $object_class The class of the object |
* to be returned by the fetch methods when |
* the DB_FETCHMODE_OBJECT mode is selected. |
* If no class is specified by default a cast |
* to object from the assoc array row will be done. |
* There is also the posibility to use and extend the |
* 'DB_row' class. |
* |
* @see DB_FETCHMODE_ORDERED |
* @see DB_FETCHMODE_ASSOC |
* @see DB_FETCHMODE_FLIPPED |
* @see DB_FETCHMODE_OBJECT |
* @see DB_row::DB_row() |
* @access public |
*/ |
function setFetchMode($fetchmode, $object_class = 'stdClass') |
{ |
switch ($fetchmode) { |
case DB_FETCHMODE_OBJECT: |
$this->fetchmode_object_class = $object_class; |
case DB_FETCHMODE_ORDERED: |
case DB_FETCHMODE_ASSOC: |
$this->fetchmode = $fetchmode; |
break; |
default: |
return $this->raiseError('invalid fetchmode mode'); |
} |
} |
// }}} |
// {{{ setOption() |
/** |
* Set run-time configuration options for PEAR DB |
* |
* Options, their data types, default values and description: |
* <ul> |
* <li> |
* <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp> |
* <br />should results be freed automatically when there are no |
* more rows? |
* </li><li> |
* <var>debug</var> <kbd>integer</kbd> = <samp>0</samp> |
* <br />debug level |
* </li><li> |
* <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp> |
* <br />should the connection be persistent? |
* </li><li> |
* <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp> |
* <br />portability mode constant (see below) |
* </li><li> |
* <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp> |
* <br />the sprintf() format string used on sequence names. This |
* format is applied to sequence names passed to |
* createSequence(), nextID() and dropSequence(). |
* </li><li> |
* <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp> |
* <br />use ssl to connect? |
* </li> |
* </ul> |
* |
* ----------------------------------------- |
* |
* PORTABILITY MODES |
* |
* These modes are bitwised, so they can be combined using <kbd>|</kbd> |
* and removed using <kbd>^</kbd>. See the examples section below on how |
* to do this. |
* |
* <samp>DB_PORTABILITY_NONE</samp> |
* turn off all portability features |
* |
* This mode gets automatically turned on if the deprecated |
* <var>optimize</var> option gets set to <samp>performance</samp>. |
* |
* |
* <samp>DB_PORTABILITY_LOWERCASE</samp> |
* convert names of tables and fields to lower case when using |
* <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd> |
* |
* This mode gets automatically turned on in the following databases |
* if the deprecated option <var>optimize</var> gets set to |
* <samp>portability</samp>: |
* + oci8 |
* |
* |
* <samp>DB_PORTABILITY_RTRIM</samp> |
* right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd> |
* |
* |
* <samp>DB_PORTABILITY_DELETE_COUNT</samp> |
* force reporting the number of rows deleted |
* |
* Some DBMS's don't count the number of rows deleted when performing |
* simple <kbd>DELETE FROM tablename</kbd> queries. This portability |
* mode tricks such DBMS's into telling the count by adding |
* <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries. |
* |
* This mode gets automatically turned on in the following databases |
* if the deprecated option <var>optimize</var> gets set to |
* <samp>portability</samp>: |
* + fbsql |
* + mysql |
* + mysqli |
* + sqlite |
* |
* |
* <samp>DB_PORTABILITY_NUMROWS</samp> |
* enable hack that makes <kbd>numRows()</kbd> work in Oracle |
* |
* This mode gets automatically turned on in the following databases |
* if the deprecated option <var>optimize</var> gets set to |
* <samp>portability</samp>: |
* + oci8 |
* |
* |
* <samp>DB_PORTABILITY_ERRORS</samp> |
* makes certain error messages in certain drivers compatible |
* with those from other DBMS's |
* |
* + mysql, mysqli: change unique/primary key constraints |
* DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT |
* |
* + odbc(access): MS's ODBC driver reports 'no such field' as code |
* 07001, which means 'too few parameters.' When this option is on |
* that code gets mapped to DB_ERROR_NOSUCHFIELD. |
* DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD |
* |
* |
* <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp> |
* 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. |
* |
* |
* <samp>DB_PORTABILITY_ALL</samp> |
* turn on all portability features |
* |
* ----------------------------------------- |
* |
* Example 1. Simple setOption() example |
* <code> <?php |
* $dbh->setOption('autofree', true); |
* ?></code> |
* |
* Example 2. Portability for lowercasing and trimming |
* <code> <?php |
* $dbh->setOption('portability', |
* DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM); |
* ?></code> |
* |
* Example 3. All portability options except trimming |
* <code> <?php |
* $dbh->setOption('portability', |
* DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM); |
* ?></code> |
* |
* @param string $option option name |
* @param mixed $value value for the option |
* |
* @return int DB_OK on success. DB_Error object on failure. |
* |
* @see DB_common::$options |
*/ |
function setOption($option, $value) |
{ |
if (isset($this->options[$option])) { |
$this->options[$option] = $value; |
/* |
* Backwards compatibility check for the deprecated 'optimize' |
* option. Done here in case settings change after connecting. |
*/ |
if ($option == 'optimize') { |
if ($value == 'portability') { |
switch ($this->phptype) { |
case 'oci8': |
$this->options['portability'] = |
DB_PORTABILITY_LOWERCASE | |
DB_PORTABILITY_NUMROWS; |
break; |
case 'fbsql': |
case 'mysql': |
case 'mysqli': |
case 'sqlite': |
$this->options['portability'] = |
DB_PORTABILITY_DELETE_COUNT; |
break; |
} |
} else { |
$this->options['portability'] = DB_PORTABILITY_NONE; |
} |
} |
return DB_OK; |
} |
return $this->raiseError("unknown option $option"); |
} |
// }}} |
// {{{ getOption() |
/** |
* Returns the value of an option |
* |
* @param string $option option name |
* |
* @return mixed the option value |
*/ |
function getOption($option) |
{ |
if (isset($this->options[$option])) { |
return $this->options[$option]; |
} |
return $this->raiseError("unknown option $option"); |
} |
// }}} |
// {{{ prepare() |
/** |
* Prepares a query for multiple execution with execute() |
* |
* Creates a query that can be run multiple times. Each time it is run, |
* the placeholders, if any, will be replaced by the contents of |
* execute()'s $data argument. |
* |
* Three types of placeholders can be used: |
* + <kbd>?</kbd> scalar value (i.e. strings, integers). The system |
* will automatically quote and escape the data. |
* + <kbd>!</kbd> value is inserted 'as is' |
* + <kbd>&</kbd> requires a file name. The file's contents get |
* inserted into the query (i.e. saving binary |
* data in a db) |
* |
* Example 1. |
* <code> <?php |
* $sth = $dbh->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); |
* $data = array( |
* "John's text", |
* "'it''s good'", |
* 'filename.txt' |
* ); |
* $res = $dbh->execute($sth, $data); |
* ?></code> |
* |
* Use backslashes to escape placeholder characters if you don't want |
* them to be interpreted as placeholders: |
* <pre> |
* "UPDATE foo SET col=? WHERE col='over \& under'" |
* </pre> |
* |
* With some database backends, this is emulated. |
* |
* {@internal ibase and oci8 have their own prepare() methods.}} |
* |
* @param string $query query to be prepared |
* |
* @return mixed DB statement resource on success. DB_Error on failure. |
* |
* @see DB_common::execute() |
* @access public |
*/ |
function prepare($query) |
{ |
$tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1, |
PREG_SPLIT_DELIM_CAPTURE); |
$token = 0; |
$types = array(); |
$newtokens = array(); |
foreach ($tokens as $val) { |
switch ($val) { |
case '?': |
$types[$token++] = DB_PARAM_SCALAR; |
break; |
case '&': |
$types[$token++] = DB_PARAM_OPAQUE; |
break; |
case '!': |
$types[$token++] = DB_PARAM_MISC; |
break; |
default: |
$newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val); |
} |
} |
$this->prepare_tokens[] = &$newtokens; |
end($this->prepare_tokens); |
$k = key($this->prepare_tokens); |
$this->prepare_types[$k] = $types; |
$this->prepared_queries[$k] = implode(' ', $newtokens); |
return $k; |
} |
// }}} |
// {{{ autoPrepare() |
/** |
* Automaticaly generate an insert or update query and pass it to prepare() |
* |
* @param string $table name of the table |
* @param array $table_fields ordered array containing the fields names |
* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE) |
* @param string $where in case of update queries, this string will be put after the sql WHERE statement |
* @return resource handle for the query |
* @see DB_common::prepare(), DB_common::buildManipSQL() |
* @access public |
*/ |
function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, $where = false) |
{ |
$query = $this->buildManipSQL($table, $table_fields, $mode, $where); |
return $this->prepare($query); |
} |
// }}} |
// {{{ autoExecute() |
/** |
* Automaticaly generate an insert or update query and call prepare() |
* and execute() with it |
* |
* @param string $table name of the table |
* @param array $fields_values assoc ($key=>$value) where $key is a field name and $value its value |
* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE) |
* @param string $where in case of update queries, this string will be put after the sql WHERE statement |
* @return mixed a new DB_Result or a DB_Error when fail |
* @see DB_common::autoPrepare(), DB_common::buildManipSQL() |
* @access public |
*/ |
function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, $where = false) |
{ |
$sth = $this->autoPrepare($table, array_keys($fields_values), $mode, $where); |
$ret =& $this->execute($sth, array_values($fields_values)); |
$this->freePrepared($sth); |
return $ret; |
} |
// }}} |
// {{{ buildManipSQL() |
/** |
* Make automaticaly an sql query for prepare() |
* |
* Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), DB_AUTOQUERY_INSERT) |
* will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?) |
* NB : - This belongs more to a SQL Builder class, but this is a simple facility |
* - Be carefull ! If you don't give a $where param with an UPDATE query, all |
* the records of the table will be updated ! |
* |
* @param string $table name of the table |
* @param array $table_fields ordered array containing the fields names |
* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE) |
* @param string $where in case of update queries, this string will be put after the sql WHERE statement |
* @return string sql query for prepare() |
* @access public |
*/ |
function buildManipSQL($table, $table_fields, $mode, $where = false) |
{ |
if (count($table_fields) == 0) { |
$this->raiseError(DB_ERROR_NEED_MORE_DATA); |
} |
$first = true; |
switch ($mode) { |
case DB_AUTOQUERY_INSERT: |
$values = ''; |
$names = ''; |
foreach ($table_fields as $value) { |
if ($first) { |
$first = false; |
} else { |
$names .= ','; |
$values .= ','; |
} |
$names .= $value; |
$values .= '?'; |
} |
return "INSERT INTO $table ($names) VALUES ($values)"; |
case DB_AUTOQUERY_UPDATE: |
$set = ''; |
foreach ($table_fields as $value) { |
if ($first) { |
$first = false; |
} else { |
$set .= ','; |
} |
$set .= "$value = ?"; |
} |
$sql = "UPDATE $table SET $set"; |
if ($where) { |
$sql .= " WHERE $where"; |
} |
return $sql; |
default: |
$this->raiseError(DB_ERROR_SYNTAX); |
} |
} |
// }}} |
// {{{ execute() |
/** |
* Executes a DB statement prepared with prepare() |
* |
* Example 1. |
* <code> <?php |
* $sth = $dbh->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); |
* $data = array( |
* "John's text", |
* "'it''s good'", |
* 'filename.txt' |
* ); |
* $res =& $dbh->execute($sth, $data); |
* ?></code> |
* |
* @param resource $stmt a DB statement resource returned from prepare() |
* @param mixed $data array, string or numeric data to be used in |
* execution of the statement. Quantity of items |
* passed must match quantity of placeholders in |
* query: meaning 1 placeholder for non-array |
* parameters or 1 placeholder per array element. |
* |
* @return object a new DB_Result or a DB_Error when fail |
* |
* {@internal ibase and oci8 have their own execute() methods.}} |
* |
* @see DB_common::prepare() |
* @access public |
*/ |
function &execute($stmt, $data = array()) |
{ |
$realquery = $this->executeEmulateQuery($stmt, $data); |
if (DB::isError($realquery)) { |
return $realquery; |
} |
$result = $this->simpleQuery($realquery); |
if (DB::isError($result) || $result === DB_OK) { |
return $result; |
} else { |
$tmp =& new DB_result($this, $result); |
return $tmp; |
} |
} |
// }}} |
// {{{ executeEmulateQuery() |
/** |
* Emulates the execute statement, when not supported |
* |
* @param resource $stmt a DB statement resource returned from execute() |
* @param mixed $data array, string or numeric data to be used in |
* execution of the statement. Quantity of items |
* passed must match quantity of placeholders in |
* query: meaning 1 placeholder for non-array |
* parameters or 1 placeholder per array element. |
* |
* @return mixed a string containing the real query run when emulating |
* prepare/execute. A DB error code is returned on failure. |
* |
* @see DB_common::execute() |
* @access private |
*/ |
function executeEmulateQuery($stmt, $data = array()) |
{ |
if (!is_array($data)) { |
$data = array($data); |
} |
if (count($this->prepare_types[$stmt]) != count($data)) { |
$this->last_query = $this->prepared_queries[$stmt]; |
return $this->raiseError(DB_ERROR_MISMATCH); |
} |
$realquery = $this->prepare_tokens[$stmt][0]; |
$i = 0; |
foreach ($data as $value) { |
if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) { |
$realquery .= $this->quoteSmart($value); |
} elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) { |
$fp = @fopen($value, 'rb'); |
if (!$fp) { |
return $this->raiseError(DB_ERROR_ACCESS_VIOLATION); |
} |
$realquery .= $this->quoteSmart(fread($fp, filesize($value))); |
fclose($fp); |
} else { |
$realquery .= $value; |
} |
$realquery .= $this->prepare_tokens[$stmt][++$i]; |
} |
return $realquery; |
} |
// }}} |
// {{{ executeMultiple() |
/** |
* This function does several execute() calls on the same |
* statement handle |
* |
* $data must be an array indexed numerically |
* from 0, one execute call is done for every "row" in the array. |
* |
* If an error occurs during execute(), executeMultiple() does not |
* execute the unfinished rows, but rather returns that error. |
* |
* @param resource $stmt query handle from prepare() |
* @param array $data numeric array containing the |
* data to insert into the query |
* |
* @return mixed DB_OK or DB_Error |
* |
* @see DB_common::prepare(), DB_common::execute() |
* @access public |
*/ |
function executeMultiple($stmt, $data) |
{ |
foreach ($data as $value) { |
$res =& $this->execute($stmt, $value); |
if (DB::isError($res)) { |
return $res; |
} |
} |
return DB_OK; |
} |
// }}} |
// {{{ freePrepared() |
/** |
* Free the resource used in a prepared query |
* |
* @param $stmt The resurce returned by the prepare() function |
* @see DB_common::prepare() |
*/ |
function freePrepared($stmt) |
{ |
// Free the internal prepared vars |
if (isset($this->prepare_tokens[$stmt])) { |
unset($this->prepare_tokens[$stmt]); |
unset($this->prepare_types[$stmt]); |
unset($this->prepared_queries[$stmt]); |
return true; |
} |
return false; |
} |
// }}} |
// {{{ modifyQuery() |
/** |
* This method is used by backends to alter queries for various |
* reasons |
* |
* It is defined here to assure that all implementations |
* have this method defined. |
* |
* @param string $query query to modify |
* |
* @return the new (modified) query |
* |
* @access private |
*/ |
function modifyQuery($query) { |
return $query; |
} |
// }}} |
// {{{ modifyLimitQuery() |
/** |
* This method is used by backends to alter limited queries |
* |
* @param string $query query to modify |
* @param integer $from the row to start to fetching |
* @param integer $count the numbers of rows to fetch |
* |
* @return the new (modified) query |
* |
* @access private |
*/ |
function modifyLimitQuery($query, $from, $count, $params = array()) |
{ |
return $query; |
} |
// }}} |
// {{{ query() |
/** |
* Send a query to the database and return any results with a |
* DB_result object |
* |
* The query string can be either a normal statement to be sent directly |
* to the server OR if <var>$params</var> are passed the query can have |
* placeholders and it will be passed through prepare() and execute(). |
* |
* @param string $query the SQL query or the statement to prepare |
* @param mixed $params array, string or numeric data to be used in |
* execution of the statement. Quantity of items |
* passed must match quantity of placeholders in |
* query: meaning 1 placeholder for non-array |
* parameters or 1 placeholder per array element. |
* |
* @return mixed a DB_result object or DB_OK on success, a DB |
* error on failure |
* |
* @see DB_result, DB_common::prepare(), DB_common::execute() |
* @access public |
*/ |
function &query($query, $params = array()) |
{ |
if (sizeof($params) > 0) { |
$sth = $this->prepare($query); |
if (DB::isError($sth)) { |
return $sth; |
} |
$ret =& $this->execute($sth, $params); |
$this->freePrepared($sth); |
return $ret; |
} else { |
$result = $this->simpleQuery($query); |
if (DB::isError($result) || $result === DB_OK) { |
return $result; |
} else { |
$tmp =& new DB_result($this, $result); |
return $tmp; |
} |
} |
} |
// }}} |
// {{{ limitQuery() |
/** |
* Generates a limited query |
* |
* @param string $query query |
* @param integer $from the row to start to fetching |
* @param integer $count the numbers of rows to fetch |
* @param mixed $params array, string or numeric data to be used in |
* execution of the statement. Quantity of items |
* passed must match quantity of placeholders in |
* query: meaning 1 placeholder for non-array |
* parameters or 1 placeholder per array element. |
* |
* @return mixed a DB_Result object, DB_OK or a DB_Error |
* |
* @access public |
*/ |
function &limitQuery($query, $from, $count, $params = array()) |
{ |
$query = $this->modifyLimitQuery($query, $from, $count, $params); |
if (DB::isError($query)){ |
return $query; |
} |
$result =& $this->query($query, $params); |
if (is_a($result, 'DB_result')) { |
$result->setOption('limit_from', $from); |
$result->setOption('limit_count', $count); |
} |
return $result; |
} |
// }}} |
// {{{ getOne() |
/** |
* Fetch the first column of the first row of data returned from |
* a query |
* |
* Takes care of doing the query and freeing the results when finished. |
* |
* @param string $query the SQL query |
* @param mixed $params array, string or numeric data to be used in |
* execution of the statement. Quantity of items |
* passed must match quantity of placeholders in |
* query: meaning 1 placeholder for non-array |
* parameters or 1 placeholder per array element. |
* |
* @return mixed the returned value of the query. DB_Error on failure. |
* |
* @access public |
*/ |
function &getOne($query, $params = array()) |
{ |
settype($params, 'array'); |
if (sizeof($params) > 0) { |
$sth = $this->prepare($query); |
if (DB::isError($sth)) { |
return $sth; |
} |
$res =& $this->execute($sth, $params); |
$this->freePrepared($sth); |
} else { |
$res =& $this->query($query); |
} |
if (DB::isError($res)) { |
return $res; |
} |
$err = $res->fetchInto($row, DB_FETCHMODE_ORDERED); |
$res->free(); |
if ($err !== DB_OK) { |
return $err; |
} |
return $row[0]; |
} |
// }}} |
// {{{ getRow() |
/** |
* Fetch the first row of data returned from a query |
* |
* Takes care of doing the query and freeing the results when finished. |
* |
* @param string $query the SQL query |
* @param array $params array to be used in execution of the statement. |
* Quantity of array elements must match quantity |
* of placeholders in query. This function does |
* NOT support scalars. |
* @param int $fetchmode the fetch mode to use |
* |
* @return array the first row of results as an array indexed from |
* 0, or a DB error code. |
* |
* @access public |
*/ |
function &getRow($query, |
$params = array(), |
$fetchmode = DB_FETCHMODE_DEFAULT) |
{ |
// compat check, the params and fetchmode parameters used to |
// have the opposite order |
if (!is_array($params)) { |
if (is_array($fetchmode)) { |
if ($params === null) { |
$tmp = DB_FETCHMODE_DEFAULT; |
} else { |
$tmp = $params; |
} |
$params = $fetchmode; |
$fetchmode = $tmp; |
} elseif ($params !== null) { |
$fetchmode = $params; |
$params = array(); |
} |
} |
if (sizeof($params) > 0) { |
$sth = $this->prepare($query); |
if (DB::isError($sth)) { |
return $sth; |
} |
$res =& $this->execute($sth, $params); |
$this->freePrepared($sth); |
} else { |
$res =& $this->query($query); |
} |
if (DB::isError($res)) { |
return $res; |
} |
$err = $res->fetchInto($row, $fetchmode); |
$res->free(); |
if ($err !== DB_OK) { |
return $err; |
} |
return $row; |
} |
// }}} |
// {{{ getCol() |
/** |
* Fetch a single column from a result set and return it as an |
* indexed array |
* |
* @param string $query the SQL query |
* @param mixed $col which column to return (integer [column number, |
* starting at 0] or string [column name]) |
* @param mixed $params array, string or numeric data to be used in |
* execution of the statement. Quantity of items |
* passed must match quantity of placeholders in |
* query: meaning 1 placeholder for non-array |
* parameters or 1 placeholder per array element. |
* |
* @return array an indexed array with the data from the first |
* row at index 0, or a DB error code |
* |
* @see DB_common::query() |
* @access public |
*/ |
function &getCol($query, $col = 0, $params = array()) |
{ |
settype($params, 'array'); |
if (sizeof($params) > 0) { |
$sth = $this->prepare($query); |
if (DB::isError($sth)) { |
return $sth; |
} |
$res =& $this->execute($sth, $params); |
$this->freePrepared($sth); |
} else { |
$res =& $this->query($query); |
} |
if (DB::isError($res)) { |
return $res; |
} |
$fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC; |
if (!is_array($row = $res->fetchRow($fetchmode))) { |
$ret = array(); |
} else { |
if (!array_key_exists($col, $row)) { |
$ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD); |
} else { |
$ret = array($row[$col]); |
while (is_array($row = $res->fetchRow($fetchmode))) { |
$ret[] = $row[$col]; |
} |
} |
} |
$res->free(); |
if (DB::isError($row)) { |
$ret = $row; |
} |
return $ret; |
} |
// }}} |
// {{{ getAssoc() |
/** |
* Fetch the entire result set of a query and return it as an |
* associative array using the first column as the key |
* |
* If the result set contains more than two columns, the value |
* will be an array of the values from column 2-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). A DB error code is |
* returned on errors. If the result set contains fewer than two |
* columns, a DB_ERROR_TRUNCATED error is returned. |
* |
* For example, if the table "mytable" contains: |
* |
* <pre> |
* ID TEXT DATE |
* -------------------------------- |
* 1 'one' 944679408 |
* 2 'two' 944679408 |
* 3 'three' 944679408 |
* </pre> |
* |
* Then the call getAssoc('SELECT id,text FROM mytable') returns: |
* <pre> |
* array( |
* '1' => 'one', |
* '2' => 'two', |
* '3' => 'three', |
* ) |
* </pre> |
* |
* ...while the call getAssoc('SELECT id,text,date FROM mytable') returns: |
* <pre> |
* array( |
* '1' => array('one', '944679408'), |
* '2' => array('two', '944679408'), |
* '3' => array('three', '944679408') |
* ) |
* </pre> |
* |
* If the more than one row occurs with the same value in the |
* first column, the last row overwrites all previous ones by |
* default. Use the $group parameter if you don't want to |
* overwrite like this. Example: |
* |
* <pre> |
* getAssoc('SELECT category,id,name FROM mytable', false, null, |
* DB_FETCHMODE_ASSOC, true) returns: |
* |
* array( |
* '1' => array(array('id' => '4', 'name' => 'number four'), |
* array('id' => '6', 'name' => 'number six') |
* ), |
* '9' => array(array('id' => '4', 'name' => 'number four'), |
* array('id' => '6', 'name' => 'number six') |
* ) |
* ) |
* </pre> |
* |
* Keep in mind that database functions in PHP usually return string |
* values for results regardless of the database's internal type. |
* |
* @param string $query the SQL query |
* @param boolean $force_array used only when the query returns |
* exactly two columns. If true, the values |
* of the returned array will be one-element |
* arrays instead of scalars. |
* @param mixed $params array, string or numeric data to be used in |
* execution of the statement. Quantity of items |
* passed must match quantity of placeholders in |
* query: meaning 1 placeholder for non-array |
* parameters or 1 placeholder per array element. |
* @param int $fetchmode the fetch mode to use |
* @param 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. |
* |
* @return array associative array with results from the query. |
* DB Error on failure. |
* |
* @access public |
*/ |
function &getAssoc($query, $force_array = false, $params = array(), |
$fetchmode = DB_FETCHMODE_DEFAULT, $group = false) |
{ |
settype($params, 'array'); |
if (sizeof($params) > 0) { |
$sth = $this->prepare($query); |
if (DB::isError($sth)) { |
return $sth; |
} |
$res =& $this->execute($sth, $params); |
$this->freePrepared($sth); |
} else { |
$res =& $this->query($query); |
} |
if (DB::isError($res)) { |
return $res; |
} |
if ($fetchmode == DB_FETCHMODE_DEFAULT) { |
$fetchmode = $this->fetchmode; |
} |
$cols = $res->numCols(); |
if ($cols < 2) { |
$tmp =& $this->raiseError(DB_ERROR_TRUNCATED); |
return $tmp; |
} |
$results = array(); |
if ($cols > 2 || $force_array) { |
// return array values |
// XXX this part can be optimized |
if ($fetchmode == DB_FETCHMODE_ASSOC) { |
while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) { |
reset($row); |
$key = current($row); |
unset($row[key($row)]); |
if ($group) { |
$results[$key][] = $row; |
} else { |
$results[$key] = $row; |
} |
} |
} elseif ($fetchmode == DB_FETCHMODE_OBJECT) { |
while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) { |
$arr = get_object_vars($row); |
$key = current($arr); |
if ($group) { |
$results[$key][] = $row; |
} else { |
$results[$key] = $row; |
} |
} |
} else { |
while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { |
// we shift away the first element to get |
// indices running from 0 again |
$key = array_shift($row); |
if ($group) { |
$results[$key][] = $row; |
} else { |
$results[$key] = $row; |
} |
} |
} |
if (DB::isError($row)) { |
$results = $row; |
} |
} else { |
// return scalar values |
while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { |
if ($group) { |
$results[$row[0]][] = $row[1]; |
} else { |
$results[$row[0]] = $row[1]; |
} |
} |
if (DB::isError($row)) { |
$results = $row; |
} |
} |
$res->free(); |
return $results; |
} |
// }}} |
// {{{ getAll() |
/** |
* Fetch all the rows returned from a query |
* |
* @param string $query the SQL query |
* @param array $params array to be used in execution of the statement. |
* Quantity of array elements must match quantity |
* of placeholders in query. This function does |
* NOT support scalars. |
* @param int $fetchmode the fetch mode to use |
* |
* @return array an nested array. DB error on failure. |
* |
* @access public |
*/ |
function &getAll($query, |
$params = array(), |
$fetchmode = DB_FETCHMODE_DEFAULT) |
{ |
// compat check, the params and fetchmode parameters used to |
// have the opposite order |
if (!is_array($params)) { |
if (is_array($fetchmode)) { |
if ($params === null) { |
$tmp = DB_FETCHMODE_DEFAULT; |
} else { |
$tmp = $params; |
} |
$params = $fetchmode; |
$fetchmode = $tmp; |
} elseif ($params !== null) { |
$fetchmode = $params; |
$params = array(); |
} |
} |
if (sizeof($params) > 0) { |
$sth = $this->prepare($query); |
if (DB::isError($sth)) { |
return $sth; |
} |
$res =& $this->execute($sth, $params); |
$this->freePrepared($sth); |
} else { |
$res =& $this->query($query); |
} |
if (DB::isError($res) || $res === DB_OK) { |
return $res; |
} |
$results = array(); |
while (DB_OK === $res->fetchInto($row, $fetchmode)) { |
if ($fetchmode & DB_FETCHMODE_FLIPPED) { |
foreach ($row as $key => $val) { |
$results[$key][] = $val; |
} |
} else { |
$results[] = $row; |
} |
} |
$res->free(); |
if (DB::isError($row)) { |
$tmp =& $this->raiseError($row); |
return $tmp; |
} |
return $results; |
} |
// }}} |
// {{{ autoCommit() |
/** |
* enable automatic Commit |
* |
* @param boolean $onoff |
* @return mixed DB_Error |
* |
* @access public |
*/ |
function autoCommit($onoff=false) |
{ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ commit() |
/** |
* starts a Commit |
* |
* @return mixed DB_Error |
* |
* @access public |
*/ |
function commit() |
{ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ rollback() |
/** |
* starts a rollback |
* |
* @return mixed DB_Error |
* |
* @access public |
*/ |
function rollback() |
{ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ numRows() |
/** |
* Returns the number of rows in a result object |
* |
* @param object DB_Result the result object to check |
* |
* @return mixed DB_Error or the number of rows |
* |
* @access public |
*/ |
function numRows($result) |
{ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ affectedRows() |
/** |
* Returns the affected rows of a query |
* |
* @return mixed DB_Error or number of rows |
* |
* @access public |
*/ |
function affectedRows() |
{ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ errorNative() |
/** |
* Returns an errormessage, provides by the database |
* |
* @return mixed DB_Error or message |
* |
* @access public |
*/ |
function errorNative() |
{ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ getSequenceName() |
/** |
* Generate the name used inside the database for a sequence |
* |
* The createSequence() docblock contains notes about storing sequence |
* names. |
* |
* @param string $sqn the sequence's public name |
* |
* @return string the sequence's name in the backend |
* |
* @see DB_common::createSequence(), DB_common::dropSequence(), |
* DB_common::nextID(), DB_common::setOption() |
* @access private |
*/ |
function getSequenceName($sqn) |
{ |
return sprintf($this->getOption('seqname_format'), |
preg_replace('/[^a-z0-9_.]/i', '_', $sqn)); |
} |
// }}} |
// {{{ nextId() |
/** |
* Returns the next free id in a sequence |
* |
* @param string $seq_name name of the sequence |
* @param boolean $ondemand when true, the seqence is automatically |
* created if it does not exist |
* |
* @return int the next id number in the sequence. DB_Error if problem. |
* |
* @see DB_common::createSequence(), DB_common::dropSequence(), |
* DB_common::getSequenceName() |
* @access public |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ createSequence() |
/** |
* Creates a new sequence |
* |
* The name of a given sequence is determined by passing the string |
* provided in the <var>$seq_name</var> argument through PHP's sprintf() |
* function using the value from the <var>seqname_format</var> option as |
* the sprintf()'s format argument. |
* |
* <var>seqname_format</var> is set via setOption(). |
* |
* @param string $seq_name name of the new sequence |
* |
* @return int DB_OK on success. A DB_Error object is returned if |
* problems arise. |
* |
* @see DB_common::dropSequence(), DB_common::getSequenceName(), |
* DB_common::nextID() |
* @access public |
*/ |
function createSequence($seq_name) |
{ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ dropSequence() |
/** |
* Deletes a sequence |
* |
* @param string $seq_name name of the sequence to be deleted |
* |
* @return int DB_OK on success. DB_Error if problems. |
* |
* @see DB_common::createSequence(), DB_common::getSequenceName(), |
* DB_common::nextID() |
* @access public |
*/ |
function dropSequence($seq_name) |
{ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ tableInfo() |
/** |
* Returns information about a table or a result set |
* |
* The format of the resulting array depends on which <var>$mode</var> |
* you select. The sample output below is based on this query: |
* <pre> |
* SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId |
* FROM tblFoo |
* JOIN tblBar ON tblFoo.fldId = tblBar.fldId |
* </pre> |
* |
* <ul> |
* <li> |
* |
* <kbd>null</kbd> (default) |
* <pre> |
* [0] => Array ( |
* [table] => tblFoo |
* [name] => fldId |
* [type] => int |
* [len] => 11 |
* [flags] => primary_key not_null |
* ) |
* [1] => Array ( |
* [table] => tblFoo |
* [name] => fldPhone |
* [type] => string |
* [len] => 20 |
* [flags] => |
* ) |
* [2] => Array ( |
* [table] => tblBar |
* [name] => fldId |
* [type] => int |
* [len] => 11 |
* [flags] => primary_key not_null |
* ) |
* </pre> |
* |
* </li><li> |
* |
* <kbd>DB_TABLEINFO_ORDER</kbd> |
* |
* <p>In addition to the information found in the default output, |
* a notation of the number of columns is provided by the |
* <samp>num_fields</samp> element while the <samp>order</samp> |
* element provides an array with the column names as the keys and |
* their location index number (corresponding to the keys in the |
* the default output) as the values.</p> |
* |
* <p>If a result set has identical field names, the last one is |
* used.</p> |
* |
* <pre> |
* [num_fields] => 3 |
* [order] => Array ( |
* [fldId] => 2 |
* [fldTrans] => 1 |
* ) |
* </pre> |
* |
* </li><li> |
* |
* <kbd>DB_TABLEINFO_ORDERTABLE</kbd> |
* |
* <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> 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.</p> |
* |
* <pre> |
* [num_fields] => 3 |
* [ordertable] => Array ( |
* [tblFoo] => Array ( |
* [fldId] => 0 |
* [fldPhone] => 1 |
* ) |
* [tblBar] => Array ( |
* [fldId] => 2 |
* ) |
* ) |
* </pre> |
* |
* </li> |
* </ul> |
* |
* The <samp>flags</samp> element contains a space separated list |
* of extra information about the field. This data is inconsistent |
* between DBMS's due to the way each DBMS works. |
* + <samp>primary_key</samp> |
* + <samp>unique_key</samp> |
* + <samp>multiple_key</samp> |
* + <samp>not_null</samp> |
* |
* Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp> |
* elements if <var>$result</var> is a table name. The following DBMS's |
* provide full information from queries: |
* + fbsql |
* + mysql |
* |
* If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp> |
* turned on, the names of tables and fields will be lowercased. |
* |
* @param object|string $result DB_result object from a query or a |
* string containing the name of a table. |
* While this also accepts a query result |
* resource identifier, this behavior is |
* deprecated. |
* @param int $mode either unused or one of the tableInfo modes: |
* <kbd>DB_TABLEINFO_ORDERTABLE</kbd>, |
* <kbd>DB_TABLEINFO_ORDER</kbd> or |
* <kbd>DB_TABLEINFO_FULL</kbd> (which does both). |
* These are bitwise, so the first two can be |
* combined using <kbd>|</kbd>. |
* @return array an associative array with the information requested. |
* If something goes wrong an error object is returned. |
* |
* @see DB_common::setOption() |
* @access public |
*/ |
function tableInfo($result, $mode = null) |
{ |
/* |
* If the DB_<driver> class has a tableInfo() method, that one |
* overrides this one. But, if the driver doesn't have one, |
* this method runs and tells users about that fact. |
*/ |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ getTables() |
/** |
* @deprecated Deprecated in release 1.2 or lower |
*/ |
function getTables() |
{ |
return $this->getListOf('tables'); |
} |
// }}} |
// {{{ getListOf() |
/** |
* list internal DB info |
* valid values for $type are db dependent, |
* often: databases, users, view, functions |
* |
* @param string $type type of requested info |
* |
* @return mixed DB_Error or the requested data |
* |
* @access public |
*/ |
function getListOf($type) |
{ |
$sql = $this->getSpecialQuery($type); |
if ($sql === null) { // No support |
return $this->raiseError(DB_ERROR_UNSUPPORTED); |
} elseif (is_int($sql) || DB::isError($sql)) { // Previous error |
return $this->raiseError($sql); |
} elseif (is_array($sql)) { // Already the result |
return $sql; |
} |
return $this->getCol($sql); // Launch this query |
} |
// }}} |
// {{{ getSpecialQuery() |
/** |
* Returns the query needed to get some backend info |
* |
* @param string $type What kind of info you want to retrieve |
* |
* @return string The SQL query string |
* |
* @access public |
*/ |
function getSpecialQuery($type) |
{ |
return $this->raiseError(DB_ERROR_UNSUPPORTED); |
} |
// }}} |
// {{{ _rtrimArrayValues() |
/** |
* Right trim all strings in an array |
* |
* @param array $array the array to be trimmed (passed by reference) |
* @return void |
* @access private |
*/ |
function _rtrimArrayValues(&$array) |
{ |
foreach ($array as $key => $value) { |
if (is_string($value)) { |
$array[$key] = rtrim($value); |
} |
} |
} |
// }}} |
// {{{ _convertNullArrayValuesToEmpty() |
/** |
* Convert all null values in an array to empty strings |
* |
* @param array $array the array to be de-nullified (passed by reference) |
* @return void |
* @access private |
*/ |
function _convertNullArrayValuesToEmpty(&$array) |
{ |
foreach ($array as $key => $value) { |
if (is_null($value)) { |
$array[$key] = ''; |
} |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/trunk/jrest/lib/DB/.directory |
---|
New file |
0,0 → 1,5 |
[Dolphin] |
Timestamp=2010,6,10,16,42,30 |
[Settings] |
ShowDotFiles=true |
/trunk/jrest/lib/Log.php |
---|
New file |
0,0 → 1,195 |
<?php |
//declare(encoding='UTF-8'); |
/** |
* Classe permettant de logger des messages dans les fichier situés dans le dossier de log |
* |
* PHP Version 5 |
* |
* @category PHP |
* @package Framework |
* @author aurelien <aurelien@tela-botanica.org> |
* @copyright 2009 Tela-Botanica |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @version SVN: <svn_id> |
* @link /doc/framework/ |
*/ |
class Log { |
/** |
* Tableau associatif stockant les descripteurs de fichiers |
*/ |
private static $fichiersLog = array(); |
/** |
* Chemin de base du dossier log de l'application |
*/ |
private static $cheminLogs = ''; |
/** |
* Booleen indiquant si l'on peut correctement écrire dans les fichiers de logs |
*/ |
private static $droitLogger = true; |
/** |
* Zone horaire (pour éviter des avertissements dans les dates) |
*/ |
private static $timeZone = ''; |
/** |
* Taille maximum d'un fichier de log avant que celui ne soit archivé (en octets) |
*/ |
private static $tailleMax = 10000; |
/** |
* séparateur de chemin |
*/ |
private static $sd = DIRECTORY_SEPARATOR; |
/** |
* Extension des fichiers de log |
*/ |
private static $ext = '.log'; |
/** |
* La classe registre se contient elle-même, (pour le pattern singleton) |
*/ |
private static $log; |
/** |
* Constructeur par défaut, privé, car on accède à la classe par le getInstance |
*/ |
private function __construct() { |
self::$sd = $sd; |
// gestion de la timezone pour éviter des erreurs |
if(function_exists("date_default_timezone_set") and function_exists("date_default_timezone_get")) { |
date_default_timezone_set(self::$timeZone); |
} |
if(!is_dir(self::$cheminLogs) || !is_writable(self::$cheminLogs)) { |
self::desactiverEcriture(); |
} |
} |
public static function setCheminLog($nouveauCheminLogs) { |
self::$cheminLogs = $nouveauCheminLogs; |
} |
public static function getCheminLog() { |
return self::$cheminLogs; |
} |
public static function setTimeZone($NouvelleTimeZone) { |
self::$timeZone = $NouvelleTimeZone; |
} |
public static function setTailleMax($nouvelleTailleMax) { |
self::$tailleMax = $nouvelleTailleMax; |
} |
/** |
* Fonction qui renvoie l'instance de classe en assurant son unicité, c'est l'unique méthode qui doit être |
* utilisée pour récupérer l'objet Registre |
* @return Log le gestionnaire de log en cours |
*/ |
public static function getInstance() { |
if (self::$log instanceof Log) { |
return self::$log; |
} |
self::$log = new Log(); |
return self::$log; |
} |
/** |
* Ajoute une entrée au log spécifié par le paramètre $nomFichier |
* @param string $nomFichier le nom du fichier dans lequel écrire |
*/ |
public static function ajouterEntree($nomFichier,$entree,$mode='a+') { |
if(self::$droitLogger) { |
$date = "\n"."\n".date('d m Y H:i')."\n" ; |
// si le fichier est déjà dans le tableau et qu'on peut y écrire |
if(self::verifierOuvrirFichier($nomFichier,$mode)) { |
// on y écrit le message de log |
fwrite(self::$fichiersLog[$nomFichier],$date.$entree); |
// on vérifie si le fichier ne dépasse pas la taille maximale |
self::verifierTailleFichierOuArchiver($nomFichier); |
} else { |
// sinon on interdit l'écriture |
self::desactiverEcriture($nomFichier); |
} |
} |
} |
/** |
* Vide un fichier log indiqué |
* @param string $nomFichier le nom du fichier à vider |
*/ |
public static function viderLog($nomFichier) { |
ajouterEntree($nomFichier,'','w'); |
} |
/** |
* Vérifie la présence d'un fichier dans le tableau, ses droits d'écriture, |
* l'ouvre si nécessaire |
* @param string $nomFichier le nom du fichier dont on doit vérifier la présence |
* @return boolean true si le fichier est ouvert ou maintenant accessible, false sinon |
*/ |
public static function verifierOuvrirFichier($nomFichier,$mode) { |
// le fichier est il déjà ouvert ? |
if(in_array($nomFichier,self::$fichiersLog)) { |
// si oui peut on y écrire ? |
if(is_writable(self::$cheminLogs.$nomFichier.self::$ext)) { |
// si oui on renvoie le descripteur |
return true; |
} |
return false; |
} else { |
// sinon on l'ouvre |
$fp = @fopen(self::$cheminLogs.$nomFichier.self::$ext,$mode); |
// si l'ouverture a réussi et si le fichier a les droits d'écriture |
if($fp && is_writable(self::$cheminLogs.$nomFichier.self::$ext)) { |
// si oui on renvoie le descripteur qu'on ajoute au tableau |
self::$fichiersLog[$nomFichier] = $fp; |
return true; |
} |
return false; |
} |
} |
/** |
* Vérifie la taille d'un fichier donné et si celle ci est trop importante |
* archive le fichier de log |
* @param string $nomFichier nom du fichier à vérifier |
*/ |
private static function verifierTailleFichierOuArchiver($nomFichier) { |
if(filesize(self::$cheminLogs.$nomFichier.self::$ext) > self::$tailleMax) { |
rename(self::$cheminLogs.$nomFichier.self::$ext,self::$cheminLogs.$nomFichier.date('d_m_Y_H:i').self::$ext); |
self::ajouterEntree($nomFichier,''); |
} |
} |
/** |
* Désactive l'écriture du log et envoie un message au gestionnaire d'erreurs |
* @param string $nomFichier le nom du fichier qui a causé l'erreur |
*/ |
private static function desactiverEcriture($nomFichier = '') { |
self::$droitLogger = false; |
if($nomFichier != '') { |
$fichierDossier = 'fichier '.$nomFichier ; |
} else { |
$fichierDossier = 'dossier des logs'; |
} |
} |
/** |
* destructeur de classe, ferme les descripteurs ouverts |
*/ |
public function __destruct() { |
foreach(self::$fichiersLog as $nomFichier => $fp) { |
fclose($fp); |
} |
} |
} |
?> |
/trunk/jrest/lib/zip.php |
---|
New file |
0,0 → 1,190 |
<?php |
/* vim: set expandtab sw=4 ts=4 sts=4: */ |
/** |
* |
* @version $Id: zip.lib.php 10240 2007-04-01 11:02:46Z cybot_tm $ |
*/ |
/** |
* Zip file creation class. |
* Makes zip files. |
* |
* Based on : |
* |
* http://www.zend.com/codex.php?id=535&single=1 |
* By Eric Mueller <eric@themepark.com> |
* |
* http://www.zend.com/codex.php?id=470&single=1 |
* by Denis125 <webmaster@atlant.ru> |
* |
* a patch from Peter Listiak <mlady@users.sourceforge.net> for last modified |
* date and time of the compressed file |
* |
* Official ZIP file format: http://www.pkware.com/appnote.txt |
* |
* @access public |
*/ |
class zipfile |
{ |
/** |
* Array to store compressed data |
* |
* @var array $datasec |
*/ |
var $datasec = array(); |
/** |
* Central directory |
* |
* @var array $ctrl_dir |
*/ |
var $ctrl_dir = array(); |
/** |
* End of central directory record |
* |
* @var string $eof_ctrl_dir |
*/ |
var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00"; |
/** |
* Last offset position |
* |
* @var integer $old_offset |
*/ |
var $old_offset = 0; |
/** |
* Converts an Unix timestamp to a four byte DOS date and time format (date |
* in high two bytes, time in low two bytes allowing magnitude comparison). |
* |
* @param integer the current Unix timestamp |
* |
* @return integer the current date in a four byte DOS format |
* |
* @access private |
*/ |
function unix2DosTime($unixtime = 0) { |
$timearray = ($unixtime == 0) ? getdate() : getdate($unixtime); |
if ($timearray['year'] < 1980) { |
$timearray['year'] = 1980; |
$timearray['mon'] = 1; |
$timearray['mday'] = 1; |
$timearray['hours'] = 0; |
$timearray['minutes'] = 0; |
$timearray['seconds'] = 0; |
} // end if |
return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | |
($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1); |
} // end of the 'unix2DosTime()' method |
/** |
* Adds "file" to archive |
* |
* @param string file contents |
* @param string name of the file in the archive (may contains the path) |
* @param integer the current timestamp |
* |
* @access public |
*/ |
function addFile($data, $name, $time = 0) |
{ |
$name = str_replace('\\', '/', $name); |
$dtime = dechex($this->unix2DosTime($time)); |
$hexdtime = '\x' . $dtime[6] . $dtime[7] |
. '\x' . $dtime[4] . $dtime[5] |
. '\x' . $dtime[2] . $dtime[3] |
. '\x' . $dtime[0] . $dtime[1]; |
eval('$hexdtime = "' . $hexdtime . '";'); |
$fr = "\x50\x4b\x03\x04"; |
$fr .= "\x14\x00"; // ver needed to extract |
$fr .= "\x00\x00"; // gen purpose bit flag |
$fr .= "\x08\x00"; // compression method |
$fr .= $hexdtime; // last mod time and date |
// "local file header" segment |
$unc_len = strlen($data); |
$crc = crc32($data); |
$zdata = gzcompress($data); |
$zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug |
$c_len = strlen($zdata); |
$fr .= pack('V', $crc); // crc32 |
$fr .= pack('V', $c_len); // compressed filesize |
$fr .= pack('V', $unc_len); // uncompressed filesize |
$fr .= pack('v', strlen($name)); // length of filename |
$fr .= pack('v', 0); // extra field length |
$fr .= $name; |
// "file data" segment |
$fr .= $zdata; |
// "data descriptor" segment (optional but necessary if archive is not |
// served as file) |
// nijel(2004-10-19): this seems not to be needed at all and causes |
// problems in some cases (bug #1037737) |
//$fr .= pack('V', $crc); // crc32 |
//$fr .= pack('V', $c_len); // compressed filesize |
//$fr .= pack('V', $unc_len); // uncompressed filesize |
// add this entry to array |
$this -> datasec[] = $fr; |
// now add to central directory record |
$cdrec = "\x50\x4b\x01\x02"; |
$cdrec .= "\x00\x00"; // version made by |
$cdrec .= "\x14\x00"; // version needed to extract |
$cdrec .= "\x00\x00"; // gen purpose bit flag |
$cdrec .= "\x08\x00"; // compression method |
$cdrec .= $hexdtime; // last mod time & date |
$cdrec .= pack('V', $crc); // crc32 |
$cdrec .= pack('V', $c_len); // compressed filesize |
$cdrec .= pack('V', $unc_len); // uncompressed filesize |
$cdrec .= pack('v', strlen($name)); // length of filename |
$cdrec .= pack('v', 0); // extra field length |
$cdrec .= pack('v', 0); // file comment length |
$cdrec .= pack('v', 0); // disk number start |
$cdrec .= pack('v', 0); // internal file attributes |
$cdrec .= pack('V', 32); // external file attributes - 'archive' bit set |
$cdrec .= pack('V', $this -> old_offset); // relative offset of local header |
$this -> old_offset += strlen($fr); |
$cdrec .= $name; |
// optional extra field, file comment goes here |
// save to central directory |
$this -> ctrl_dir[] = $cdrec; |
} // end of the 'addFile()' method |
/** |
* Dumps out file |
* |
* @return string the zipped file |
* |
* @access public |
*/ |
function file() |
{ |
$data = implode('', $this -> datasec); |
$ctrldir = implode('', $this -> ctrl_dir); |
return |
$data . |
$ctrldir . |
$this -> eof_ctrl_dir . |
pack('v', sizeof($this -> ctrl_dir)) . // total # of entries "on this disk" |
pack('v', sizeof($this -> ctrl_dir)) . // total # of entries overall |
pack('V', strlen($ctrldir)) . // size of central dir |
pack('V', strlen($data)) . // offset to start of central dir |
"\x00\x00"; // .zip file comment length |
} // end of the 'file()' method |
} // end of the 'zipfile' class |
?> |
/trunk/jrest/lib/DB.php |
---|
New file |
0,0 → 1,1114 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */ |
// +----------------------------------------------------------------------+ |
// | PHP Version 4 | |
// +----------------------------------------------------------------------+ |
// | Copyright (c) 1997-2004 The PHP Group | |
// +----------------------------------------------------------------------+ |
// | This source file is subject to version 2.02 of the PHP license, | |
// | that is bundled with this package in the file LICENSE, and is | |
// | available at through the world-wide-web at | |
// | http://www.php.net/license/2_02.txt. | |
// | If you did not receive a copy of the PHP license and are unable to | |
// | obtain it through the world-wide-web, please send a note to | |
// | license@php.net so we can mail you a copy immediately. | |
// +----------------------------------------------------------------------+ |
// | Authors: Stig Bakken <ssb@php.net> | |
// | Tomas V.V.Cox <cox@idecnet.com> | |
// | Maintainer: Daniel Convissor <danielc@php.net> | |
// +----------------------------------------------------------------------+ |
// |
// $Id$ |
// |
// Database independent query interface. |
require_once 'PEAR.php'; |
// {{{ constants |
// {{{ error codes |
/* |
* The method mapErrorCode in each DB_dbtype implementation maps |
* native error codes to one of these. |
* |
* If you add an error code here, make sure you also add a textual |
* version of it in DB::errorMessage(). |
*/ |
define('DB_OK', 1); |
define('DB_ERROR', -1); |
define('DB_ERROR_SYNTAX', -2); |
define('DB_ERROR_CONSTRAINT', -3); |
define('DB_ERROR_NOT_FOUND', -4); |
define('DB_ERROR_ALREADY_EXISTS', -5); |
define('DB_ERROR_UNSUPPORTED', -6); |
define('DB_ERROR_MISMATCH', -7); |
define('DB_ERROR_INVALID', -8); |
define('DB_ERROR_NOT_CAPABLE', -9); |
define('DB_ERROR_TRUNCATED', -10); |
define('DB_ERROR_INVALID_NUMBER', -11); |
define('DB_ERROR_INVALID_DATE', -12); |
define('DB_ERROR_DIVZERO', -13); |
define('DB_ERROR_NODBSELECTED', -14); |
define('DB_ERROR_CANNOT_CREATE', -15); |
define('DB_ERROR_CANNOT_DELETE', -16); |
define('DB_ERROR_CANNOT_DROP', -17); |
define('DB_ERROR_NOSUCHTABLE', -18); |
define('DB_ERROR_NOSUCHFIELD', -19); |
define('DB_ERROR_NEED_MORE_DATA', -20); |
define('DB_ERROR_NOT_LOCKED', -21); |
define('DB_ERROR_VALUE_COUNT_ON_ROW', -22); |
define('DB_ERROR_INVALID_DSN', -23); |
define('DB_ERROR_CONNECT_FAILED', -24); |
define('DB_ERROR_EXTENSION_NOT_FOUND',-25); |
define('DB_ERROR_ACCESS_VIOLATION', -26); |
define('DB_ERROR_NOSUCHDB', -27); |
define('DB_ERROR_CONSTRAINT_NOT_NULL',-29); |
// }}} |
// {{{ prepared statement-related |
/* |
* These constants are used when storing information about prepared |
* statements (using the "prepare" method in DB_dbtype). |
* |
* The prepare/execute model in DB is mostly borrowed from the ODBC |
* extension, in a query the "?" character means a scalar parameter. |
* There are two extensions though, a "&" character means an opaque |
* parameter. An opaque parameter is simply a file name, the real |
* data are in that file (useful for putting uploaded files into your |
* database and such). The "!" char means a parameter that must be |
* left as it is. |
* They modify the quote behavoir: |
* DB_PARAM_SCALAR (?) => 'original string quoted' |
* DB_PARAM_OPAQUE (&) => 'string from file quoted' |
* DB_PARAM_MISC (!) => original string |
*/ |
define('DB_PARAM_SCALAR', 1); |
define('DB_PARAM_OPAQUE', 2); |
define('DB_PARAM_MISC', 3); |
// }}} |
// {{{ binary data-related |
/* |
* These constants define different ways of returning binary data |
* from queries. Again, this model has been borrowed from the ODBC |
* extension. |
* |
* DB_BINMODE_PASSTHRU sends the data directly through to the browser |
* when data is fetched from the database. |
* DB_BINMODE_RETURN lets you return data as usual. |
* DB_BINMODE_CONVERT returns data as well, only it is converted to |
* hex format, for example the string "123" would become "313233". |
*/ |
define('DB_BINMODE_PASSTHRU', 1); |
define('DB_BINMODE_RETURN', 2); |
define('DB_BINMODE_CONVERT', 3); |
// }}} |
// {{{ fetch modes |
/** |
* This is a special constant that tells DB the user hasn't specified |
* any particular get mode, so the default should be used. |
*/ |
define('DB_FETCHMODE_DEFAULT', 0); |
/** |
* Column data indexed by numbers, ordered from 0 and up |
*/ |
define('DB_FETCHMODE_ORDERED', 1); |
/** |
* Column data indexed by column names |
*/ |
define('DB_FETCHMODE_ASSOC', 2); |
/** |
* Column data as object properties |
*/ |
define('DB_FETCHMODE_OBJECT', 3); |
/** |
* For multi-dimensional results: normally the first level of arrays |
* is the row number, and the second level indexed by column number or name. |
* DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays |
* is the column name, and the second level the row number. |
*/ |
define('DB_FETCHMODE_FLIPPED', 4); |
/* for compatibility */ |
define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED); |
define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC); |
define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED); |
// }}} |
// {{{ tableInfo() && autoPrepare()-related |
/** |
* these are constants for the tableInfo-function |
* they are bitwised or'ed. so if there are more constants to be defined |
* in the future, adjust DB_TABLEINFO_FULL accordingly |
*/ |
define('DB_TABLEINFO_ORDER', 1); |
define('DB_TABLEINFO_ORDERTABLE', 2); |
define('DB_TABLEINFO_FULL', 3); |
/* |
* Used by autoPrepare() |
*/ |
define('DB_AUTOQUERY_INSERT', 1); |
define('DB_AUTOQUERY_UPDATE', 2); |
// }}} |
// {{{ portability modes |
/** |
* Portability: turn off all portability features. |
* @see DB_common::setOption() |
*/ |
define('DB_PORTABILITY_NONE', 0); |
/** |
* Portability: convert names of tables and fields to lower case |
* when using the get*(), fetch*() and tableInfo() methods. |
* @see DB_common::setOption() |
*/ |
define('DB_PORTABILITY_LOWERCASE', 1); |
/** |
* Portability: right trim the data output by get*() and fetch*(). |
* @see DB_common::setOption() |
*/ |
define('DB_PORTABILITY_RTRIM', 2); |
/** |
* Portability: force reporting the number of rows deleted. |
* @see DB_common::setOption() |
*/ |
define('DB_PORTABILITY_DELETE_COUNT', 4); |
/** |
* Portability: enable hack that makes numRows() work in Oracle. |
* @see DB_common::setOption() |
*/ |
define('DB_PORTABILITY_NUMROWS', 8); |
/** |
* Portability: makes certain error messages in certain drivers compatible |
* with those from other DBMS's. |
* |
* + mysql, mysqli: change unique/primary key constraints |
* DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT |
* |
* + odbc(access): MS's ODBC driver reports 'no such field' as code |
* 07001, which means 'too few parameters.' When this option is on |
* that code gets mapped to DB_ERROR_NOSUCHFIELD. |
* |
* @see DB_common::setOption() |
*/ |
define('DB_PORTABILITY_ERRORS', 16); |
/** |
* Portability: convert null values to empty strings in data output by |
* get*() and fetch*(). |
* @see DB_common::setOption() |
*/ |
define('DB_PORTABILITY_NULL_TO_EMPTY', 32); |
/** |
* Portability: turn on all portability features. |
* @see DB_common::setOption() |
*/ |
define('DB_PORTABILITY_ALL', 63); |
// }}} |
// }}} |
// {{{ class DB |
/** |
* The main "DB" class is simply a container class with some static |
* methods for creating DB objects as well as some utility functions |
* common to all parts of DB. |
* |
* The object model of DB is as follows (indentation means inheritance): |
* |
* DB The main DB class. This is simply a utility class |
* with some "static" methods for creating DB objects as |
* well as common utility functions for other DB classes. |
* |
* DB_common The base for each DB implementation. Provides default |
* | implementations (in OO lingo virtual methods) for |
* | the actual DB implementations as well as a bunch of |
* | query utility functions. |
* | |
* +-DB_mysql The DB implementation for MySQL. Inherits DB_common. |
* When calling DB::factory or DB::connect for MySQL |
* connections, the object returned is an instance of this |
* class. |
* |
* @package DB |
* @author Stig Bakken <ssb@php.net> |
* @author Tomas V.V.Cox <cox@idecnet.com> |
* @since PHP 4.0 |
* @version $Id$ |
* @category Database |
*/ |
class DB |
{ |
// {{{ &factory() |
/** |
* Create a new DB object for the specified database type. |
* |
* Allows creation of a DB_<driver> object from which the object's |
* methods can be utilized without actually connecting to a database. |
* |
* @param string $type database type, for example "mysql" |
* @param array $options associative array of option names and values |
* |
* @return object a new DB object. On error, an error object. |
* |
* @see DB_common::setOption() |
* @access public |
*/ |
function &factory($type, $options = false) |
{ |
if (!is_array($options)) { |
$options = array('persistent' => $options); |
} |
if (isset($options['debug']) && $options['debug'] >= 2) { |
// expose php errors with sufficient debug level |
include_once "DB/{$type}.php"; |
} else { |
@include_once "DB/{$type}.php"; |
} |
$classname = "DB_${type}"; |
if (!class_exists($classname)) { |
$tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, |
"Unable to include the DB/{$type}.php file", |
'DB_Error', true); |
return $tmp; |
} |
@$obj =& new $classname; |
foreach ($options as $option => $value) { |
$test = $obj->setOption($option, $value); |
if (DB::isError($test)) { |
return $test; |
} |
} |
return $obj; |
} |
// }}} |
// {{{ &connect() |
/** |
* Create a new DB object and connect to the specified database. |
* |
* Example 1. |
* <code> <?php |
* require_once 'DB.php'; |
* |
* $dsn = 'mysql://user:password@host/database' |
* $options = array( |
* 'debug' => 2, |
* 'portability' => DB_PORTABILITY_ALL, |
* ); |
* |
* $dbh =& DB::connect($dsn, $options); |
* if (DB::isError($dbh)) { |
* die($dbh->getMessage()); |
* } |
* ?></code> |
* |
* @param mixed $dsn string "data source name" or an array in the |
* format returned by DB::parseDSN() |
* |
* @param array $options an associative array of option names and |
* their values |
* |
* @return object a newly created DB connection object, or a DB |
* error object on error |
* |
* @see DB::parseDSN(), DB_common::setOption(), DB::isError() |
* @access public |
*/ |
function &connect($dsn, $options = array()) |
{ |
$dsninfo = DB::parseDSN($dsn); |
$type = $dsninfo['phptype']; |
if (!is_array($options)) { |
/* |
* For backwards compatibility. $options used to be boolean, |
* indicating whether the connection should be persistent. |
*/ |
$options = array('persistent' => $options); |
} |
if (isset($options['debug']) && $options['debug'] >= 2) { |
// expose php errors with sufficient debug level |
include_once "DB/${type}.php"; |
} else { |
@include_once "DB/${type}.php"; |
} |
$classname = "DB_${type}"; |
if (!class_exists($classname)) { |
$tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, |
"Unable to include the DB/{$type}.php file for `$dsn'", |
'DB_Error', true); |
return $tmp; |
} |
@$obj =& new $classname; |
foreach ($options as $option => $value) { |
$test = $obj->setOption($option, $value); |
if (DB::isError($test)) { |
return $test; |
} |
} |
$err = $obj->connect($dsninfo, $obj->getOption('persistent')); |
if (DB::isError($err)) { |
$err->addUserInfo($dsn); |
return $err; |
} |
return $obj; |
} |
// }}} |
// {{{ apiVersion() |
/** |
* Return the DB API version |
* |
* @return int the DB API version number |
* |
* @access public |
*/ |
function apiVersion() |
{ |
return 2; |
} |
// }}} |
// {{{ isError() |
/** |
* Tell whether a result code from a DB method is an error |
* |
* @param int $value result code |
* |
* @return bool whether $value is an error |
* |
* @access public |
*/ |
function isError($value) |
{ |
return is_a($value, 'DB_Error'); |
} |
// }}} |
// {{{ isConnection() |
/** |
* Tell whether a value is a DB connection |
* |
* @param mixed $value value to test |
* |
* @return bool whether $value is a DB connection |
* |
* @access public |
*/ |
function isConnection($value) |
{ |
return (is_object($value) && |
is_subclass_of($value, 'db_common') && |
method_exists($value, 'simpleQuery')); |
} |
// }}} |
// {{{ isManip() |
/** |
* Tell whether a query is a data manipulation query (insert, |
* update or delete) or a data definition query (create, drop, |
* alter, grant, revoke). |
* |
* @access public |
* |
* @param string $query the query |
* |
* @return boolean whether $query is a data manipulation query |
*/ |
function isManip($query) |
{ |
$manips = 'INSERT|UPDATE|DELETE|LOAD DATA|'.'REPLACE|CREATE|DROP|'. |
'ALTER|GRANT|REVOKE|'.'LOCK|UNLOCK'; |
if (preg_match('/^\s*"?('.$manips.')\s+/i', $query)) { |
return true; |
} |
return false; |
} |
// }}} |
// {{{ errorMessage() |
/** |
* Return a textual error message for a DB error code |
* |
* @param integer $value error code |
* |
* @return string error message, or false if the error code was |
* not recognized |
*/ |
function errorMessage($value) |
{ |
static $errorMessages; |
if (!isset($errorMessages)) { |
$errorMessages = array( |
DB_ERROR => 'unknown error', |
DB_ERROR_ALREADY_EXISTS => 'already exists', |
DB_ERROR_CANNOT_CREATE => 'can not create', |
DB_ERROR_CANNOT_DELETE => 'can not delete', |
DB_ERROR_CANNOT_DROP => 'can not drop', |
DB_ERROR_CONSTRAINT => 'constraint violation', |
DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', |
DB_ERROR_DIVZERO => 'division by zero', |
DB_ERROR_INVALID => 'invalid', |
DB_ERROR_INVALID_DATE => 'invalid date or time', |
DB_ERROR_INVALID_NUMBER => 'invalid number', |
DB_ERROR_MISMATCH => 'mismatch', |
DB_ERROR_NODBSELECTED => 'no database selected', |
DB_ERROR_NOSUCHFIELD => 'no such field', |
DB_ERROR_NOSUCHTABLE => 'no such table', |
DB_ERROR_NOT_CAPABLE => 'DB backend not capable', |
DB_ERROR_NOT_FOUND => 'not found', |
DB_ERROR_NOT_LOCKED => 'not locked', |
DB_ERROR_SYNTAX => 'syntax error', |
DB_ERROR_UNSUPPORTED => 'not supported', |
DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', |
DB_ERROR_INVALID_DSN => 'invalid DSN', |
DB_ERROR_CONNECT_FAILED => 'connect failed', |
DB_OK => 'no error', |
DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', |
DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', |
DB_ERROR_NOSUCHDB => 'no such database', |
DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions', |
DB_ERROR_TRUNCATED => 'truncated' |
); |
} |
if (DB::isError($value)) { |
$value = $value->getCode(); |
} |
return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[DB_ERROR]; |
} |
// }}} |
// {{{ parseDSN() |
/** |
* Parse a data source name. |
* |
* Additional keys can be added by appending a URI query string to the |
* end of the DSN. |
* |
* The format of the supplied DSN is in its fullest form: |
* <code> |
* phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true |
* </code> |
* |
* Most variations are allowed: |
* <code> |
* phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644 |
* phptype://username:password@hostspec/database_name |
* phptype://username:password@hostspec |
* phptype://username@hostspec |
* phptype://hostspec/database |
* phptype://hostspec |
* phptype(dbsyntax) |
* phptype |
* </code> |
* |
* @param string $dsn Data Source Name to be parsed |
* |
* @return array an associative array with the following keys: |
* + phptype: Database backend used in PHP (mysql, odbc etc.) |
* + dbsyntax: Database used with regards to SQL syntax etc. |
* + protocol: Communication protocol to use (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 |
* |
* @author Tomas V.V.Cox <cox@idecnet.com> |
*/ |
function parseDSN($dsn) |
{ |
$parsed = array( |
'phptype' => false, |
'dbsyntax' => false, |
'username' => false, |
'password' => false, |
'protocol' => false, |
'hostspec' => false, |
'port' => false, |
'socket' => false, |
'database' => false, |
); |
if (is_array($dsn)) { |
$dsn = array_merge($parsed, $dsn); |
if (!$dsn['dbsyntax']) { |
$dsn['dbsyntax'] = $dsn['phptype']; |
} |
return $dsn; |
} |
// Find phptype and dbsyntax |
if (($pos = strpos($dsn, '://')) !== false) { |
$str = substr($dsn, 0, $pos); |
$dsn = substr($dsn, $pos + 3); |
} else { |
$str = $dsn; |
$dsn = null; |
} |
// Get phptype and dbsyntax |
// $str => phptype(dbsyntax) |
if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { |
$parsed['phptype'] = $arr[1]; |
$parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2]; |
} else { |
$parsed['phptype'] = $str; |
$parsed['dbsyntax'] = $str; |
} |
if (!count($dsn)) { |
return $parsed; |
} |
// Get (if found): username and password |
// $dsn => username:password@protocol+hostspec/database |
if (($at = strrpos($dsn,'@')) !== false) { |
$str = substr($dsn, 0, $at); |
$dsn = substr($dsn, $at + 1); |
if (($pos = strpos($str, ':')) !== false) { |
$parsed['username'] = rawurldecode(substr($str, 0, $pos)); |
$parsed['password'] = rawurldecode(substr($str, $pos + 1)); |
} else { |
$parsed['username'] = rawurldecode($str); |
} |
} |
// Find protocol and hostspec |
// $dsn => proto(proto_opts)/database |
if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { |
$proto = $match[1]; |
$proto_opts = $match[2] ? $match[2] : false; |
$dsn = $match[3]; |
// $dsn => protocol+hostspec/database (old format) |
} else { |
if (strpos($dsn, '+') !== false) { |
list($proto, $dsn) = explode('+', $dsn, 2); |
} |
if (strpos($dsn, '/') !== false) { |
list($proto_opts, $dsn) = explode('/', $dsn, 2); |
} else { |
$proto_opts = $dsn; |
$dsn = null; |
} |
} |
// process the different protocol options |
$parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; |
$proto_opts = rawurldecode($proto_opts); |
if ($parsed['protocol'] == 'tcp') { |
if (strpos($proto_opts, ':') !== false) { |
list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts); |
} else { |
$parsed['hostspec'] = $proto_opts; |
} |
} elseif ($parsed['protocol'] == 'unix') { |
$parsed['socket'] = $proto_opts; |
} |
// Get dabase if any |
// $dsn => database |
if ($dsn) { |
// /database |
if (($pos = strpos($dsn, '?')) === false) { |
$parsed['database'] = $dsn; |
// /database?param1=value1¶m2=value2 |
} else { |
$parsed['database'] = substr($dsn, 0, $pos); |
$dsn = substr($dsn, $pos + 1); |
if (strpos($dsn, '&') !== false) { |
$opts = explode('&', $dsn); |
} else { // database?param1=value1 |
$opts = array($dsn); |
} |
foreach ($opts as $opt) { |
list($key, $value) = explode('=', $opt); |
if (!isset($parsed[$key])) { |
// don't allow params overwrite |
$parsed[$key] = rawurldecode($value); |
} |
} |
} |
} |
return $parsed; |
} |
// }}} |
// {{{ assertExtension() |
/** |
* Load a PHP database extension if it is not loaded already. |
* |
* @access public |
* |
* @param string $name the base name of the extension (without the .so or |
* .dll suffix) |
* |
* @return boolean true if the extension was already or successfully |
* loaded, false if it could not be loaded |
*/ |
function assertExtension($name) |
{ |
if (!extension_loaded($name)) { |
$dlext = OS_WINDOWS ? '.dll' : '.so'; |
$dlprefix = OS_WINDOWS ? 'php_' : ''; |
@dl($dlprefix . $name . $dlext); |
return extension_loaded($name); |
} |
return true; |
} |
// }}} |
} |
// }}} |
// {{{ class DB_Error |
/** |
* DB_Error implements a class for reporting portable database error |
* messages. |
* |
* @package DB |
* @author Stig Bakken <ssb@php.net> |
*/ |
class DB_Error extends PEAR_Error |
{ |
// {{{ constructor |
/** |
* DB_Error constructor. |
* |
* @param mixed $code DB error code, or string with error message. |
* @param integer $mode what "error mode" to operate in |
* @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER |
* @param mixed $debuginfo additional debug info, such as the last query |
* |
* @access public |
* |
* @see PEAR_Error |
*/ |
function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN, |
$level = E_USER_NOTICE, $debuginfo = null) |
{ |
if (is_int($code)) { |
$this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, $mode, $level, $debuginfo); |
} else { |
$this->PEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo); |
} |
} |
// }}} |
} |
// }}} |
// {{{ class DB_result |
/** |
* This class implements a wrapper for a DB result set. |
* A new instance of this class will be returned by the DB implementation |
* after processing a query that returns data. |
* |
* @package DB |
* @author Stig Bakken <ssb@php.net> |
*/ |
class DB_result |
{ |
// {{{ properties |
var $dbh; |
var $result; |
var $row_counter = null; |
/** |
* for limit queries, the row to start fetching |
* @var integer |
*/ |
var $limit_from = null; |
/** |
* for limit queries, the number of rows to fetch |
* @var integer |
*/ |
var $limit_count = null; |
// }}} |
// {{{ constructor |
/** |
* DB_result constructor. |
* @param resource &$dbh DB object reference |
* @param resource $result result resource id |
* @param array $options assoc array with optional result options |
*/ |
function DB_result(&$dbh, $result, $options = array()) |
{ |
$this->dbh = &$dbh; |
$this->result = $result; |
foreach ($options as $key => $value) { |
$this->setOption($key, $value); |
} |
$this->limit_type = $dbh->features['limit']; |
$this->autofree = $dbh->options['autofree']; |
$this->fetchmode = $dbh->fetchmode; |
$this->fetchmode_object_class = $dbh->fetchmode_object_class; |
} |
function setOption($key, $value = null) |
{ |
switch ($key) { |
case 'limit_from': |
$this->limit_from = $value; break; |
case 'limit_count': |
$this->limit_count = $value; break; |
} |
} |
// }}} |
// {{{ fetchRow() |
/** |
* Fetch a row of data and return it by reference into an array. |
* |
* The type of array returned can be controlled either by setting this |
* method's <var>$fetchmode</var> parameter or by changing the default |
* fetch mode setFetchMode() before calling this method. |
* |
* There are two options for standardizing the information returned |
* from databases, ensuring their values are consistent when changing |
* DBMS's. These portability options can be turned on when creating a |
* new DB object or by using setOption(). |
* |
* + <samp>DB_PORTABILITY_LOWERCASE</samp> |
* convert names of fields to lower case |
* |
* + <samp>DB_PORTABILITY_RTRIM</samp> |
* right trim the data |
* |
* @param int $fetchmode how the resulting array should be indexed |
* @param int $rownum the row number to fetch |
* |
* @return array a row of data, null on no more rows or PEAR_Error |
* object on error |
* |
* @see DB_common::setOption(), DB_common::setFetchMode() |
* @access public |
*/ |
function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null) |
{ |
if ($fetchmode === DB_FETCHMODE_DEFAULT) { |
$fetchmode = $this->fetchmode; |
} |
if ($fetchmode === DB_FETCHMODE_OBJECT) { |
$fetchmode = DB_FETCHMODE_ASSOC; |
$object_class = $this->fetchmode_object_class; |
} |
if ($this->limit_from !== null) { |
if ($this->row_counter === null) { |
$this->row_counter = $this->limit_from; |
// Skip rows |
if ($this->limit_type == false) { |
$i = 0; |
while ($i++ < $this->limit_from) { |
$this->dbh->fetchInto($this->result, $arr, $fetchmode); |
} |
} |
} |
if ($this->row_counter >= ( |
$this->limit_from + $this->limit_count)) |
{ |
if ($this->autofree) { |
$this->free(); |
} |
$tmp = null; |
return $tmp; |
} |
if ($this->limit_type == 'emulate') { |
$rownum = $this->row_counter; |
} |
$this->row_counter++; |
} |
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); |
if ($res === DB_OK) { |
if (isset($object_class)) { |
// default mode specified in DB_common::fetchmode_object_class property |
if ($object_class == 'stdClass') { |
$arr = (object) $arr; |
} else { |
$arr = &new $object_class($arr); |
} |
} |
return $arr; |
} |
if ($res == null && $this->autofree) { |
$this->free(); |
} |
return $res; |
} |
// }}} |
// {{{ fetchInto() |
/** |
* Fetch a row of data into an array which is passed by reference. |
* |
* The type of array returned can be controlled either by setting this |
* method's <var>$fetchmode</var> parameter or by changing the default |
* fetch mode setFetchMode() before calling this method. |
* |
* There are two options for standardizing the information returned |
* from databases, ensuring their values are consistent when changing |
* DBMS's. These portability options can be turned on when creating a |
* new DB object or by using setOption(). |
* |
* + <samp>DB_PORTABILITY_LOWERCASE</samp> |
* convert names of fields to lower case |
* |
* + <samp>DB_PORTABILITY_RTRIM</samp> |
* right trim the data |
* |
* @param array &$arr (reference) array where data from the row |
* should be placed |
* @param int $fetchmode how the resulting array should be indexed |
* @param int $rownum the row number to fetch |
* |
* @return mixed DB_OK on success, null on no more rows or |
* a DB_Error object on error |
* |
* @see DB_common::setOption(), DB_common::setFetchMode() |
* @access public |
*/ |
function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null) |
{ |
if ($fetchmode === DB_FETCHMODE_DEFAULT) { |
$fetchmode = $this->fetchmode; |
} |
if ($fetchmode === DB_FETCHMODE_OBJECT) { |
$fetchmode = DB_FETCHMODE_ASSOC; |
$object_class = $this->fetchmode_object_class; |
} |
if ($this->limit_from !== null) { |
if ($this->row_counter === null) { |
$this->row_counter = $this->limit_from; |
// Skip rows |
if ($this->limit_type == false) { |
$i = 0; |
while ($i++ < $this->limit_from) { |
$this->dbh->fetchInto($this->result, $arr, $fetchmode); |
} |
} |
} |
if ($this->row_counter >= ( |
$this->limit_from + $this->limit_count)) |
{ |
if ($this->autofree) { |
$this->free(); |
} |
return null; |
} |
if ($this->limit_type == 'emulate') { |
$rownum = $this->row_counter; |
} |
$this->row_counter++; |
} |
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); |
if ($res === DB_OK) { |
if (isset($object_class)) { |
// default mode specified in DB_common::fetchmode_object_class property |
if ($object_class == 'stdClass') { |
$arr = (object) $arr; |
} else { |
$arr = new $object_class($arr); |
} |
} |
return DB_OK; |
} |
if ($res == null && $this->autofree) { |
$this->free(); |
} |
return $res; |
} |
// }}} |
// {{{ numCols() |
/** |
* Get the the number of columns in a result set. |
* |
* @return int the number of columns, or a DB error |
* |
* @access public |
*/ |
function numCols() |
{ |
return $this->dbh->numCols($this->result); |
} |
// }}} |
// {{{ numRows() |
/** |
* Get the number of rows in a result set. |
* |
* @return int the number of rows, or a DB error |
* |
* @access public |
*/ |
function numRows() |
{ |
return $this->dbh->numRows($this->result); |
} |
// }}} |
// {{{ nextResult() |
/** |
* Get the next result if a batch of queries was executed. |
* |
* @return bool true if a new result is available or false if not. |
* |
* @access public |
*/ |
function nextResult() |
{ |
return $this->dbh->nextResult($this->result); |
} |
// }}} |
// {{{ free() |
/** |
* Frees the resources allocated for this result set. |
* @return int error code |
* |
* @access public |
*/ |
function free() |
{ |
$err = $this->dbh->freeResult($this->result); |
if (DB::isError($err)) { |
return $err; |
} |
$this->result = false; |
return true; |
} |
// }}} |
// {{{ tableInfo() |
/** |
* @deprecated |
* @internal |
* @see DB_common::tableInfo() |
*/ |
function tableInfo($mode = null) |
{ |
if (is_string($mode)) { |
return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA); |
} |
return $this->dbh->tableInfo($this, $mode); |
} |
// }}} |
// {{{ getRowCounter() |
/** |
* returns the actual row number |
* @return integer |
*/ |
function getRowCounter() |
{ |
return $this->row_counter; |
} |
// }}} |
} |
// }}} |
// {{{ class DB_row |
/** |
* Pear DB Row Object |
* @see DB_common::setFetchMode() |
*/ |
class DB_row |
{ |
// {{{ constructor |
/** |
* constructor |
* |
* @param resource row data as array |
*/ |
function DB_row(&$arr) |
{ |
foreach ($arr as $key => $value) { |
$this->$key = &$arr[$key]; |
} |
} |
// }}} |
} |
// }}} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/trunk/jrest/lib/PEAR.php |
---|
New file |
0,0 → 1,971 |
<?php |
// |
// +----------------------------------------------------------------------+ |
// | PEAR, the PHP Extension and Application Repository | |
// +----------------------------------------------------------------------+ |
// | Copyright (c) 1997-2004 The PHP Group | |
// +----------------------------------------------------------------------+ |
// | This source file is subject to version 2.0 of the PHP license, | |
// | that is bundled with this package in the file LICENSE, and is | |
// | available through the world-wide-web at the following url: | |
// | 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 world-wide-web, please send a note to | |
// | license@php.net so we can mail you a copy immediately. | |
// +----------------------------------------------------------------------+ |
// | Authors: Sterling Hughes <sterling@php.net> | |
// | Stig Bakken <ssb@php.net> | |
// | Tomas V.V.Cox <cox@idecnet.com> | |
// +----------------------------------------------------------------------+ |
// |
// $Id$ |
// |
define('PEAR_ERROR_RETURN', 1); |
define('PEAR_ERROR_PRINT', 2); |
define('PEAR_ERROR_TRIGGER', 4); |
define('PEAR_ERROR_DIE', 8); |
define('PEAR_ERROR_CALLBACK', 16); |
define('PEAR_ERROR_EXCEPTION', 32); |
define('PEAR_ZE2', (function_exists('version_compare') && |
version_compare(zend_version(), "2-dev", "ge"))); |
if (substr(PHP_OS, 0, 3) == 'WIN') { |
define('OS_WINDOWS', true); |
define('OS_UNIX', false); |
define('PEAR_OS', 'Windows'); |
} else { |
define('OS_WINDOWS', false); |
define('OS_UNIX', true); |
define('PEAR_OS', 'Unix'); // blatant assumption |
} |
// instant backwards compatibility |
if (!defined('PATH_SEPARATOR')) { |
if (OS_WINDOWS) { |
define('PATH_SEPARATOR', ';'); |
} else { |
define('PATH_SEPARATOR', ':'); |
} |
} |
$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; |
$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; |
$GLOBALS['_PEAR_destructor_object_list'] = array(); |
$GLOBALS['_PEAR_shutdown_funcs'] = array(); |
$GLOBALS['_PEAR_error_handler_stack'] = array(); |
ini_set('track_errors', true); |
/** |
* Base class for other PEAR classes. Provides rudimentary |
* emulation of destructors. |
* |
* If you want a destructor in your class, inherit PEAR and make a |
* destructor method called _yourclassname (same name as the |
* constructor, but with a "_" prefix). Also, in your constructor you |
* have to call the PEAR constructor: $this->PEAR();. |
* The destructor method will be called without parameters. Note that |
* at in some SAPI implementations (such as Apache), any output during |
* the request shutdown (in which destructors are called) seems to be |
* discarded. If you need to get any debug information from your |
* destructor, use error_log(), syslog() or something similar. |
* |
* IMPORTANT! To use the emulated destructors you need to create the |
* objects by reference: $obj =& new PEAR_child; |
* |
* @since PHP 4.0.2 |
* @author Stig Bakken <ssb@php.net> |
* @see http://pear.php.net/manual/ |
*/ |
class PEAR |
{ |
// {{{ properties |
/** |
* Whether to enable internal debug messages. |
* |
* @var bool |
* @access private |
*/ |
var $_debug = false; |
/** |
* Default error mode for this object. |
* |
* @var int |
* @access private |
*/ |
var $_default_error_mode = null; |
/** |
* Default error options used for this object when error mode |
* is PEAR_ERROR_TRIGGER. |
* |
* @var int |
* @access private |
*/ |
var $_default_error_options = null; |
/** |
* Default error handler (callback) for this object, if error mode is |
* PEAR_ERROR_CALLBACK. |
* |
* @var string |
* @access private |
*/ |
var $_default_error_handler = ''; |
/** |
* Which class to use for error objects. |
* |
* @var string |
* @access private |
*/ |
var $_error_class = 'PEAR_Error'; |
/** |
* An array of expected errors. |
* |
* @var array |
* @access private |
*/ |
var $_expected_errors = array(); |
// }}} |
// {{{ constructor |
/** |
* Constructor. Registers this object in |
* $_PEAR_destructor_object_list for destructor emulation if a |
* destructor object exists. |
* |
* @param string $error_class (optional) which class to use for |
* error objects, defaults to PEAR_Error. |
* @access public |
* @return void |
*/ |
function PEAR($error_class = null) |
{ |
$classname = get_class($this); |
if ($this->_debug) { |
print "PEAR constructor called, class=$classname\n"; |
} |
if ($error_class !== null) { |
$this->_error_class = $error_class; |
} |
while ($classname) { |
$destructor = "_$classname"; |
if (method_exists($this, $destructor)) { |
global $_PEAR_destructor_object_list; |
$_PEAR_destructor_object_list[] = &$this; |
break; |
} else { |
$classname = get_parent_class($classname); |
} |
} |
} |
// }}} |
// {{{ destructor |
/** |
* Destructor (the emulated type of...). Does nothing right now, |
* but is included for forward compatibility, so subclass |
* destructors should always call it. |
* |
* See the note in the class desciption about output from |
* destructors. |
* |
* @access public |
* @return void |
*/ |
function _PEAR() { |
if ($this->_debug) { |
printf("PEAR destructor called, class=%s\n", get_class($this)); |
} |
} |
// }}} |
// {{{ getStaticProperty() |
/** |
* 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: $myVar = &PEAR::getStaticProperty('myVar'); |
* You MUST use a reference, or they will not persist! |
* |
* @access public |
* @param string $class The calling classname, to prevent clashes |
* @param string $var The variable to retrieve. |
* @return mixed A reference to the variable. If not set it will be |
* auto initialised to NULL. |
*/ |
function &getStaticProperty($class, $var) |
{ |
static $properties; |
return $properties[$class][$var]; |
} |
// }}} |
// {{{ registerShutdownFunc() |
/** |
* Use this function to register a shutdown method for static |
* classes. |
* |
* @access public |
* @param mixed $func The function name (or array of class/method) to call |
* @param mixed $args The arguments to pass to the function |
* @return void |
*/ |
function registerShutdownFunc($func, $args = array()) |
{ |
$GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); |
} |
// }}} |
// {{{ isError() |
/** |
* Tell whether a value is a PEAR error. |
* |
* @param mixed $data the value to test |
* @param int $code if $data is an error object, return true |
* only if $code is a string and |
* $obj->getMessage() == $code or |
* $code is an integer and $obj->getCode() == $code |
* @access public |
* @return bool true if parameter is an error |
*/ |
function isError($data, $code = null) |
{ |
if (is_a($data, 'PEAR_Error')) { |
if (is_null($code)) { |
return true; |
} elseif (is_string($code)) { |
return $data->getMessage() == $code; |
} else { |
return $data->getCode() == $code; |
} |
} |
return false; |
} |
// }}} |
// {{{ setErrorHandling() |
/** |
* Sets how errors generated by this object should be handled. |
* Can be invoked both in objects and statically. If called |
* statically, setErrorHandling sets the default behaviour for all |
* PEAR objects. If called in an object, setErrorHandling sets |
* the default behaviour for that object. |
* |
* @param int $mode |
* One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, |
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, |
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. |
* |
* @param mixed $options |
* When $mode is PEAR_ERROR_TRIGGER, this is the error level (one |
* of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). |
* |
* When $mode is PEAR_ERROR_CALLBACK, this parameter is expected |
* to be the callback function or method. A callback |
* function is a string with the name of the function, a |
* callback method is an array of two elements: the element |
* at index 0 is the object, and the element at index 1 is |
* the name of the method to call in the object. |
* |
* When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is |
* a printf format string used when printing the error |
* message. |
* |
* @access public |
* @return void |
* @see PEAR_ERROR_RETURN |
* @see PEAR_ERROR_PRINT |
* @see PEAR_ERROR_TRIGGER |
* @see PEAR_ERROR_DIE |
* @see PEAR_ERROR_CALLBACK |
* @see PEAR_ERROR_EXCEPTION |
* |
* @since PHP 4.0.5 |
*/ |
function setErrorHandling($mode = null, $options = null) |
{ |
if (isset($this) && is_a($this, 'PEAR')) { |
$setmode = &$this->_default_error_mode; |
$setoptions = &$this->_default_error_options; |
} else { |
$setmode = &$GLOBALS['_PEAR_default_error_mode']; |
$setoptions = &$GLOBALS['_PEAR_default_error_options']; |
} |
switch ($mode) { |
case PEAR_ERROR_RETURN: |
case PEAR_ERROR_PRINT: |
case PEAR_ERROR_TRIGGER: |
case PEAR_ERROR_DIE: |
case PEAR_ERROR_EXCEPTION: |
case null: |
$setmode = $mode; |
$setoptions = $options; |
break; |
case PEAR_ERROR_CALLBACK: |
$setmode = $mode; |
// class/object method callback |
if (is_callable($options)) { |
$setoptions = $options; |
} else { |
trigger_error("invalid error callback", E_USER_WARNING); |
} |
break; |
default: |
trigger_error("invalid error mode", E_USER_WARNING); |
break; |
} |
} |
// }}} |
// {{{ expectError() |
/** |
* 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. |
* |
* Note that this method can not be called statically |
* |
* @param mixed $code a single error code or an array of error codes to expect |
* |
* @return int the new depth of the "expected errors" stack |
* @access public |
*/ |
function expectError($code = '*') |
{ |
if (is_array($code)) { |
array_push($this->_expected_errors, $code); |
} else { |
array_push($this->_expected_errors, array($code)); |
} |
return sizeof($this->_expected_errors); |
} |
// }}} |
// {{{ popExpect() |
/** |
* This method pops one element off the expected error codes |
* stack. |
* |
* @return array the list of error codes that were popped |
*/ |
function popExpect() |
{ |
return array_pop($this->_expected_errors); |
} |
// }}} |
// {{{ _checkDelExpect() |
/** |
* This method checks unsets an error code if available |
* |
* @param mixed error code |
* @return bool true if the error code was unset, false otherwise |
* @access private |
* @since PHP 4.3.0 |
*/ |
function _checkDelExpect($error_code) |
{ |
$deleted = false; |
foreach ($this->_expected_errors AS $key => $error_array) { |
if (in_array($error_code, $error_array)) { |
unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); |
$deleted = true; |
} |
// clean up empty arrays |
if (0 == count($this->_expected_errors[$key])) { |
unset($this->_expected_errors[$key]); |
} |
} |
return $deleted; |
} |
// }}} |
// {{{ delExpect() |
/** |
* This method deletes all occurences of the specified element from |
* the expected error codes stack. |
* |
* @param mixed $error_code error code that should be deleted |
* @return mixed list of error codes that were deleted or error |
* @access public |
* @since PHP 4.3.0 |
*/ |
function delExpect($error_code) |
{ |
$deleted = false; |
if ((is_array($error_code) && (0 != count($error_code)))) { |
// $error_code is a non-empty array here; |
// we walk through it trying to unset all |
// values |
foreach($error_code as $key => $error) { |
if ($this->_checkDelExpect($error)) { |
$deleted = true; |
} else { |
$deleted = false; |
} |
} |
return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME |
} elseif (!empty($error_code)) { |
// $error_code comes alone, trying to unset it |
if ($this->_checkDelExpect($error_code)) { |
return true; |
} else { |
return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME |
} |
} else { |
// $error_code is empty |
return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME |
} |
} |
// }}} |
// {{{ raiseError() |
/** |
* This method is a wrapper that returns an instance of the |
* configured error class with this object's default error |
* handling applied. If the $mode and $options parameters are not |
* specified, the object's defaults are used. |
* |
* @param mixed $message a text error message or a PEAR error object |
* |
* @param int $code a numeric error code (it is up to your class |
* to define these if you want to use codes) |
* |
* @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, |
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, |
* PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION. |
* |
* @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter |
* specifies the PHP-internal error level (one of |
* E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). |
* If $mode is PEAR_ERROR_CALLBACK, this |
* parameter specifies the callback function or |
* method. In other error modes this parameter |
* is ignored. |
* |
* @param string $userinfo If you need to pass along for example debug |
* information, this parameter is meant for that. |
* |
* @param string $error_class The returned error object will be |
* instantiated from this class, if specified. |
* |
* @param bool $skipmsg If true, raiseError will only pass error codes, |
* the error message parameter will be dropped. |
* |
* @access public |
* @return object a PEAR error object |
* @see PEAR::setErrorHandling |
* @since PHP 4.0.5 |
*/ |
function raiseError($message = null, |
$code = null, |
$mode = null, |
$options = null, |
$userinfo = null, |
$error_class = null, |
$skipmsg = false) |
{ |
// The error is yet a PEAR error object |
if (is_object($message)) { |
$code = $message->getCode(); |
$userinfo = $message->getUserInfo(); |
$error_class = $message->getType(); |
$message->error_message_prefix = ''; |
$message = $message->getMessage(); |
} |
if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) { |
if ($exp[0] == "*" || |
(is_int(reset($exp)) && in_array($code, $exp)) || |
(is_string(reset($exp)) && in_array($message, $exp))) { |
$mode = PEAR_ERROR_RETURN; |
} |
} |
// No mode given, try global ones |
if ($mode === null) { |
// Class error handler |
if (isset($this) && isset($this->_default_error_mode)) { |
$mode = $this->_default_error_mode; |
$options = $this->_default_error_options; |
// Global error handler |
} elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { |
$mode = $GLOBALS['_PEAR_default_error_mode']; |
$options = $GLOBALS['_PEAR_default_error_options']; |
} |
} |
if ($error_class !== null) { |
$ec = $error_class; |
} elseif (isset($this) && isset($this->_error_class)) { |
$ec = $this->_error_class; |
} else { |
$ec = 'PEAR_Error'; |
} |
if ($skipmsg) { |
return new $ec($code, $mode, $options, $userinfo); |
} else { |
return new $ec($message, $code, $mode, $options, $userinfo); |
} |
} |
// }}} |
// {{{ throwError() |
/** |
* Simpler form of raiseError with fewer options. In most cases |
* message, code and userinfo are enough. |
* |
* @param string $message |
* |
*/ |
function throwError($message = null, |
$code = null, |
$userinfo = null) |
{ |
if (isset($this) && is_subclass_of($this, 'PEAR_Error')) { |
return $this->raiseError($message, $code, null, null, $userinfo); |
} else { |
return PEAR::raiseError($message, $code, null, null, $userinfo); |
} |
} |
// }}} |
// {{{ pushErrorHandling() |
/** |
* Push a new error handler on top of the error handler options stack. With this |
* you can easily override the actual error handler for some code and restore |
* it later with popErrorHandling. |
* |
* @param mixed $mode (same as setErrorHandling) |
* @param mixed $options (same as setErrorHandling) |
* |
* @return bool Always true |
* |
* @see PEAR::setErrorHandling |
*/ |
function pushErrorHandling($mode, $options = null) |
{ |
$stack = &$GLOBALS['_PEAR_error_handler_stack']; |
if (isset($this) && is_a($this, 'PEAR')) { |
$def_mode = &$this->_default_error_mode; |
$def_options = &$this->_default_error_options; |
} else { |
$def_mode = &$GLOBALS['_PEAR_default_error_mode']; |
$def_options = &$GLOBALS['_PEAR_default_error_options']; |
} |
$stack[] = array($def_mode, $def_options); |
if (isset($this) && is_a($this, 'PEAR')) { |
$this->setErrorHandling($mode, $options); |
} else { |
PEAR::setErrorHandling($mode, $options); |
} |
$stack[] = array($mode, $options); |
return true; |
} |
// }}} |
// {{{ popErrorHandling() |
/** |
* Pop the last error handler used |
* |
* @return bool Always true |
* |
* @see PEAR::pushErrorHandling |
*/ |
function popErrorHandling() |
{ |
$stack = &$GLOBALS['_PEAR_error_handler_stack']; |
array_pop($stack); |
list($mode, $options) = $stack[sizeof($stack) - 1]; |
array_pop($stack); |
if (isset($this) && is_a($this, 'PEAR')) { |
$this->setErrorHandling($mode, $options); |
} else { |
PEAR::setErrorHandling($mode, $options); |
} |
return true; |
} |
// }}} |
// {{{ loadExtension() |
/** |
* OS independant PHP extension load. Remember to take care |
* on the correct extension name for case sensitive OSes. |
* |
* @param string $ext The extension name |
* @return bool Success or not on the dl() call |
*/ |
function loadExtension($ext) |
{ |
if (!extension_loaded($ext)) { |
// if either returns true dl() will produce a FATAL error, stop that |
if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { |
return false; |
} |
if (OS_WINDOWS) { |
$suffix = '.dll'; |
} elseif (PHP_OS == 'HP-UX') { |
$suffix = '.sl'; |
} elseif (PHP_OS == 'AIX') { |
$suffix = '.a'; |
} elseif (PHP_OS == 'OSX') { |
$suffix = '.bundle'; |
} else { |
$suffix = '.so'; |
} |
return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); |
} |
return true; |
} |
// }}} |
} |
// {{{ _PEAR_call_destructors() |
function _PEAR_call_destructors() |
{ |
global $_PEAR_destructor_object_list; |
if (is_array($_PEAR_destructor_object_list) && |
sizeof($_PEAR_destructor_object_list)) |
{ |
reset($_PEAR_destructor_object_list); |
while (list($k, $objref) = each($_PEAR_destructor_object_list)) { |
$classname = get_class($objref); |
while ($classname) { |
$destructor = "_$classname"; |
if (method_exists($objref, $destructor)) { |
$objref->$destructor(); |
break; |
} else { |
$classname = get_parent_class($classname); |
} |
} |
} |
// Empty the object list to ensure that destructors are |
// not called more than once. |
$_PEAR_destructor_object_list = array(); |
} |
// Now call the shutdown functions |
if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) { |
foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { |
call_user_func_array($value[0], $value[1]); |
} |
} |
} |
// }}} |
class PEAR_Error |
{ |
// {{{ properties |
var $error_message_prefix = ''; |
var $mode = PEAR_ERROR_RETURN; |
var $level = E_USER_NOTICE; |
var $code = -1; |
var $message = ''; |
var $userinfo = ''; |
var $backtrace = null; |
// }}} |
// {{{ constructor |
/** |
* PEAR_Error constructor |
* |
* @param string $message message |
* |
* @param int $code (optional) error code |
* |
* @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, |
* PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER, |
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION |
* |
* @param mixed $options (optional) error level, _OR_ in the case of |
* PEAR_ERROR_CALLBACK, the callback function or object/method |
* tuple. |
* |
* @param string $userinfo (optional) additional user/debug info |
* |
* @access public |
* |
*/ |
function PEAR_Error($message = 'unknown error', $code = null, |
$mode = null, $options = null, $userinfo = null) |
{ |
if ($mode === null) { |
$mode = PEAR_ERROR_RETURN; |
} |
$this->message = $message; |
$this->code = $code; |
$this->mode = $mode; |
$this->userinfo = $userinfo; |
if (function_exists("debug_backtrace")) { |
$this->backtrace = debug_backtrace(); |
} |
if ($mode & PEAR_ERROR_CALLBACK) { |
$this->level = E_USER_NOTICE; |
$this->callback = $options; |
} else { |
if ($options === null) { |
$options = E_USER_NOTICE; |
} |
$this->level = $options; |
$this->callback = null; |
} |
if ($this->mode & PEAR_ERROR_PRINT) { |
if (is_null($options) || is_int($options)) { |
$format = "%s"; |
} else { |
$format = $options; |
} |
printf($format, $this->getMessage()); |
} |
if ($this->mode & PEAR_ERROR_TRIGGER) { |
trigger_error($this->getMessage(), $this->level); |
} |
if ($this->mode & PEAR_ERROR_DIE) { |
$msg = $this->getMessage(); |
if (is_null($options) || is_int($options)) { |
$format = "%s"; |
if (substr($msg, -1) != "\n") { |
$msg .= "\n"; |
} |
} else { |
$format = $options; |
} |
die(sprintf($format, $msg)); |
} |
if ($this->mode & PEAR_ERROR_CALLBACK) { |
if (is_callable($this->callback)) { |
call_user_func($this->callback, $this); |
} |
} |
if (PEAR_ZE2 && $this->mode & PEAR_ERROR_EXCEPTION) { |
eval('throw $this;'); |
} |
} |
// }}} |
// {{{ getMode() |
/** |
* Get the error mode from an error object. |
* |
* @return int error mode |
* @access public |
*/ |
function getMode() { |
return $this->mode; |
} |
// }}} |
// {{{ getCallback() |
/** |
* Get the callback function/method from an error object. |
* |
* @return mixed callback function or object/method array |
* @access public |
*/ |
function getCallback() { |
return $this->callback; |
} |
// }}} |
// {{{ getMessage() |
/** |
* Get the error message from an error object. |
* |
* @return string full error message |
* @access public |
*/ |
function getMessage() |
{ |
return ($this->error_message_prefix . $this->message); |
} |
// }}} |
// {{{ getCode() |
/** |
* Get error code from an error object |
* |
* @return int error code |
* @access public |
*/ |
function getCode() |
{ |
return $this->code; |
} |
// }}} |
// {{{ getType() |
/** |
* Get the name of this error/exception. |
* |
* @return string error/exception name (type) |
* @access public |
*/ |
function getType() |
{ |
return get_class($this); |
} |
// }}} |
// {{{ getUserInfo() |
/** |
* Get additional user-supplied information. |
* |
* @return string user-supplied information |
* @access public |
*/ |
function getUserInfo() |
{ |
return $this->userinfo; |
} |
// }}} |
// {{{ getDebugInfo() |
/** |
* Get additional debug information supplied by the application. |
* |
* @return string debug information |
* @access public |
*/ |
function getDebugInfo() |
{ |
return $this->getUserInfo(); |
} |
// }}} |
// {{{ getBacktrace() |
/** |
* Get the call backtrace from where the error was generated. |
* Supported with PHP 4.3.0 or newer. |
* |
* @param int $frame (optional) what frame to fetch |
* @return array Backtrace, or NULL if not available. |
* @access public |
*/ |
function getBacktrace($frame = null) |
{ |
if ($frame === null) { |
return $this->backtrace; |
} |
return $this->backtrace[$frame]; |
} |
// }}} |
// {{{ addUserInfo() |
function addUserInfo($info) |
{ |
if (empty($this->userinfo)) { |
$this->userinfo = $info; |
} else { |
$this->userinfo .= " ** $info"; |
} |
} |
// }}} |
// {{{ toString() |
/** |
* Make a string representation of this object. |
* |
* @return string a string with an object summary |
* @access public |
*/ |
function toString() { |
$modes = array(); |
$levels = array(E_USER_NOTICE => 'notice', |
E_USER_WARNING => 'warning', |
E_USER_ERROR => 'error'); |
if ($this->mode & PEAR_ERROR_CALLBACK) { |
if (is_array($this->callback)) { |
$callback = get_class($this->callback[0]) . '::' . |
$this->callback[1]; |
} else { |
$callback = $this->callback; |
} |
return sprintf('[%s: message="%s" code=%d mode=callback '. |
'callback=%s prefix="%s" info="%s"]', |
get_class($this), $this->message, $this->code, |
$callback, $this->error_message_prefix, |
$this->userinfo); |
} |
if ($this->mode & PEAR_ERROR_PRINT) { |
$modes[] = 'print'; |
} |
if ($this->mode & PEAR_ERROR_TRIGGER) { |
$modes[] = 'trigger'; |
} |
if ($this->mode & PEAR_ERROR_DIE) { |
$modes[] = 'die'; |
} |
if ($this->mode & PEAR_ERROR_RETURN) { |
$modes[] = 'return'; |
} |
return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. |
'prefix="%s" info="%s"]', |
get_class($this), $this->message, $this->code, |
implode("|", $modes), $levels[$this->level], |
$this->error_message_prefix, |
$this->userinfo); |
} |
// }}} |
} |
register_shutdown_function("_PEAR_call_destructors"); |
/* |
* Local Variables: |
* mode: php |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/trunk/jrest/lib/DBAccessor.php |
---|
New file |
0,0 → 1,34 |
<?php |
require_once 'JrestService.php'; |
class DBAccessor extends JrestService { |
public function connectDB($config, $base = 'database') { |
require_once 'DB.php'; |
$dsn = $config[$base]; |
$DB =& DB::connect($dsn); |
if (DB::isError($DB)) { |
die($DB->getMessage()); |
} |
$DB->query("SET NAMES 'utf8'"); |
return $DB; |
} |
public function connecterPDO($config, $base = 'database') { |
$cfg = $config[$base]; |
$dsn = $cfg['phptype'].':dbname='.$cfg['database'].';host='.$cfg['hostspec']; |
try { |
$PDO = new PDO($dsn, $cfg['username'], $cfg['password']); |
} catch (PDOException $e) { |
echo 'La connexion à la base de donnée via PDO a échouée : ' . $e->getMessage(); |
} |
// Passe en UTF-8 la connexion à la BDD |
$PDO->exec("SET NAMES 'utf8'"); |
// Affiche les erreurs détectées par PDO (sinon mode silencieux => aucune erreur affiché) |
$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
return $PDO; |
} |
} |
?> |
/trunk/jrest/lib/JrestService.php |
---|
New file |
0,0 → 1,72 |
<?php |
/** |
* PHP Version 5 |
* |
* @category PHP |
* @package jrest |
* @author aurelien <aurelien@tela-botanica.org> |
* @copyright 2009 Tela-Botanica |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @version $$id$$ |
* @link /doc/jrest/ |
*/ |
class JrestService { |
protected $config; |
protected $script_time; |
protected $max_exec_time; |
public function JrestService($config) { |
$this->config = config; |
$this->script_time = microtime(true); |
$this->max_exec_time = ini_get('max_execution_time'); |
} |
public function isAdmin($id) { |
$admins = $this->config['jrest_admin']['admin']; |
$admin_tab = split(',',$admins); |
if (in_array($id,$admin_tab)) { |
return true; |
} else { |
return false; |
} |
} |
public function controleUtilisateur($id) { |
if ($_SESSION['user']['name'] == '') { |
//cas de la session temporaire, on ne fait rien de particulier |
} else { |
if (!$this->isAdmin($_SESSION['user']['name']) && $_SESSION['user']['name'] != $id) { |
// cas d'usurpation d'identité |
print 'Accès interdit'; |
exit(); |
} |
} |
} |
public function logger($index,$chaine) { |
if(!class_exists('Log')) { |
include_once('Log.php'); |
Log::getInstance(); |
} |
Log::setCheminLog($this->config['log']['cheminlog']); |
Log::setTimeZone($this->config['log']['timezone']); |
Log::setTailleMax($this->config['log']['taillemax']); |
Log::ajouterEntree($index,$chaine); |
} |
public function verifierOuRelancerExecution() { |
if((microtime(true) - $this->script_time) > ($this->max_exec_time - 5)*100) { |
set_time_limit(2); |
$this->logger('JRestService','Durée du script augmentée :'.microtime(true).' - '.$this->script_time.'.) > ('.$this->max_exec_time.' - 5)*1000000'); |
return true; |
} |
return false; |
} |
} |
?> |
/trunk/jrest/lib/HTTP/Download.php |
---|
New file |
0,0 → 1,1034 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* HTTP::Download |
* |
* PHP versions 4 and 5 |
* |
* @category HTTP |
* @package HTTP_Download |
* @author Michael Wallner <mike@php.net> |
* @copyright 2003-2005 Michael Wallner |
* @license BSD, revised |
* @version CVS: $Id$ |
* @link http://pear.php.net/package/HTTP_Download |
*/ |
// {{{ includes |
/** |
* Requires PEAR |
*/ |
require_once 'PEAR.php'; |
/** |
* Requires HTTP_Header |
*/ |
require_once 'HTTP/Header.php'; |
// }}} |
// {{{ constants |
/**#@+ Use with HTTP_Download::setContentDisposition() **/ |
/** |
* Send data as attachment |
*/ |
define('HTTP_DOWNLOAD_ATTACHMENT', 'attachment'); |
/** |
* Send data inline |
*/ |
define('HTTP_DOWNLOAD_INLINE', 'inline'); |
/**#@-**/ |
/**#@+ Use with HTTP_Download::sendArchive() **/ |
/** |
* Send as uncompressed tar archive |
*/ |
define('HTTP_DOWNLOAD_TAR', 'TAR'); |
/** |
* Send as gzipped tar archive |
*/ |
define('HTTP_DOWNLOAD_TGZ', 'TGZ'); |
/** |
* Send as bzip2 compressed tar archive |
*/ |
define('HTTP_DOWNLOAD_BZ2', 'BZ2'); |
/** |
* Send as zip archive |
*/ |
define('HTTP_DOWNLOAD_ZIP', 'ZIP'); |
/**#@-**/ |
/**#@+ |
* Error constants |
*/ |
define('HTTP_DOWNLOAD_E_HEADERS_SENT', -1); |
define('HTTP_DOWNLOAD_E_NO_EXT_ZLIB', -2); |
define('HTTP_DOWNLOAD_E_NO_EXT_MMAGIC', -3); |
define('HTTP_DOWNLOAD_E_INVALID_FILE', -4); |
define('HTTP_DOWNLOAD_E_INVALID_PARAM', -5); |
define('HTTP_DOWNLOAD_E_INVALID_RESOURCE', -6); |
define('HTTP_DOWNLOAD_E_INVALID_REQUEST', -7); |
define('HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE', -8); |
define('HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE', -9); |
/**#@-**/ |
// }}} |
/** |
* Send HTTP Downloads/Responses. |
* |
* With this package you can handle (hidden) downloads. |
* It supports partial downloads, resuming and sending |
* raw data ie. from database BLOBs. |
* |
* <i>ATTENTION:</i> |
* 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! |
* |
* @access public |
* @version $Revision$ |
*/ |
class HTTP_Download |
{ |
// {{{ protected member variables |
/** |
* Path to file for download |
* |
* @see HTTP_Download::setFile() |
* @access protected |
* @var string |
*/ |
var $file = ''; |
/** |
* Data for download |
* |
* @see HTTP_Download::setData() |
* @access protected |
* @var string |
*/ |
var $data = null; |
/** |
* Resource handle for download |
* |
* @see HTTP_Download::setResource() |
* @access protected |
* @var int |
*/ |
var $handle = null; |
/** |
* Whether to gzip the download |
* |
* @access protected |
* @var bool |
*/ |
var $gzip = false; |
/** |
* Whether to allow caching of the download on the clients side |
* |
* @access protected |
* @var bool |
*/ |
var $cache = true; |
/** |
* Size of download |
* |
* @access protected |
* @var int |
*/ |
var $size = 0; |
/** |
* Last modified |
* |
* @access protected |
* @var int |
*/ |
var $lastModified = 0; |
/** |
* HTTP headers |
* |
* @access protected |
* @var array |
*/ |
var $headers = array( |
'Content-Type' => 'application/x-octetstream', |
'Pragma' => 'cache', |
'Cache-Control' => 'public, must-revalidate, max-age=0', |
'Accept-Ranges' => 'bytes', |
'X-Sent-By' => 'PEAR::HTTP::Download' |
); |
/** |
* HTTP_Header |
* |
* @access protected |
* @var object |
*/ |
var $HTTP = null; |
/** |
* ETag |
* |
* @access protected |
* @var string |
*/ |
var $etag = ''; |
/** |
* Buffer Size |
* |
* @access protected |
* @var int |
*/ |
var $bufferSize = 2097152; |
/** |
* Throttle Delay |
* |
* @access protected |
* @var float |
*/ |
var $throttleDelay = 0; |
/** |
* Sent Bytes |
* |
* @access public |
* @var int |
*/ |
var $sentBytes = 0; |
// }}} |
// {{{ constructor |
/** |
* Constructor |
* |
* Set supplied parameters. |
* |
* @access public |
* @param array $params associative array of parameters |
* |
* <b>one of:</b> |
* o 'file' => path to file for download |
* o 'data' => raw data for download |
* o 'resource' => resource handle for download |
* <br/> |
* <b>and any of:</b> |
* o 'cache' => whether to allow cs caching |
* o 'gzip' => whether to gzip the download |
* o 'lastmodified' => unix timestamp |
* o 'contenttype' => content type of download |
* o 'contentdisposition' => content disposition |
* o 'buffersize' => amount of bytes to buffer |
* o 'throttledelay' => amount of secs to sleep |
* o 'cachecontrol' => cache privacy and validity |
* |
* <br /> |
* 'Content-Disposition' is not HTTP compliant, but most browsers |
* follow this header, so it was borrowed from MIME standard. |
* |
* It looks like this: <br /> |
* "Content-Disposition: attachment; filename=example.tgz". |
* |
* @see HTTP_Download::setContentDisposition() |
*/ |
function HTTP_Download($params = array()) |
{ |
$this->HTTP = &new HTTP_Header; |
$this->setParams($params); |
} |
// }}} |
// {{{ public methods |
/** |
* Set parameters |
* |
* Set supplied parameters through its accessor methods. |
* |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param array $params associative array of parameters |
* |
* @see HTTP_Download::HTTP_Download() |
*/ |
function setParams($params) |
{ |
foreach((array) $params as $param => $value){ |
$method = 'set'. $param; |
if (!method_exists($this, $method)) { |
return PEAR::raiseError( |
"Method '$method' doesn't exist.", |
HTTP_DOWNLOAD_E_INVALID_PARAM |
); |
} |
$e = call_user_func_array(array(&$this, $method), (array) $value); |
if (PEAR::isError($e)) { |
return $e; |
} |
} |
return true; |
} |
/** |
* Set path to file for download |
* |
* The Last-Modified header will be set to files filemtime(), actually. |
* Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_FILE) if file doesn't exist. |
* Sends HTTP 404 status if $send_404 is set to true. |
* |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param string $file path to file for download |
* @param bool $send_404 whether to send HTTP/404 if |
* the file wasn't found |
*/ |
function setFile($file, $send_404 = true) |
{ |
$file = realpath($file); |
if (!is_file($file)) { |
if ($send_404) { |
$this->HTTP->sendStatusCode(404); |
} |
return PEAR::raiseError( |
"File '$file' not found.", |
HTTP_DOWNLOAD_E_INVALID_FILE |
); |
} |
$this->setLastModified(filemtime($file)); |
$this->file = $file; |
$this->size = filesize($file); |
return true; |
} |
/** |
* Set data for download |
* |
* Set $data to null if you want to unset this. |
* |
* @access public |
* @return void |
* @param $data raw data to send |
*/ |
function setData($data = null) |
{ |
$this->data = $data; |
$this->size = strlen($data); |
} |
/** |
* Set resource for download |
* |
* The resource handle supplied will be closed after sending the download. |
* Returns a PEAR_Error (HTTP_DOWNLOAD_E_INVALID_RESOURCE) if $handle |
* is no valid resource. Set $handle to null if you want to unset this. |
* |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param int $handle resource handle |
*/ |
function setResource($handle = null) |
{ |
if (!isset($handle)) { |
$this->handle = null; |
$this->size = 0; |
return true; |
} |
if (is_resource($handle)) { |
$this->handle = $handle; |
$filestats = fstat($handle); |
$this->size = $filestats['size']; |
return true; |
} |
return PEAR::raiseError( |
"Handle '$handle' is no valid resource.", |
HTTP_DOWNLOAD_E_INVALID_RESOURCE |
); |
} |
/** |
* Whether to gzip the download |
* |
* Returns a PEAR_Error (HTTP_DOWNLOAD_E_NO_EXT_ZLIB) |
* if ext/zlib is not available/loadable. |
* |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param bool $gzip whether to gzip the download |
*/ |
function setGzip($gzip = false) |
{ |
if ($gzip && !PEAR::loadExtension('zlib')){ |
return PEAR::raiseError( |
'GZIP compression (ext/zlib) not available.', |
HTTP_DOWNLOAD_E_NO_EXT_ZLIB |
); |
} |
$this->gzip = (bool) $gzip; |
return true; |
} |
/** |
* Whether to allow caching |
* |
* If set to true (default) we'll send some headers that are commonly |
* used for caching purposes like ETag, Cache-Control and Last-Modified. |
* |
* If caching is disabled, we'll send the download no matter if it |
* would actually be cached at the client side. |
* |
* @access public |
* @return void |
* @param bool $cache whether to allow caching |
*/ |
function setCache($cache = true) |
{ |
$this->cache = (bool) $cache; |
} |
/** |
* Whether to allow proxies to cache |
* |
* If set to 'private' proxies shouldn't cache the response. |
* This setting defaults to 'public' and affects only cached responses. |
* |
* @access public |
* @return bool |
* @param string $cache private or public |
* @param int $maxage maximum age of the client cache entry |
*/ |
function setCacheControl($cache = 'public', $maxage = 0) |
{ |
switch ($cache = strToLower($cache)) |
{ |
case 'private': |
case 'public': |
$this->headers['Cache-Control'] = |
$cache .', must-revalidate, max-age='. abs($maxage); |
return true; |
break; |
} |
return false; |
} |
/** |
* Set ETag |
* |
* Sets a user-defined ETag for cache-validation. The ETag is usually |
* generated by HTTP_Download through its payload information. |
* |
* @access public |
* @return void |
* @param string $etag Entity tag used for strong cache validation. |
*/ |
function setETag($etag = null) |
{ |
$this->etag = (string) $etag; |
} |
/** |
* Set Size of Buffer |
* |
* 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. |
* |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param int $bytes Amount of bytes to use as buffer. |
*/ |
function setBufferSize($bytes = 2097152) |
{ |
if (0 >= $bytes) { |
return PEAR::raiseError( |
'Buffer size must be greater than 0 bytes ('. $bytes .' given)', |
HTTP_DOWNLOAD_E_INVALID_PARAM); |
} |
$this->bufferSize = abs($bytes); |
return true; |
} |
/** |
* Set Throttle Delay |
* |
* 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 the following settings |
* HTTP_Download will sleep a second after each 25 K of data sent. |
* |
* <code> |
* Array( |
* 'throttledelay' => 1, |
* 'buffersize' => 1024 * 25, |
* ) |
* </code> |
* |
* Just be aware that if gzipp'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. |
* |
* @access public |
* @return void |
* @param float $seconds Amount of seconds to sleep after each |
* chunk that has been sent. |
*/ |
function setThrottleDelay($seconds = 0) |
{ |
$this->throttleDelay = abs($seconds) * 1000; |
} |
/** |
* Set "Last-Modified" |
* |
* This is usually determined by filemtime() in HTTP_Download::setFile() |
* If you set raw data for download with HTTP_Download::setData() and you |
* want do send an appropiate "Last-Modified" header, you should call this |
* method. |
* |
* @access public |
* @return void |
* @param int unix timestamp |
*/ |
function setLastModified($last_modified) |
{ |
$this->lastModified = $this->headers['Last-Modified'] = (int) $last_modified; |
} |
/** |
* Set Content-Disposition header |
* |
* @see HTTP_Download::HTTP_Download |
* |
* @access public |
* @return void |
* @param string $disposition whether to send the download |
* inline or as attachment |
* @param string $file_name the filename to display in |
* the browser's download window |
* |
* <b>Example:</b> |
* <code> |
* $HTTP_Download->setContentDisposition( |
* HTTP_DOWNLOAD_ATTACHMENT, |
* 'download.tgz' |
* ); |
* </code> |
*/ |
function setContentDisposition( $disposition = HTTP_DOWNLOAD_ATTACHMENT, |
$file_name = null) |
{ |
$cd = $disposition; |
if (isset($file_name)) { |
$cd .= '; filename="' . $file_name . '"'; |
} elseif ($this->file) { |
$cd .= '; filename="' . basename($this->file) . '"'; |
} |
$this->headers['Content-Disposition'] = $cd; |
} |
/** |
* Set content type of the download |
* |
* Default content type of the download will be 'application/x-octetstream'. |
* Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) if |
* $content_type doesn't seem to be valid. |
* |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param string $content_type content type of file for download |
*/ |
function setContentType($content_type = 'application/x-octetstream') |
{ |
if (!preg_match('/^[a-z]+\w*\/[a-z]+[\w.;= -]*$/', $content_type)) { |
return PEAR::raiseError( |
"Invalid content type '$content_type' supplied.", |
HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE |
); |
} |
$this->headers['Content-Type'] = $content_type; |
return true; |
} |
/** |
* Guess content type of file |
* |
* First we try to use PEAR::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: |
* o if PEAR::MIME_Type failed to detect a proper content type |
* (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) |
* o ext/magic.mime is not installed, or not properly configured |
* (HTTP_DOWNLOAD_E_NO_EXT_MMAGIC) |
* o mime_content_type() couldn't guess content type or returned |
* a content type considered to be bogus by setContentType() |
* (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) |
* |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
*/ |
function guessContentType() |
{ |
if (class_exists('MIME_Type') || @include_once 'MIME/Type.php') { |
if (PEAR::isError($mime_type = MIME_Type::autoDetect($this->file))) { |
return PEAR::raiseError($mime_type->getMessage(), |
HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE); |
} |
return $this->setContentType($mime_type); |
} |
if (!function_exists('mime_content_type')) { |
return PEAR::raiseError( |
'This feature requires ext/mime_magic!', |
HTTP_DOWNLOAD_E_NO_EXT_MMAGIC |
); |
} |
if (!is_file(ini_get('mime_magic.magicfile'))) { |
return PEAR::raiseError( |
'ext/mime_magic is loaded but not properly configured!', |
HTTP_DOWNLOAD_E_NO_EXT_MMAGIC |
); |
} |
if (!$content_type = @mime_content_type($this->file)) { |
return PEAR::raiseError( |
'Couldn\'t guess content type with mime_content_type().', |
HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE |
); |
} |
return $this->setContentType($content_type); |
} |
/** |
* Send |
* |
* Returns PEAR_Error if: |
* o HTTP headers were already sent (HTTP_DOWNLOAD_E_HEADERS_SENT) |
* o HTTP Range was invalid (HTTP_DOWNLOAD_E_INVALID_REQUEST) |
* |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param bool $autoSetContentDisposition Whether to set the |
* Content-Disposition header if it isn't already. |
*/ |
function send($autoSetContentDisposition = true) |
{ |
if (headers_sent()) { |
return PEAR::raiseError( |
'Headers already sent.', |
HTTP_DOWNLOAD_E_HEADERS_SENT |
); |
} |
if (!ini_get('safe_mode')) { |
@set_time_limit(0); |
} |
if ($autoSetContentDisposition && |
!isset($this->headers['Content-Disposition'])) { |
$this->setContentDisposition(); |
} |
if ($this->cache) { |
$this->headers['ETag'] = $this->generateETag(); |
if ($this->isCached()) { |
$this->HTTP->sendStatusCode(304); |
$this->sendHeaders(); |
return true; |
} |
} else { |
unset($this->headers['Last-Modified']); |
} |
if (ob_get_level()) { |
while (@ob_end_clean()); |
} |
if ($this->gzip) { |
@ob_start('ob_gzhandler'); |
} else { |
ob_start(); |
} |
$this->sentBytes = 0; |
if ($this->isRangeRequest()) { |
$this->HTTP->sendStatusCode(206); |
$chunks = $this->getChunks(); |
} else { |
$this->HTTP->sendStatusCode(200); |
$chunks = array(array(0, $this->size)); |
if (!$this->gzip && count(ob_list_handlers()) < 2) { |
$this->headers['Content-Length'] = $this->size; |
} |
} |
if (PEAR::isError($e = $this->sendChunks($chunks))) { |
ob_end_clean(); |
$this->HTTP->sendStatusCode(416); |
return $e; |
} |
ob_end_flush(); |
flush(); |
return true; |
} |
/** |
* Static send |
* |
* @see HTTP_Download::HTTP_Download() |
* @see HTTP_Download::send() |
* |
* @static |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param array $params associative array of parameters |
* @param bool $guess whether HTTP_Download::guessContentType() |
* should be called |
*/ |
function staticSend($params, $guess = false) |
{ |
$d = &new HTTP_Download(); |
$e = $d->setParams($params); |
if (PEAR::isError($e)) { |
return $e; |
} |
if ($guess) { |
$e = $d->guessContentType(); |
if (PEAR::isError($e)) { |
return $e; |
} |
} |
return $d->send(); |
} |
/** |
* Send a bunch of files or directories as an archive |
* |
* Example: |
* <code> |
* require_once 'HTTP/Download.php'; |
* HTTP_Download::sendArchive( |
* 'myArchive.tgz', |
* '/var/ftp/pub/mike', |
* HTTP_DOWNLOAD_TGZ, |
* '', |
* '/var/ftp/pub' |
* ); |
* </code> |
* |
* @see Archive_Tar::createModify() |
* @deprecated use HTTP_Download_Archive::send() |
* @static |
* @access public |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param string $name name the sent archive should have |
* @param mixed $files files/directories |
* @param string $type archive type |
* @param string $add_path path that should be prepended to the files |
* @param string $strip_path path that should be stripped from the files |
*/ |
function sendArchive( $name, |
$files, |
$type = HTTP_DOWNLOAD_TGZ, |
$add_path = '', |
$strip_path = '') |
{ |
require_once 'HTTP/Download/Archive.php'; |
return HTTP_Download_Archive::send($name, $files, $type, |
$add_path, $strip_path); |
} |
// }}} |
// {{{ protected methods |
/** |
* Generate ETag |
* |
* @access protected |
* @return string |
*/ |
function generateETag() |
{ |
if (!$this->etag) { |
if ($this->data) { |
$md5 = md5($this->data); |
} else { |
$fst = is_resource($this->handle) ? |
fstat($this->handle) : stat($this->file); |
$md5 = md5($fst['mtime'] .'='. $fst['ino'] .'='. $fst['size']); |
} |
$this->etag = '"' . $md5 . '-' . crc32($md5) . '"'; |
} |
return $this->etag; |
} |
/** |
* Send multiple chunks |
* |
* @access protected |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param array $chunks |
*/ |
function sendChunks($chunks) |
{ |
if (count($chunks) == 1) { |
return $this->sendChunk(current($chunks)); |
} |
$bound = uniqid('HTTP_DOWNLOAD-', true); |
$cType = $this->headers['Content-Type']; |
$this->headers['Content-Type'] = |
'multipart/byteranges; boundary=' . $bound; |
$this->sendHeaders(); |
foreach ($chunks as $chunk){ |
if (PEAR::isError($e = $this->sendChunk($chunk, $cType, $bound))) { |
return $e; |
} |
} |
#echo "\r\n--$bound--\r\n"; |
return true; |
} |
/** |
* Send chunk of data |
* |
* @access protected |
* @return mixed Returns true on success or PEAR_Error on failure. |
* @param array $chunk start and end offset of the chunk to send |
* @param string $cType actual content type |
* @param string $bound boundary for multipart/byteranges |
*/ |
function sendChunk($chunk, $cType = null, $bound = null) |
{ |
list($offset, $lastbyte) = $chunk; |
$length = ($lastbyte - $offset) + 1; |
if ($length < 1) { |
return PEAR::raiseError( |
"Error processing range request: $offset-$lastbyte/$length", |
HTTP_DOWNLOAD_E_INVALID_REQUEST |
); |
} |
$range = $offset . '-' . $lastbyte . '/' . $this->size; |
if (isset($cType, $bound)) { |
echo "\r\n--$bound\r\n", |
"Content-Type: $cType\r\n", |
"Content-Range: bytes $range\r\n\r\n"; |
} else { |
if ($this->isRangeRequest()) { |
$this->headers['Content-Length'] = $length; |
$this->headers['Content-Range'] = 'bytes '. $range; |
} |
$this->sendHeaders(); |
} |
if ($this->data) { |
while (($length -= $this->bufferSize) > 0) { |
$this->flush(substr($this->data, $offset, $this->bufferSize)); |
$this->throttleDelay and $this->sleep(); |
$offset += $this->bufferSize; |
} |
if ($length) { |
$this->flush(substr($this->data, $offset, $this->bufferSize + $length)); |
} |
} else { |
if (!is_resource($this->handle)) { |
$this->handle = fopen($this->file, 'rb'); |
} |
fseek($this->handle, $offset); |
while (($length -= $this->bufferSize) > 0) { |
$this->flush(fread($this->handle, $this->bufferSize)); |
$this->throttleDelay and $this->sleep(); |
} |
if ($length) { |
$this->flush(fread($this->handle, $this->bufferSize + $length)); |
} |
} |
return true; |
} |
/** |
* Get chunks to send |
* |
* @access protected |
* @return array |
*/ |
function getChunks() |
{ |
$parts = array(); |
foreach (explode(',', $this->getRanges()) as $chunk){ |
list($o, $e) = explode('-', $chunk); |
if ($e >= $this->size || (empty($e) && $e !== 0 && $e !== '0')) { |
$e = $this->size - 1; |
} |
if (empty($o) && $o !== 0 && $o !== '0') { |
$o = $this->size - $e; |
$e = $this->size - 1; |
} |
$parts[] = array($o, $e); |
} |
return $parts; |
} |
/** |
* Check if range is requested |
* |
* @access protected |
* @return bool |
*/ |
function isRangeRequest() |
{ |
if (!isset($_SERVER['HTTP_RANGE'])) { |
return false; |
} |
return $this->isValidRange(); |
} |
/** |
* Get range request |
* |
* @access protected |
* @return array |
*/ |
function getRanges() |
{ |
return preg_match('/^bytes=((\d*-\d*,? ?)+)$/', |
@$_SERVER['HTTP_RANGE'], $matches) ? $matches[1] : array(); |
} |
/** |
* Check if entity is cached |
* |
* @access protected |
* @return bool |
*/ |
function isCached() |
{ |
return ( |
(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && |
$this->lastModified == strtotime(current($a = explode( |
';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])))) || |
(isset($_SERVER['HTTP_IF_NONE_MATCH']) && |
$this->compareAsterisk('HTTP_IF_NONE_MATCH', $this->etag)) |
); |
} |
/** |
* Check if entity hasn't changed |
* |
* @access protected |
* @return bool |
*/ |
function isValidRange() |
{ |
if (isset($_SERVER['HTTP_IF_MATCH']) && |
!$this->compareAsterisk('HTTP_IF_MATCH', $this->etag)) { |
return false; |
} |
if (isset($_SERVER['HTTP_IF_RANGE']) && |
$_SERVER['HTTP_IF_RANGE'] !== $this->etag && |
strtotime($_SERVER['HTTP_IF_RANGE']) !== $this->lastModified) { |
return false; |
} |
if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) { |
$lm = current($a = explode(';', $_SERVER['HTTP_IF_UNMODIFIED_SINCE'])); |
if (strtotime($lm) !== $this->lastModified) { |
return false; |
} |
} |
if (isset($_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])) { |
$lm = current($a = explode(';', $_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])); |
if (strtotime($lm) !== $this->lastModified) { |
return false; |
} |
} |
return true; |
} |
/** |
* Compare against an asterisk or check for equality |
* |
* @access protected |
* @return bool |
* @param string key for the $_SERVER array |
* @param string string to compare |
*/ |
function compareAsterisk($svar, $compare) |
{ |
foreach (array_map('trim', explode(',', $_SERVER[$svar])) as $request) { |
if ($request === '*' || $request === $compare) { |
return true; |
} |
} |
return false; |
} |
/** |
* Send HTTP headers |
* |
* @access protected |
* @return void |
*/ |
function sendHeaders() |
{ |
foreach ($this->headers as $header => $value) { |
$this->HTTP->setHeader($header, $value); |
} |
$this->HTTP->sendHeaders(); |
/* NSAPI won't output anything if we did this */ |
if (strncasecmp(PHP_SAPI, 'nsapi', 5)) { |
ob_flush(); |
flush(); |
} |
} |
/** |
* Flush |
* |
* @access protected |
* @return void |
* @param string $data |
*/ |
function flush($data = '') |
{ |
if ($dlen = strlen($data)) { |
$this->sentBytes += $dlen; |
echo $data; |
} |
ob_flush(); |
flush(); |
} |
/** |
* Sleep |
* |
* @access protected |
* @return void |
*/ |
function sleep() |
{ |
if (OS_WINDOWS) { |
com_message_pump($this->throttleDelay); |
} else { |
usleep($this->throttleDelay * 1000); |
} |
} |
// }}} |
} |
?> |
/trunk/jrest/lib/HTTP/.directory |
---|
New file |
0,0 → 1,5 |
[Dolphin] |
Timestamp=2010,6,10,16,42,37 |
[Settings] |
ShowDotFiles=true |
/trunk/jrest/lib/JSON.php |
---|
New file |
0,0 → 1,806 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* Converts to and from JSON format. |
* |
* JSON (JavaScript Object Notation) is a lightweight data-interchange |
* format. It is easy for humans to read and write. It is easy for machines |
* to parse and generate. It is based on a subset of the JavaScript |
* Programming Language, Standard ECMA-262 3rd Edition - December 1999. |
* This feature can also be found in Python. JSON is a text format that is |
* completely language independent but uses conventions that are familiar |
* to programmers of the C-family of languages, including C, C++, C#, Java, |
* JavaScript, Perl, TCL, and many others. These properties make JSON an |
* ideal data-interchange language. |
* |
* This package provides a simple encoder and decoder for JSON notation. It |
* is intended for use with client-side Javascript applications that make |
* use of HTTPRequest to perform server communication functions - data can |
* be encoded into JSON notation for use in a client-side javascript, or |
* decoded from incoming Javascript requests. JSON format is native to |
* Javascript, and can be directly eval()'ed with no further parsing |
* overhead |
* |
* All strings should be in ASCII or UTF-8 format! |
* |
* LICENSE: 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. |
* |
* THIS SOFTWARE IS PROVIDED ``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 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. |
* |
* @category |
* @package Services_JSON |
* @author Michal Migurski <mike-json@teczno.com> |
* @author Matt Knapp <mdknapp[at]gmail[dot]com> |
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> |
* @copyright 2005 Michal Migurski |
* @version CVS: $Id$ |
* @license http://www.opensource.org/licenses/bsd-license.php |
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 |
*/ |
/** |
* Marker constant for Services_JSON::decode(), used to flag stack state |
*/ |
define('SERVICES_JSON_SLICE', 1); |
/** |
* Marker constant for Services_JSON::decode(), used to flag stack state |
*/ |
define('SERVICES_JSON_IN_STR', 2); |
/** |
* Marker constant for Services_JSON::decode(), used to flag stack state |
*/ |
define('SERVICES_JSON_IN_ARR', 3); |
/** |
* Marker constant for Services_JSON::decode(), used to flag stack state |
*/ |
define('SERVICES_JSON_IN_OBJ', 4); |
/** |
* Marker constant for Services_JSON::decode(), used to flag stack state |
*/ |
define('SERVICES_JSON_IN_CMT', 5); |
/** |
* Behavior switch for Services_JSON::decode() |
*/ |
define('SERVICES_JSON_LOOSE_TYPE', 16); |
/** |
* Behavior switch for Services_JSON::decode() |
*/ |
define('SERVICES_JSON_SUPPRESS_ERRORS', 32); |
/** |
* Converts to and from JSON format. |
* |
* Brief example of use: |
* |
* <code> |
* // create a new instance of Services_JSON |
* $json = new Services_JSON(); |
* |
* // convert a complexe value to JSON notation, and send it to the browser |
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); |
* $output = $json->encode($value); |
* |
* print($output); |
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] |
* |
* // accept incoming POST data, assumed to be in JSON notation |
* $input = file_get_contents('php://input', 1000000); |
* $value = $json->decode($input); |
* </code> |
*/ |
class Services_JSON |
{ |
/** |
* constructs a new JSON instance |
* |
* @param int $use object behavior flags; combine with boolean-OR |
* |
* possible values: |
* - SERVICES_JSON_LOOSE_TYPE: loose typing. |
* "{...}" syntax creates associative arrays |
* instead of objects in decode(). |
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. |
* Values which can't be encoded (e.g. resources) |
* appear as NULL instead of throwing errors. |
* By default, a deeply-nested resource will |
* bubble up with an error, so all return values |
* from encode() should be checked with isError() |
*/ |
function Services_JSON($use = 0) |
{ |
$this->use = $use; |
} |
/** |
* convert a string from one UTF-16 char to one UTF-8 char |
* |
* Normally should be handled by mb_convert_encoding, but |
* provides a slower PHP-only method for installations |
* that lack the multibye string extension. |
* |
* @param string $utf16 UTF-16 character |
* @return string UTF-8 character |
* @access private |
*/ |
function utf162utf8($utf16) |
{ |
// oh please oh please oh please oh please oh please |
if(function_exists('mb_convert_encoding')) { |
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); |
} |
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); |
switch(true) { |
case ((0x7F & $bytes) == $bytes): |
// this case should never be reached, because we are in ASCII range |
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
return chr(0x7F & $bytes); |
case (0x07FF & $bytes) == $bytes: |
// return a 2-byte UTF-8 character |
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
return chr(0xC0 | (($bytes >> 6) & 0x1F)) |
. chr(0x80 | ($bytes & 0x3F)); |
case (0xFFFF & $bytes) == $bytes: |
// return a 3-byte UTF-8 character |
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
return chr(0xE0 | (($bytes >> 12) & 0x0F)) |
. chr(0x80 | (($bytes >> 6) & 0x3F)) |
. chr(0x80 | ($bytes & 0x3F)); |
} |
// ignoring UTF-32 for now, sorry |
return ''; |
} |
/** |
* convert a string from one UTF-8 char to one UTF-16 char |
* |
* Normally should be handled by mb_convert_encoding, but |
* provides a slower PHP-only method for installations |
* that lack the multibye string extension. |
* |
* @param string $utf8 UTF-8 character |
* @return string UTF-16 character |
* @access private |
*/ |
function utf82utf16($utf8) |
{ |
// oh please oh please oh please oh please oh please |
if(function_exists('mb_convert_encoding')) { |
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); |
} |
switch(strlen($utf8)) { |
case 1: |
// this case should never be reached, because we are in ASCII range |
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
return $utf8; |
case 2: |
// return a UTF-16 character from a 2-byte UTF-8 char |
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
return chr(0x07 & (ord($utf8{0}) >> 2)) |
. chr((0xC0 & (ord($utf8{0}) << 6)) |
| (0x3F & ord($utf8{1}))); |
case 3: |
// return a UTF-16 character from a 3-byte UTF-8 char |
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
return chr((0xF0 & (ord($utf8{0}) << 4)) |
| (0x0F & (ord($utf8{1}) >> 2))) |
. chr((0xC0 & (ord($utf8{1}) << 6)) |
| (0x7F & ord($utf8{2}))); |
} |
// ignoring UTF-32 for now, sorry |
return ''; |
} |
/** |
* encodes an arbitrary variable into JSON format |
* |
* @param mixed $var any number, boolean, string, array, or object to be encoded. |
* see argument 1 to Services_JSON() above for array-parsing behavior. |
* if var is a strng, note that encode() always expects it |
* to be in ASCII or UTF-8 format! |
* |
* @return mixed JSON string representation of input var or an error if a problem occurs |
* @access public |
*/ |
function encode($var) |
{ |
switch (gettype($var)) { |
case 'boolean': |
return $var ? 'true' : 'false'; |
case 'NULL': |
return 'null'; |
case 'integer': |
return (int) $var; |
case 'double': |
case 'float': |
return (float) $var; |
case 'string': |
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT |
$ascii = ''; |
$strlen_var = strlen($var); |
/* |
* Iterate over every character in the string, |
* escaping with a slash or encoding to UTF-8 where necessary |
*/ |
for ($c = 0; $c < $strlen_var; ++$c) { |
$ord_var_c = ord($var{$c}); |
switch (true) { |
case $ord_var_c == 0x08: |
$ascii .= '\b'; |
break; |
case $ord_var_c == 0x09: |
$ascii .= '\t'; |
break; |
case $ord_var_c == 0x0A: |
$ascii .= '\n'; |
break; |
case $ord_var_c == 0x0C: |
$ascii .= '\f'; |
break; |
case $ord_var_c == 0x0D: |
$ascii .= '\r'; |
break; |
case $ord_var_c == 0x22: |
case $ord_var_c == 0x2F: |
case $ord_var_c == 0x5C: |
// double quote, slash, slosh |
$ascii .= '\\'.$var{$c}; |
break; |
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): |
// characters U-00000000 - U-0000007F (same as ASCII) |
$ascii .= $var{$c}; |
break; |
case (($ord_var_c & 0xE0) == 0xC0): |
// characters U-00000080 - U-000007FF, mask 110XXXXX |
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$char = pack('C*', $ord_var_c, ord($var{$c + 1})); |
$c += 1; |
$utf16 = $this->utf82utf16($char); |
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
break; |
case (($ord_var_c & 0xF0) == 0xE0): |
// characters U-00000800 - U-0000FFFF, mask 1110XXXX |
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$char = pack('C*', $ord_var_c, |
ord($var{$c + 1}), |
ord($var{$c + 2})); |
$c += 2; |
$utf16 = $this->utf82utf16($char); |
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
break; |
case (($ord_var_c & 0xF8) == 0xF0): |
// characters U-00010000 - U-001FFFFF, mask 11110XXX |
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$char = pack('C*', $ord_var_c, |
ord($var{$c + 1}), |
ord($var{$c + 2}), |
ord($var{$c + 3})); |
$c += 3; |
$utf16 = $this->utf82utf16($char); |
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
break; |
case (($ord_var_c & 0xFC) == 0xF8): |
// characters U-00200000 - U-03FFFFFF, mask 111110XX |
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$char = pack('C*', $ord_var_c, |
ord($var{$c + 1}), |
ord($var{$c + 2}), |
ord($var{$c + 3}), |
ord($var{$c + 4})); |
$c += 4; |
$utf16 = $this->utf82utf16($char); |
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
break; |
case (($ord_var_c & 0xFE) == 0xFC): |
// characters U-04000000 - U-7FFFFFFF, mask 1111110X |
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$char = pack('C*', $ord_var_c, |
ord($var{$c + 1}), |
ord($var{$c + 2}), |
ord($var{$c + 3}), |
ord($var{$c + 4}), |
ord($var{$c + 5})); |
$c += 5; |
$utf16 = $this->utf82utf16($char); |
$ascii .= sprintf('\u%04s', bin2hex($utf16)); |
break; |
} |
} |
return '"'.$ascii.'"'; |
case 'array': |
/* |
* As per JSON spec if any array key is not an integer |
* we must treat the the whole array as an object. We |
* also try to catch a sparsely populated associative |
* array with numeric keys here because some JS engines |
* will create an array with empty indexes up to |
* max_index which can cause memory issues and because |
* the keys, which may be relevant, will be remapped |
* otherwise. |
* |
* As per the ECMA and JSON specification an object may |
* have any string as a property. Unfortunately due to |
* a hole in the ECMA specification if the key is a |
* ECMA reserved word or starts with a digit the |
* parameter is only accessible using ECMAScript's |
* bracket notation. |
*/ |
// treat as a JSON object |
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { |
$properties = array_map(array($this, 'name_value'), |
array_keys($var), |
array_values($var)); |
foreach($properties as $property) { |
if(Services_JSON::isError($property)) { |
return $property; |
} |
} |
return '{' . join(',', $properties) . '}'; |
} |
// treat it like a regular array |
$elements = array_map(array($this, 'encode'), $var); |
foreach($elements as $element) { |
if(Services_JSON::isError($element)) { |
return $element; |
} |
} |
return '[' . join(',', $elements) . ']'; |
case 'object': |
$vars = get_object_vars($var); |
$properties = array_map(array($this, 'name_value'), |
array_keys($vars), |
array_values($vars)); |
foreach($properties as $property) { |
if(Services_JSON::isError($property)) { |
return $property; |
} |
} |
return '{' . join(',', $properties) . '}'; |
default: |
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) |
? 'null' |
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); |
} |
} |
/** |
* array-walking function for use in generating JSON-formatted name-value pairs |
* |
* @param string $name name of key to use |
* @param mixed $value reference to an array element to be encoded |
* |
* @return string JSON-formatted name-value pair, like '"name":value' |
* @access private |
*/ |
function name_value($name, $value) |
{ |
$encoded_value = $this->encode($value); |
if(Services_JSON::isError($encoded_value)) { |
return $encoded_value; |
} |
return $this->encode(strval($name)) . ':' . $encoded_value; |
} |
/** |
* reduce a string by removing leading and trailing comments and whitespace |
* |
* @param $str string string value to strip of comments and whitespace |
* |
* @return string string value stripped of comments and whitespace |
* @access private |
*/ |
function reduce_string($str) |
{ |
$str = preg_replace(array( |
// eliminate single line comments in '// ...' form |
'#^\s*//(.+)$#m', |
// eliminate multi-line comments in '/* ... */' form, at start of string |
'#^\s*/\*(.+)\*/#Us', |
// eliminate multi-line comments in '/* ... */' form, at end of string |
'#/\*(.+)\*/\s*$#Us' |
), '', $str); |
// eliminate extraneous space |
return trim($str); |
} |
/** |
* decodes a JSON string into appropriate variable |
* |
* @param string $str JSON-formatted string |
* |
* @return mixed number, boolean, string, array, or object |
* corresponding to given JSON input string. |
* See argument 1 to Services_JSON() above for object-output behavior. |
* Note that decode() always returns strings |
* in ASCII or UTF-8 format! |
* @access public |
*/ |
function decode($str) |
{ |
$str = $this->reduce_string($str); |
switch (strtolower($str)) { |
case 'true': |
return true; |
case 'false': |
return false; |
case 'null': |
return null; |
default: |
$m = array(); |
if (is_numeric($str)) { |
// Lookie-loo, it's a number |
// This would work on its own, but I'm trying to be |
// good about returning integers where appropriate: |
// return (float)$str; |
// Return float or int, as appropriate |
return ((float)$str == (integer)$str) |
? (integer)$str |
: (float)$str; |
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { |
// STRINGS RETURNED IN UTF-8 FORMAT |
$delim = substr($str, 0, 1); |
$chrs = substr($str, 1, -1); |
$utf8 = ''; |
$strlen_chrs = strlen($chrs); |
for ($c = 0; $c < $strlen_chrs; ++$c) { |
$substr_chrs_c_2 = substr($chrs, $c, 2); |
$ord_chrs_c = ord($chrs{$c}); |
switch (true) { |
case $substr_chrs_c_2 == '\b': |
$utf8 .= chr(0x08); |
++$c; |
break; |
case $substr_chrs_c_2 == '\t': |
$utf8 .= chr(0x09); |
++$c; |
break; |
case $substr_chrs_c_2 == '\n': |
$utf8 .= chr(0x0A); |
++$c; |
break; |
case $substr_chrs_c_2 == '\f': |
$utf8 .= chr(0x0C); |
++$c; |
break; |
case $substr_chrs_c_2 == '\r': |
$utf8 .= chr(0x0D); |
++$c; |
break; |
case $substr_chrs_c_2 == '\\"': |
case $substr_chrs_c_2 == '\\\'': |
case $substr_chrs_c_2 == '\\\\': |
case $substr_chrs_c_2 == '\\/': |
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || |
($delim == "'" && $substr_chrs_c_2 != '\\"')) { |
$utf8 .= $chrs{++$c}; |
} |
break; |
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): |
// single, escaped unicode character |
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) |
. chr(hexdec(substr($chrs, ($c + 4), 2))); |
$utf8 .= $this->utf162utf8($utf16); |
$c += 5; |
break; |
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): |
$utf8 .= $chrs{$c}; |
break; |
case ($ord_chrs_c & 0xE0) == 0xC0: |
// characters U-00000080 - U-000007FF, mask 110XXXXX |
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$utf8 .= substr($chrs, $c, 2); |
++$c; |
break; |
case ($ord_chrs_c & 0xF0) == 0xE0: |
// characters U-00000800 - U-0000FFFF, mask 1110XXXX |
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$utf8 .= substr($chrs, $c, 3); |
$c += 2; |
break; |
case ($ord_chrs_c & 0xF8) == 0xF0: |
// characters U-00010000 - U-001FFFFF, mask 11110XXX |
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$utf8 .= substr($chrs, $c, 4); |
$c += 3; |
break; |
case ($ord_chrs_c & 0xFC) == 0xF8: |
// characters U-00200000 - U-03FFFFFF, mask 111110XX |
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$utf8 .= substr($chrs, $c, 5); |
$c += 4; |
break; |
case ($ord_chrs_c & 0xFE) == 0xFC: |
// characters U-04000000 - U-7FFFFFFF, mask 1111110X |
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
$utf8 .= substr($chrs, $c, 6); |
$c += 5; |
break; |
} |
} |
return $utf8; |
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { |
// array, or object notation |
if ($str{0} == '[') { |
$stk = array(SERVICES_JSON_IN_ARR); |
$arr = array(); |
} else { |
if ($this->use & SERVICES_JSON_LOOSE_TYPE) { |
$stk = array(SERVICES_JSON_IN_OBJ); |
$obj = array(); |
} else { |
$stk = array(SERVICES_JSON_IN_OBJ); |
$obj = new stdClass(); |
} |
} |
array_push($stk, array('what' => SERVICES_JSON_SLICE, |
'where' => 0, |
'delim' => false)); |
$chrs = substr($str, 1, -1); |
$chrs = $this->reduce_string($chrs); |
if ($chrs == '') { |
if (reset($stk) == SERVICES_JSON_IN_ARR) { |
return $arr; |
} else { |
return $obj; |
} |
} |
//print("\nparsing {$chrs}\n"); |
$strlen_chrs = strlen($chrs); |
for ($c = 0; $c <= $strlen_chrs; ++$c) { |
$top = end($stk); |
$substr_chrs_c_2 = substr($chrs, $c, 2); |
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { |
// found a comma that is not inside a string, array, etc., |
// OR we've reached the end of the character list |
$slice = substr($chrs, $top['where'], ($c - $top['where'])); |
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); |
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); |
if (reset($stk) == SERVICES_JSON_IN_ARR) { |
// we are in an array, so just push an element onto the stack |
array_push($arr, $this->decode($slice)); |
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { |
// we are in an object, so figure |
// out the property name and set an |
// element in an associative array, |
// for now |
$parts = array(); |
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { |
// "name":value pair |
$key = $this->decode($parts[1]); |
$val = $this->decode($parts[2]); |
if ($this->use & SERVICES_JSON_LOOSE_TYPE) { |
$obj[$key] = $val; |
} else { |
$obj->$key = $val; |
} |
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { |
// name:value pair, where name is unquoted |
$key = $parts[1]; |
$val = $this->decode($parts[2]); |
if ($this->use & SERVICES_JSON_LOOSE_TYPE) { |
$obj[$key] = $val; |
} else { |
$obj->$key = $val; |
} |
} |
} |
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { |
// found a quote, and we are not inside a string |
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); |
//print("Found start of string at {$c}\n"); |
} elseif (($chrs{$c} == $top['delim']) && |
($top['what'] == SERVICES_JSON_IN_STR) && |
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { |
// found a quote, we're in a string, and it's not escaped |
// we know that it's not escaped becase there is _not_ an |
// odd number of backslashes at the end of the string so far |
array_pop($stk); |
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); |
} elseif (($chrs{$c} == '[') && |
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { |
// found a left-bracket, and we are in an array, object, or slice |
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); |
//print("Found start of array at {$c}\n"); |
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { |
// found a right-bracket, and we're in an array |
array_pop($stk); |
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); |
} elseif (($chrs{$c} == '{') && |
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { |
// found a left-brace, and we are in an array, object, or slice |
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); |
//print("Found start of object at {$c}\n"); |
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { |
// found a right-brace, and we're in an object |
array_pop($stk); |
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); |
} elseif (($substr_chrs_c_2 == '/*') && |
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { |
// found a comment start, and we are in an array, object, or slice |
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); |
$c++; |
//print("Found start of comment at {$c}\n"); |
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { |
// found a comment end, and we're in one now |
array_pop($stk); |
$c++; |
for ($i = $top['where']; $i <= $c; ++$i) |
$chrs = substr_replace($chrs, ' ', $i, 1); |
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); |
} |
} |
if (reset($stk) == SERVICES_JSON_IN_ARR) { |
return $arr; |
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { |
return $obj; |
} |
} |
} |
} |
/** |
* @todo Ultimately, this should just call PEAR::isError() |
*/ |
function isError($data, $code = null) |
{ |
if (class_exists('pear')) { |
return PEAR::isError($data, $code); |
} elseif (is_object($data) && (get_class($data) == 'services_json_error' || |
is_subclass_of($data, 'services_json_error'))) { |
return true; |
} |
return false; |
} |
} |
if (class_exists('PEAR_Error')) { |
class Services_JSON_Error extends PEAR_Error |
{ |
function Services_JSON_Error($message = 'unknown error', $code = null, |
$mode = null, $options = null, $userinfo = null) |
{ |
parent::PEAR_Error($message, $code, $mode, $options, $userinfo); |
} |
} |
} else { |
/** |
* @todo Ultimately, this class shall be descended from PEAR_Error |
*/ |
class Services_JSON_Error |
{ |
function Services_JSON_Error($message = 'unknown error', $code = null, |
$mode = null, $options = null, $userinfo = null) |
{ |
} |
} |
} |
?> |
/trunk/jrest/lib/PDF.php |
---|
New file |
0,0 → 1,3001 |
<?php |
/** |
* File_PDF:: |
* |
* The File_PDF:: class provides a PHP-only implementation of a PDF library. |
* No external libs or PHP extensions are required. |
* |
* Based on the FPDF class by Olivier Plathey (http://www.fpdf.org). |
* |
* Copyright 2001-2003 Olivier Plathey <olivier@fpdf.org> |
* Copyright 2003-2007 The Horde Project (http://www.horde.org/) |
* |
* See the enclosed file COPYING for license information (LGPL). If you |
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. |
* |
* $Horde: framework/File_PDF/PDF.php,v 1.48 2007/01/05 13:12:21 jan Exp $ |
* |
* @author Olivier Plathey <olivier@fpdf.org> |
* @author Marko Djukic <marko@oblo.com> |
* @author Jan Schneider <jan@horde.org> |
* @package File_PDF |
* @category Fileformats |
*/ |
class File_PDF { |
/** |
* Current page number. |
* |
* @var integer |
*/ |
var $_page = 0; |
/** |
* Current object number. |
* |
* @var integer |
*/ |
var $_n = 2; |
/** |
* Array of object offsets. |
* |
* @var array |
*/ |
var $_offsets = array(); |
/** |
* Buffer holding in-memory PDF. |
* |
* @var string |
*/ |
var $_buffer = ''; |
/** |
* Array containing the pages. |
* |
* @var array |
*/ |
var $_pages = array(); |
/** |
* Current document state. |
* 0 - initial state |
* 1 - document opened |
* 2 - page opened |
* 3 - document closed |
* |
* @var integer |
*/ |
var $_state = 0; |
/** |
* Flag indicating if PDF file is to be compressed or not. |
* |
* @var boolean |
*/ |
var $_compress; |
/** |
* The default page orientation. |
* |
* @var string |
*/ |
var $_default_orientation; |
/** |
* The current page orientation. |
* |
* @var string |
*/ |
var $_current_orientation; |
/** |
* Array indicating orientation changes. |
* |
* @var array |
*/ |
var $_orientation_changes = array(); |
/** |
* Current width of page format in points. |
* |
* @var float |
*/ |
var $fwPt; |
/** |
* Current height of page format in points. |
* |
* @var float |
*/ |
var $fhPt; |
/** |
* Current width of page format in user units. |
* |
* @var float |
*/ |
var $fw; |
/** |
* Current height of page format in user units. |
* |
* @var float |
*/ |
var $fh; |
/** |
* Current width of page in points. |
* |
* @var float |
*/ |
var $wPt; |
/** |
* Current height of page in points. |
* |
* @var float |
*/ |
var $hPt; |
/** |
* Current width of page in user units |
* |
* @var float |
*/ |
var $w; |
/** |
* Current height of page in user units |
* |
* @var float |
*/ |
var $h; |
/** |
* Scale factor (number of points in user units). |
* |
* @var float |
*/ |
var $_scale; |
/** |
* Left page margin size. |
* |
* @var float |
*/ |
var $_left_margin; |
/** |
* Top page margin size. |
* |
* @var float |
*/ |
var $_top_margin; |
/** |
* Right page margin size. |
* |
* @var float |
*/ |
var $_right_margin; |
/** |
* Break page margin size, the bottom margin which triggers a page break. |
* |
* @var float |
*/ |
var $_break_margin; |
/** |
* Cell margin size. |
* |
* @var float |
*/ |
var $_cell_margin; |
/** |
* The current horizontal position for cell positioning. |
* Value is set in user units and is calculated from the top left corner |
* as origin. |
* |
* @var float |
*/ |
var $x; |
/** |
* The current vertical position for cell positioning. |
* Value is set in user units and is calculated from the top left corner |
* as origin. |
* |
* @var float |
*/ |
var $y; |
/** |
* The height of the last cell printed. |
* |
* @var float |
*/ |
var $_last_height; |
/** |
* Line width in user units. |
* |
* @var float |
*/ |
var $_line_width; |
/** |
* An array of standard font names. |
* |
* @var array |
*/ |
var $_core_fonts = array('courier' => 'Courier', |
'courierB' => 'Courier-Bold', |
'courierI' => 'Courier-Oblique', |
'courierBI' => 'Courier-BoldOblique', |
'helvetica' => 'Helvetica', |
'helveticaB' => 'Helvetica-Bold', |
'helveticaI' => 'Helvetica-Oblique', |
'helveticaBI' => 'Helvetica-BoldOblique', |
'times' => 'Times-Roman', |
'timesB' => 'Times-Bold', |
'timesI' => 'Times-Italic', |
'timesBI' => 'Times-BoldItalic', |
'symbol' => 'Symbol', |
'zapfdingbats' => 'ZapfDingbats'); |
/** |
* An array of used fonts. |
* |
* @var array |
*/ |
var $_fonts = array(); |
/** |
* An array of font files. |
* |
* @var array |
*/ |
var $_font_files = array(); |
/** |
* An array of encoding differences. |
* |
* @var array |
*/ |
var $_diffs = array(); |
/** |
* An array of used images. |
* |
* @var array |
*/ |
var $_images = array(); |
/** |
* An array of links in pages. |
* |
* @var array |
*/ |
var $_page_links; |
/** |
* An array of internal links. |
* |
* @var array |
*/ |
var $_links = array(); |
/** |
* Current font family. |
* |
* @var string |
*/ |
var $_font_family = ''; |
/** |
* Current font style. |
* |
* @var string |
*/ |
var $_font_style = ''; |
/** |
* Underlining flag. |
* |
* @var boolean |
*/ |
var $_underline = false; |
/** |
* An array containing current font info. |
* |
* @var array |
*/ |
var $_current_font; |
/** |
* Current font size in points. |
* |
* @var float |
*/ |
var $_font_size_pt = 12; |
/** |
* Current font size in user units. |
* |
* @var float |
*/ |
var $_font_size; |
/** |
* Commands for filling color. |
* |
* @var string |
*/ |
var $_fill_color = '0 g'; |
/** |
* Commands for text color. |
* |
* @var string |
*/ |
var $_text_color = '0 g'; |
/** |
* Whether text color is different from fill color. |
* |
* @var boolean |
*/ |
var $_color_flag = false; |
/** |
* Commands for drawing color. |
* |
* @var string |
*/ |
var $_draw_color = '0 G'; |
/** |
* Word spacing. |
* |
* @var integer |
*/ |
var $_word_spacing = 0; |
/** |
* Automatic page breaking. |
* |
* @var boolean |
*/ |
var $_auto_page_break; |
/** |
* Threshold used to trigger page breaks. |
* |
* @var float |
*/ |
var $_page_break_trigger; |
/** |
* Flag set when processing footer. |
* |
* @var boolean |
*/ |
var $_in_footer = false; |
/** |
* Zoom display mode. |
* |
* @var string |
*/ |
var $_zoom_mode; |
/** |
* Layout display mode. |
* |
* @var string |
*/ |
var $_layout_mode; |
/** |
* An array containing the document info, consisting of: |
* - title |
* - subject |
* - author |
* - keywords |
* - creator |
* |
* @var array |
*/ |
var $_info = array(); |
/** |
* Alias for total number of pages. |
* |
* @var string |
*/ |
var $_alias_nb_pages = '{nb}'; |
/** |
* Attempts to return a conrete PDF instance. It allows to set up the page |
* format, the orientation and the units of measurement used in all the |
* methods (except for the font sizes). |
* |
* Example:<pre> |
* $pdf = &File_PDF::factory(array('orientation' => 'P', |
* 'unit' => 'mm', |
* 'format' => 'A4'));</pre> |
* |
* @param array $params A hash with parameters for the created PDF object. |
* Possible parameters are: |
* orientation - Default page orientation. Possible |
* values are (case insensitive): |
* <pre> |
* - P or Portrait (default) |
* - L or Landscape |
* </pre> |
* unit - User measure units. Possible values values |
* are: |
* <pre> |
* - pt: point |
* - mm: millimeter (default) |
* - cm: centimeter |
* - in: inch |
* </pre> |
* A point equals 1/72 of inch, that is to say about |
* 0.35 mm (an inch being 2.54 cm). This is a very |
* common unit in typography; font sizes are |
* expressed in that unit. |
* format - The format used for pages. It can be |
* either one of the following values (case |
* insensitive): |
* <pre> |
* - A3 |
* - A4 (default) |
* - A5 |
* - Letter |
* - Legal |
* </pre> |
* or a custom format in the form of a two-element |
* array containing the width and the height |
* (expressed in the unit given by the unit |
* parameter). |
* @param string $class The concrete class name to return an instance of. |
* Defaults to File_PDF. |
*/ |
function &factory($params = array(), $class = 'File_PDF') |
{ |
/* Check for PHP locale-related bug. */ |
if (1.1 == 1) { |
$error = File_PDF::raiseError('Do not alter the locale before including the class file.'); |
return $error; |
} |
/* Default parameters. */ |
$defaults = array('orientation' => 'P', 'unit' => 'mm', 'format' => 'A4'); |
/* Backward compatibility with old method signature. */ |
/* Should be removed a few versions later. */ |
if (!is_array($params)) { |
$class = 'File_PDF'; |
$params = $defaults; |
$names = array_keys($defaults); |
for ($i = 0; $i < func_num_args(); $i++) { |
$params[$names[$i]] = func_get_arg($i); |
} |
} else { |
$params = array_merge($defaults, $params); |
} |
/* Create the PDF object. */ |
$pdf = &new $class(); |
/* Scale factor. */ |
if ($params['unit'] == 'pt') { |
$pdf->_scale = 1; |
} elseif ($params['unit'] == 'mm') { |
$pdf->_scale = 72 / 25.4; |
} elseif ($params['unit'] == 'cm') { |
$pdf->_scale = 72 / 2.54; |
} elseif ($params['unit'] == 'in') { |
$pdf->_scale = 72; |
} else { |
$error = File_PDF::raiseError(sprintf('Incorrect units: %s', $params['unit'])); |
return $error; |
} |
/* Page format. */ |
if (is_string($params['format'])) { |
$params['format'] = strtolower($params['format']); |
if ($params['format'] == 'a3') { |
$params['format'] = array(841.89, 1190.55); |
} elseif ($params['format'] == 'a4') { |
$params['format'] = array(595.28, 841.89); |
} elseif ($params['format'] == 'a5') { |
$params['format'] = array(420.94, 595.28); |
} elseif ($params['format'] == 'letter') { |
$params['format'] = array(612, 792); |
} elseif ($params['format'] == 'legal') { |
$params['format'] = array(612, 1008); |
} else { |
$error = File_PDF::raiseError(sprintf('Unknown page format: %s', $params['format'])); |
return $error; |
} |
$pdf->fwPt = $params['format'][0]; |
$pdf->fhPt = $params['format'][1]; |
} else { |
$pdf->fwPt = $params['format'][0] * $pdf->_scale; |
$pdf->fhPt = $params['format'][1] * $pdf->_scale; |
} |
$pdf->fw = $pdf->fwPt / $pdf->_scale; |
$pdf->fh = $pdf->fhPt / $pdf->_scale; |
/* Page orientation. */ |
$params['orientation'] = strtolower($params['orientation']); |
if ($params['orientation'] == 'p' || $params['orientation'] == 'portrait') { |
$pdf->_default_orientation = 'P'; |
$pdf->wPt = $pdf->fwPt; |
$pdf->hPt = $pdf->fhPt; |
} elseif ($params['orientation'] == 'l' || $params['orientation'] == 'landscape') { |
$pdf->_default_orientation = 'L'; |
$pdf->wPt = $pdf->fhPt; |
$pdf->hPt = $pdf->fwPt; |
} else { |
$error = File_PDF::raiseError(sprintf('Incorrect orientation: %s', $params['orientation'])); |
return $error; |
} |
$pdf->_current_orientation = $pdf->_default_orientation; |
$pdf->w = $pdf->wPt / $pdf->_scale; |
$pdf->h = $pdf->hPt / $pdf->_scale; |
/* Page margins (1 cm) */ |
$margin = 28.35 / $pdf->_scale; |
$pdf->setMargins($margin, $margin); |
/* Interior cell margin (1 mm) */ |
$pdf->_cell_margin = $margin / 10; |
/* Line width (0.2 mm) */ |
$pdf->_line_width = .567 / $pdf->_scale; |
/* Automatic page break */ |
$pdf->setAutoPageBreak(true, 2 * $margin); |
/* Full width display mode */ |
$pdf->setDisplayMode('fullwidth'); |
/* Compression */ |
$pdf->setCompression(true); |
return $pdf; |
} |
/** |
* Returns a PEAR_Error object. Wraps around PEAR::raiseError() to |
* avoid having to include PEAR.php unless an error occurs. |
* |
* @param mixed $error The error message. |
* |
* @return object PEAR_Error |
*/ |
function raiseError($error) |
{ |
require_once 'PEAR.php'; |
return PEAR::raiseError($error); |
} |
/** |
* Defines the left, top and right margins. By default, they equal 1 cm. |
* Call this method to change them. |
* |
* @param float $left Left margin. |
* @param float $top Top margin. |
* @param float $right Right margin. If not specified default to the value |
* of the left one. |
* |
* @see File_PDF::setAutoPageBreak |
* @see File_PDF::setLeftMargin |
* @see File_PDF::setRightMargin |
* @see File_PDF::setTopMargin |
*/ |
function setMargins($left, $top, $right = null) |
{ |
/* Set left and top margins. */ |
$this->_left_margin = $left; |
$this->_top_margin = $top; |
/* If no right margin set default to same as left. */ |
$this->_right_margin = (is_null($right) ? $left : $right); |
} |
/** |
* Defines the left margin. The method can be called before creating the |
* first page. |
* If the current abscissa gets out of page, it is brought back to the |
* margin. |
* |
* @param float $margin The margin. |
* |
* @see File_PDF::setAutoPageBreak |
* @see File_PDF::setMargins |
* @see File_PDF::setRightMargin |
* @see File_PDF::setTopMargin |
*/ |
function setLeftMargin($margin) |
{ |
$this->_left_margin = $margin; |
/* If there is a current page and the current X position is less than |
* margin set the X position to the margin value. */ |
if ($this->_page > 0 && $this->x < $margin) { |
$this->x = $margin; |
} |
} |
/** |
* Defines the top margin. The method can be called before creating the |
* first page. |
* |
* @param float $margin The margin. |
*/ |
function setTopMargin($margin) |
{ |
$this->_top_margin = $margin; |
} |
/** |
* Defines the right margin. The method can be called before creating the |
* first page. |
* |
* @param float $margin The margin. |
*/ |
function setRightMargin($margin) |
{ |
$this->_right_margin = $margin; |
} |
/** |
* Returns the actual page width. |
* |
* @since File_PDF 0.2.0 |
* @since Horde 3.2 |
* |
* @return float The page width. |
*/ |
function getPageWidth() |
{ |
return ($this->w - $this->_right_margin - $this->_left_margin); |
} |
/** |
* Returns the actual page height. |
* |
* @since File_PDF 0.2.0 |
* @since Horde 3.2 |
* |
* @return float The page height. |
*/ |
function getPageHeight() |
{ |
return ($this->h - $this->_top_margin - $this->_break_margin); |
} |
/** |
* Enables or disables the automatic page breaking mode. When enabling, |
* the second parameter is the distance from the bottom of the page that |
* defines the triggering limit. By default, the mode is on and the margin |
* is 2 cm. |
* |
* @param boolean auto Boolean indicating if mode should be on or off. |
* @param float $margin Distance from the bottom of the page. |
*/ |
function setAutoPageBreak($auto, $margin = 0) |
{ |
$this->_auto_page_break = $auto; |
$this->_break_margin = $margin; |
$this->_page_break_trigger = $this->h - $margin; |
} |
/** |
* Defines the way the document is to be displayed by the viewer. The zoom |
* level can be set: pages can be displayed entirely on screen, occupy the |
* full width of the window, use real size, be scaled by a specific |
* zooming factor or use viewer default (configured in the Preferences |
* menu of Acrobat). The page layout can be specified too: single at once, |
* continuous display, two columns or viewer default. |
* By default, documents use the full width mode with continuous display. |
* |
* @param mixed $zoom The zoom to use. It can be one of the |
* following string values: |
* - fullpage: entire page on screen |
* - fullwidth: maximum width of window |
* - real: uses real size (100% zoom) |
* - default: uses viewer default mode |
* or a number indicating the zooming factor. |
* @param string layout The page layout. Possible values are: |
* - single: one page at once |
* - continuous: pages in continuously |
* - two: two pages on two columns |
* - default: uses viewer default mode |
* Default value is continuous. |
*/ |
function setDisplayMode($zoom, $layout = 'continuous') |
{ |
$zoom = strtolower($zoom); |
if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real' |
|| $zoom == 'default' || !is_string($zoom)) { |
$this->_zoom_mode = $zoom; |
} elseif ($zoom == 'zoom') { |
$this->_zoom_mode = $layout; |
} else { |
return $this->raiseError(sprintf('Incorrect zoom display mode: %s', $zoom)); |
} |
$layout = strtolower($layout); |
if ($layout == 'single' || $layout == 'continuous' || $layout == 'two' |
|| $layout == 'default') { |
$this->_layout_mode = $layout; |
} elseif ($zoom != 'zoom') { |
return $this->raiseError(sprintf('Incorrect layout display mode: %s', $layout)); |
} |
} |
/** |
* Activates or deactivates page compression. When activated, the internal |
* representation of each page is compressed, which leads to a compression |
* ratio of about 2 for the resulting document. |
* Compression is on by default. |
* Note: the Zlib extension is required for this feature. If not present, |
* compression will be turned off. |
* |
* @param boolean $compress Boolean indicating if compression must be |
* enabled or not. |
*/ |
function setCompression($compress) |
{ |
/* If no gzcompress function is available then default to false. */ |
$this->_compress = (function_exists('gzcompress') ? $compress : false); |
} |
/** |
* Set the info to a document. Possible info settings are: |
* - title |
* - subject |
* - author |
* - keywords |
* - creator |
* |
* @param mixed $info If passed as an array then the complete hash |
* containing the info to be inserted into the |
* document. Otherwise the name of setting to be set. |
* @param string $value The value of the setting. |
*/ |
function setInfo($info, $value = '') |
{ |
if (is_array($info)) { |
$this->_info = $info; |
} else { |
$this->_info[$info] = $value; |
} |
} |
/** |
* Defines an alias for the total number of pages. It will be substituted |
* as the document is closed. |
* |
* Example: |
* class My_File_PDF extends File_PDF { |
* function footer() |
* { |
* // Go to 1.5 cm from bottom |
* $this->setY(-15); |
* // Select Arial italic 8 |
* $this->setFont('Arial', 'I', 8); |
* // Print current and total page numbers |
* $this->cell(0, 10, 'Page ' . $this->getPageNo() . '/{nb}', 0, |
* 0, 'C'); |
* } |
* } |
* $pdf = &My_File_PDF::factory(); |
* $pdf->aliasNbPages(); |
* |
* @param string $alias The alias. Default value: {nb}. |
* |
* @see File_PDF::getPageNo |
* @see File_PDF::footer |
*/ |
function aliasNbPages($alias = '{nb}') |
{ |
$this->_alias_nb_pages = $alias; |
} |
/** |
* This method begins the generation of the PDF document; it must be |
* called before any output commands. No page is created by this method, |
* therefore it is necessary to call File_PDF::addPage. |
* |
* @see File_PDF::addPage |
* @see File_PDF::close |
*/ |
function open() |
{ |
$this->_beginDoc(); |
} |
/** |
* Terminates the PDF document. It is not necessary to call this method |
* explicitly because File_PDF::output does it automatically. |
* If the document contains no page, File_PDF::addPage is called to prevent |
* from getting an invalid document. |
* |
* @see File_PDF::open |
* @see File_PDF::output |
*/ |
function close() |
{ |
/* Terminate document */ |
if ($this->_page == 0) { |
$this->addPage(); |
} |
/* Page footer */ |
$this->_in_footer = true; |
$this->footer(); |
$this->_in_footer = false; |
/* Close page */ |
$this->_endPage(); |
/* Close document */ |
$this->_endDoc(); |
} |
/** |
* Adds a new page to the document. If a page is already present, the |
* File_PDF::footer method is called first to output the footer. Then the |
* page is added, the current position set to the top-left corner according |
* to the left and top margins, and File_PDF::header is called to display |
* the header. |
* The font which was set before calling is automatically restored. There |
* is no need to call File_PDF::setFont again if you want to continue with |
* the same font. The same is true for colors and line width. |
* The origin of the coordinate system is at the top-left corner and |
* increasing ordinates go downwards. |
* |
* @param string $orientation Page orientation. Possible values |
* are (case insensitive): |
* - P or Portrait |
* - L or Landscape |
* The default value is the one passed to the |
* constructor. |
* |
* @see File_PDF::PDF |
* @see File_PDF::header |
* @see File_PDF::footer |
* @see File_PDF::setMargins |
*/ |
function addPage($orientation = '') |
{ |
/* For good measure make sure this is called. */ |
$this->_beginDoc(); |
/* Save style settings so that they are not overridden by footer(). */ |
$lw = $this->_line_width; |
$dc = $this->_draw_color; |
$fc = $this->_fill_color; |
$tc = $this->_text_color; |
$cf = $this->_color_flag; |
if ($this->_page > 0) { |
/* Page footer. */ |
$this->_in_footer = true; |
$this->footer(); |
$this->_in_footer = false; |
/* Close page. */ |
$this->_endPage(); |
} |
/* Start new page. */ |
$this->_beginPage($orientation); |
/* Set line cap style to square. */ |
$this->_out('2 J'); |
/* Set line width. */ |
$this->_line_width = $lw; |
$this->_out(sprintf('%.2f w', $lw * $this->_scale)); |
/* Set font for the beginning of the page. */ |
$font_family = null; |
if ($this->_font_family) { |
$font_family = $this->_font_family; |
$font_style = $this->_font_style . ($this->_underline ? 'U' : ''); |
$font_size = $this->_font_size_pt; |
$this->setFont($font_family, $font_style, $font_size); |
} |
/* Set colors. */ |
$this->_fill_color = $fc; |
/* Check if fill color has been set before this page. */ |
if ($this->_fill_color != '0 g') { |
$this->_out($this->_fill_color); |
} |
$this->_draw_color = $dc; |
/* Check if draw color has been set before this page. */ |
if ($this->_draw_color != '0 G') { |
$this->_out($this->_draw_color); |
} |
$this->_text_color = $tc; |
$this->_color_flag = $cf; |
/* Page header. */ |
$this->header(); |
/* Restore line width. */ |
if ($this->_line_width != $lw) { |
$this->_line_width = $lw; |
$this->_out(sprintf('%.2f w', $lw * $this->_scale)); |
} |
/* Make sure the font is set for this page as it was before the |
* header. */ |
if ($font_family) { |
$this->setFont($font_family, $font_style, $font_size, true); |
} |
/* Restore colors. */ |
if ($this->_draw_color != $dc) { |
$this->_draw_color = $dc; |
$this->_out($dc); |
} |
if ($this->_fill_color != $fc) { |
$this->_fill_color = $fc; |
$this->_out($fc); |
} |
$this->_text_color = $tc; |
$this->_color_flag = $cf; |
} |
/** |
* This method is used to render the page header. It is automatically |
* called by File_PDF::addPage and should not be called directly by the |
* application. The implementation in File_PDF:: is empty, so you have to |
* subclass it and override the method if you want a specific processing. |
* |
* Example: |
* |
* class My_File_PDF extends File_PDF { |
* function header() |
* { |
* // Select Arial bold 15 |
* $this->setFont('Arial', 'B', 15); |
* // Move to the right |
* $this->cell(80); |
* // Framed title |
* $this->cell(30, 10, 'Title', 1, 0, 'C'); |
* // Line break |
* $this->newLine(20); |
* } |
* } |
* |
* @see File_PDF::footer |
*/ |
function header() |
{ |
/* To be implemented in your own inherited class. */ |
} |
/** |
* This method is used to render the page footer. It is automatically |
* called by File_PDF::addPage and File_PDF::close and should not be called |
* directly by the application. The implementation in File_PDF:: is empty, |
* so you have to subclass it and override the method if you want a specific |
* processing. |
* |
* Example: |
* |
* class My_File_PDF extends File_PDF { |
* function footer() |
* { |
* // Go to 1.5 cm from bottom |
* $this->setY(-15); |
* // Select Arial italic 8 |
* $this->setFont('Arial', 'I', 8); |
* // Print centered page number |
* $this->cell(0, 10, 'Page ' . $this->getPageNo(), 0, 0, 'C'); |
* } |
* } |
* |
* @see File_PDF::header |
*/ |
function footer() |
{ |
/* To be implemented in your own inherited class. */ |
} |
/** |
* Returns the current page number. |
* |
* @return integer |
* |
* @see File_PDF::aliasNbPages |
*/ |
function getPageNo() |
{ |
return $this->_page; |
} |
/** |
* Sets the fill color. |
* |
* Depending on the colorspace called, the number of color component |
* parameters required can be either 1, 3 or 4. The method can be called |
* before the first page is created and the color is retained from page to |
* page. |
* |
* @param string $cs Indicates the colorspace which can be either 'rgb', |
* 'cmyk' or 'gray'. Defaults to 'rgb'. |
* @param float $c1 First color component, floating point value between 0 |
* and 1. Required for gray, rgb and cmyk. |
* @param float $c2 Second color component, floating point value between |
* 0 and 1. Required for rgb and cmyk. |
* @param float $c3 Third color component, floating point value between |
* 0 and 1. Required for rgb and cmyk. |
* @param float $c4 Fourth color component, floating point value between |
* 0 and 1. Required for cmyk. |
* |
* @see File_PDF::setTextColor |
* @see File_PDF::setDrawColor |
* @see File_PDF::rect |
* @see File_PDF::cell |
* @see File_PDF::multiCell |
*/ |
function setFillColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0) |
{ |
$cs = strtolower($cs); |
if ($cs == 'rgb') { |
$this->_fill_color = sprintf('%.3f %.3f %.3f rg', $c1, $c2, $c3); |
} elseif ($cs == 'cmyk') { |
$this->_fill_color = sprintf('%.3f %.3f %.3f %.3f k', $c1, $c2, $c3, $c4); |
} else { |
$this->_fill_color = sprintf('%.3f g', $c1); |
} |
if ($this->_page > 0) { |
$this->_out($this->_fill_color); |
} |
$this->_color_flag = $this->_fill_color != $this->_text_color; |
} |
/** |
* Sets the text color. |
* |
* Depending on the colorspace called, the number of color component |
* parameters required can be either 1, 3 or 4. The method can be called |
* before the first page is created and the color is retained from page to |
* page. |
* |
* @param string $cs Indicates the colorspace which can be either 'rgb', |
* 'cmyk' or 'gray'. Defaults to 'rgb'. |
* @param float $c1 First color component, floating point value between 0 |
* and 1. Required for gray, rgb and cmyk. |
* @param float $c2 Second color component, floating point value between |
* 0 and 1. Required for rgb and cmyk. |
* @param float $c3 Third color component, floating point value between |
* 0 and 1. Required for rgb and cmyk. |
* @param float $c4 Fourth color component, floating point value between |
* 0 and 1. Required for cmyk. |
* |
* @since File_PDF 0.2.0 |
* @since Horde 3.2 |
* @see File_PDF::setFillColor |
* @see File_PDF::setDrawColor |
* @see File_PDF::rect |
* @see File_PDF::cell |
* @see File_PDF::multiCell |
*/ |
function setTextColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0) |
{ |
$cs = strtolower($cs); |
if ($cs == 'rgb') { |
$this->_text_color = sprintf('%.3f %.3f %.3f rg', $c1, $c2, $c3); |
} elseif ($cs == 'cmyk') { |
$this->_text_color = sprintf('%.3f %.3f %.3f %.3f k', $c1, $c2, $c3, $c4); |
} else { |
$this->_text_color = sprintf('%.3f g', $c1); |
} |
if ($this->_page > 0) { |
$this->_out($this->_text_color); |
} |
$this->_color_flag = $this->_fill_color != $this->_text_color; |
} |
/** |
* Sets the draw color, used when drawing lines. Depending on the |
* colorspace called, the number of color component parameters required |
* can be either 1, 3 or 4. The method can be called before the first page |
* is created and the color is retained from page to page. |
* |
* @param string $cs Indicates the colorspace which can be either 'rgb', |
* 'cmyk' or 'gray'. Defaults to 'rgb'. |
* @param float $c1 First color component, floating point value between 0 |
* and 1. Required for gray, rgb and cmyk. |
* @param float $c2 Second color component, floating point value between |
* 0 and 1. Required for rgb and cmyk. |
* @param float $c3 Third color component, floating point value between 0 |
* and 1. Required for rgb and cmyk. |
* @param float $c4 Fourth color component, floating point value between |
* 0 and 1. Required for cmyk. |
* |
* @see File_PDF::setFillColor |
* @see File_PDF::line |
* @see File_PDF::rect |
* @see File_PDF::cell |
* @see File_PDF::multiCell |
*/ |
function setDrawColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0) |
{ |
$cs = strtolower($cs); |
if ($cs == 'rgb') { |
$this->_draw_color = sprintf('%.3f %.3f %.3f RG', $c1, $c2, $c3); |
} elseif ($cs == 'cmyk') { |
$this->_draw_color = sprintf('%.3f %.3f %.3f %.3f K', $c1, $c2, $c3, $c4); |
} else { |
$this->_draw_color = sprintf('%.3f G', $c1); |
} |
if ($this->_page > 0) { |
$this->_out($this->_draw_color); |
} |
} |
/** |
* Returns the length of a text string. A font must be selected. |
* |
* @param string $text The text whose length is to be computed. |
* @param boolean $pt Boolean to indicate if the width should be returned |
* in points or user units. Default is 'false'. |
* |
* @return float |
*/ |
function getStringWidth($text, $pt = false) |
{ |
$text = (string)$text; |
$width = 0; |
$length = strlen($text); |
for ($i = 0; $i < $length; $i++) { |
$width += $this->_current_font['cw'][$text{$i}]; |
} |
/* Adjust for word spacing. */ |
$width += $this->_word_spacing * substr_count($text, ' ') * $this->_current_font['cw'][' ']; |
if ($pt) { |
return $width * $this->_font_size_pt / 1000; |
} else { |
return $width * $this->_font_size / 1000; |
} |
} |
/** |
* Defines the line width. By default, the value equals 0.2 mm. The method |
* can be called before the first page is created and the value is |
* retained from page to page. |
* |
* @param float $width The width. |
* |
* @see File_PDF::line |
* @see File_PDF::rect |
* @see File_PDF::cell |
* @see File_PDF::multiCell |
*/ |
function setLineWidth($width) |
{ |
$this->_line_width = $width; |
if ($this->_page > 0) { |
$this->_out(sprintf('%.2f w', $width * $this->_scale)); |
} |
} |
/** |
* Draws a line between two points. |
* |
* All coordinates can be negative to provide values from the right or |
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2). |
* |
* @param float $x1 Abscissa of first point. |
* @param float $y1 Ordinate of first point. |
* @param float $x2 Abscissa of second point. |
* @param float $y2 Ordinate of second point. |
* |
* @see File_PDF::setLineWidth |
* @see File_PDF::setDrawColor. |
*/ |
function line($x1, $y1, $x2, $y2) |
{ |
if ($x1 < 0) { |
$x1 += $this->w; |
} |
if ($y1 < 0) { |
$y1 += $this->h; |
} |
if ($x2 < 0) { |
$x2 += $this->w; |
} |
if ($y2 < 0) { |
$y2 += $this->h; |
} |
$this->_out(sprintf('%.2f %.2f m %.2f %.2f l S', $x1 * $this->_scale, ($this->h - $y1) * $this->_scale, $x2 * $this->_scale, ($this->h - $y2) * $this->_scale)); |
} |
/** |
* Outputs a rectangle. It can be drawn (border only), filled (with no |
* border) or both. |
* |
* All coordinates can be negative to provide values from the right or |
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2). |
* |
* @param float $x Abscissa of upper-left corner. |
* @param float $y Ordinate of upper-left corner. |
* @param float $width Width. |
* @param float $height Height. |
* @param float $style Style of rendering. Possible values are: |
* - D or empty string: draw (default) |
* - F: fill |
* - DF or FD: draw and fill |
* |
* @see File_PDF::setLineWidth |
* @see File_PDF::setDrawColor |
* @see File_PDF::setFillColor |
*/ |
function rect($x, $y, $width, $height, $style = '') |
{ |
if ($x < 0) { |
$x += $this->w; |
} |
if ($y < 0) { |
$y += $this->h; |
} |
$style = strtoupper($style); |
if ($style == 'F') { |
$op = 'f'; |
} elseif ($style == 'FD' || $style == 'DF') { |
$op = 'B'; |
} else { |
$op = 'S'; |
} |
$x = $this->_toPt($x); |
$y = $this->_toPt($y); |
$width = $this->_toPt($width); |
$height = $this->_toPt($height); |
$this->_out(sprintf('%.2f %.2f %.2f %.2f re %s', $x, $this->hPt - $y, $width, -$height, $op)); |
} |
/** |
* Outputs a circle. It can be drawn (border only), filled (with no |
* border) or both. |
* |
* All coordinates can be negative to provide values from the right or |
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2). |
* |
* @param float $x Abscissa of the center of the circle. |
* @param float $y Ordinate of the center of the circle. |
* @param float $r Circle radius. |
* @param string $style Style of rendering. Possible values are: |
* - D or empty string: draw (default) |
* - F: fill |
* - DF or FD: draw and fill |
*/ |
function circle($x, $y, $r, $style = '') |
{ |
if ($x < 0) { |
$x += $this->w; |
} |
if ($y < 0) { |
$y += $this->h; |
} |
$style = strtolower($style); |
if ($style == 'f') { |
$op = 'f'; // Style is fill only. |
} elseif ($style == 'fd' || $style == 'df') { |
$op = 'B'; // Style is fill and stroke. |
} else { |
$op = 'S'; // Style is stroke only. |
} |
$x = $this->_toPt($x); |
$y = $this->_toPt($y); |
$r = $this->_toPt($r); |
/* Invert the y scale. */ |
$y = $this->hPt - $y; |
/* Length of the Bezier control. */ |
$b = $r * 0.552; |
/* Move from the given origin and set the current point |
* to the start of the first Bezier curve. */ |
$c = sprintf('%.2f %.2f m', $x - $r, $y); |
$x = $x - $r; |
/* First circle quarter. */ |
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c', |
$x, $y + $b, // First control point. |
$x + $r - $b, $y + $r, // Second control point. |
$x + $r, $y + $r); // Final point. |
/* Set x/y to the final point. */ |
$x = $x + $r; |
$y = $y + $r; |
/* Second circle quarter. */ |
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c', |
$x + $b, $y, |
$x + $r, $y - $r + $b, |
$x + $r, $y - $r); |
/* Set x/y to the final point. */ |
$x = $x + $r; |
$y = $y - $r; |
/* Third circle quarter. */ |
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c', |
$x, $y - $b, |
$x - $r + $b, $y - $r, |
$x - $r, $y - $r); |
/* Set x/y to the final point. */ |
$x = $x - $r; |
$y = $y - $r; |
/* Fourth circle quarter. */ |
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c %s', |
$x - $b, $y, |
$x - $r, $y + $r - $b, |
$x - $r, $y + $r, |
$op); |
/* Output the whole string. */ |
$this->_out($c); |
} |
/** |
* Imports a TrueType or Type1 font and makes it available. It is |
* necessary to generate a font definition file first with the |
* makefont.php utility. |
* The location of the definition file (and the font file itself when |
* embedding) must be found at the full path name included. |
* |
* Example: |
* $pdf->addFont('Comic', 'I'); |
* is equivalent to: |
* $pdf->addFont('Comic', 'I', 'comici.php'); |
* |
* @param string $family Font family. The name can be chosen arbitrarily. |
* If it is a standard family name, it will |
* override the corresponding font. |
* @param string $style Font style. Possible values are (case |
* insensitive): |
* - empty string: regular (default) |
* - B: bold |
* - I: italic |
* - BI or IB: bold italic |
* @param string $file The font definition file. By default, the name is |
* built from the family and style, in lower case |
* with no space. |
* |
* @see File_PDF::setFont |
*/ |
function addFont($family, $style = '', $file = '') |
{ |
$family = strtolower($family); |
if ($family == 'arial') { |
$family = 'helvetica'; |
} |
$style = strtoupper($style); |
if ($style == 'IB') { |
$style = 'BI'; |
} |
if (isset($this->_fonts[$family . $style])) { |
return $this->raiseError(sprintf('Font already added: %s %s', $family, $style)); |
} |
if ($file == '') { |
$file = str_replace(' ', '', $family) . strtolower($style) . '.php'; |
} |
include($file); |
if (!isset($name)) { |
return $this->raiseError('Could not include font definition file.'); |
} |
$i = count($this->_fonts) + 1; |
$this->_fonts[$family . $style] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file); |
if ($diff) { |
/* Search existing encodings. */ |
$d = 0; |
$nb = count($this->_diffs); |
for ($i = 1; $i <= $nb; $i++) { |
if ($this->_diffs[$i] == $diff) { |
$d = $i; |
break; |
} |
} |
if ($d == 0) { |
$d = $nb + 1; |
$this->_diffs[$d] = $diff; |
} |
$this->_fonts[$family.$style]['diff'] = $d; |
} |
if ($file) { |
if ($type == 'TrueType') { |
$this->_font_files[$file] = array('length1' => $originalsize); |
} else { |
$this->_font_files[$file] = array('length1' => $size1, 'length2' => $size2); |
} |
} |
} |
/** |
* Sets the font used to print character strings. It is mandatory to call |
* this method at least once before printing text or the resulting |
* document would not be valid. The font can be either a standard one or a |
* font added via the File_PDF::addFont method. Standard fonts use Windows |
* encoding cp1252 (Western Europe). |
* The method can be called before the first page is created and the font |
* is retained from page to page. |
* If you just wish to change the current font size, it is simpler to call |
* File_PDF::setFontSize. |
* |
* @param string $family Family font. It can be either a name defined by |
* File_PDF::addFont or one of the standard families |
* (case insensitive): |
* - Courier (fixed-width) |
* - Helvetica or Arial (sans serif) |
* - Times (serif) |
* - Symbol (symbolic) |
* - ZapfDingbats (symbolic) |
* It is also possible to pass an empty string. In |
* that case, the current family is retained. |
* @param string $style Font style. Possible values are (case |
* insensitive): |
* - empty string: regular |
* - B: bold |
* - I: italic |
* - U: underline |
* or any combination. The default value is regular. |
* Bold and italic styles do not apply to Symbol and |
* ZapfDingbats. |
* @param integer $size Font size in points. The default value is the |
* current size. If no size has been specified since |
* the beginning of the document, the value taken |
* is 12. |
* @param boolean $force Force the setting of the font. Each new page will |
* require a new call to File_PDF::setFont and |
* settings this to true will make sure that the |
* checks for same font calls will be skipped. |
* |
* @see File_PDF::addFont |
* @see File_PDF::setFontSize |
* @see File_PDF::cell |
* @see File_PDF::multiCell |
* @see File_PDF::Write |
*/ |
function setFont($family, $style = '', $size = null, $force = false) |
{ |
$family = strtolower($family); |
if ($family == 'arial') { |
/* Use helvetica instead of arial. */ |
$family = 'helvetica'; |
} elseif ($family == 'symbol' || $family == 'zapfdingbats') { |
/* These two fonts do not have styles available. */ |
$style = ''; |
} |
$style = strtoupper($style); |
/* Underline is handled separately, if specified in the style var |
* remove it from the style and set the underline flag. */ |
if (strpos($style, 'U') !== false) { |
$this->_underline = true; |
$style = str_replace('U', '', $style); |
} else { |
$this->_underline = false; |
} |
if ($style == 'IB') { |
$style = 'BI'; |
} |
/* If no size specified, use current size. */ |
if (is_null($size)) { |
$size = $this->_font_size_pt; |
} |
/* If font requested is already the current font and no force setting |
* of the font is requested (eg. when adding a new page) don't bother |
* with the rest of the function and simply return. */ |
if ($this->_font_family == $family && $this->_font_style == $style && |
$this->_font_size_pt == $size && !$force) { |
return; |
} |
/* Set the font key. */ |
$fontkey = $family . $style; |
/* Test if already cached. */ |
if (!isset($this->_fonts[$fontkey])) { |
/* Get the character width definition file. */ |
$font_widths = &File_PDF::_getFontFile($fontkey); |
if (is_a($font_widths, 'PEAR_Error')) { |
return $font_widths; |
} |
$i = count($this->_fonts) + 1; |
$this->_fonts[$fontkey] = array( |
'i' => $i, |
'type' => 'core', |
'name' => $this->_core_fonts[$fontkey], |
'up' => -100, |
'ut' => 50, |
'cw' => $font_widths[$fontkey]); |
} |
/* Store font information as current font. */ |
$this->_font_family = $family; |
$this->_font_style = $style; |
$this->_font_size_pt = $size; |
$this->_font_size = $size / $this->_scale; |
$this->_current_font = &$this->_fonts[$fontkey]; |
/* Output font information if at least one page has been defined. */ |
if ($this->_page > 0) { |
$this->_out(sprintf('BT /F%d %.2f Tf ET', $this->_current_font['i'], $this->_font_size_pt)); |
} |
} |
/** |
* Defines the size of the current font. |
* |
* @param float $size The size (in points). |
* |
* @see File_PDF::setFont |
*/ |
function setFontSize($size) |
{ |
/* If the font size is already the current font size, just return. */ |
if ($this->_font_size_pt == $size) { |
return; |
} |
/* Set the current font size, both in points and scaled to user |
* units. */ |
$this->_font_size_pt = $size; |
$this->_font_size = $size / $this->_scale; |
/* Output font information if at least one page has been defined. */ |
if ($this->_page > 0) { |
$this->_out(sprintf('BT /F%d %.2f Tf ET', $this->_current_font['i'], $this->_font_size_pt)); |
} |
} |
/** |
* Defines the style of the current font. |
* |
* @param string $style The font style. |
* |
* @see File_PDF::setFont |
* @since File_PDF 0.2.0 |
* @since Horde 3.2 |
*/ |
function setFontStyle($style) |
{ |
$this->setFont($this->_font_family, $style); |
} |
/** |
* Creates a new internal link and returns its identifier. An internal |
* link is a clickable area which directs to another place within the |
* document. |
* The identifier can then be passed to File_PDF::cell, File_PDF::write, |
* File_PDF::image or File_PDF::link. The destination is defined with |
* File_PDF::setLink. |
* |
* @see File_PDF::cell |
* @see File_PDF::Write |
* @see File_PDF::image |
* @see File_PDF::Link |
* @see File_PDF::SetLink |
*/ |
function addLink() |
{ |
$n = count($this->_links) + 1; |
$this->_links[$n] = array(0, 0); |
return $n; |
} |
/** |
* Defines the page and position a link points to. |
* |
* @param integer $link The link identifier returned by File_PDF::addLink. |
* @param float $y Ordinate of target position; -1 indicates the |
* current position. The default value is 0 (top of |
* page). |
* @param integer $page Number of target page; -1 indicates the current |
* page. This is the default value. |
* |
* @see File_PDF::addLink |
*/ |
function setLink($link, $y = 0, $page = -1) |
{ |
if ($y == -1) { |
$y = $this->y; |
} |
if ($page == -1) { |
$page = $this->_page; |
} |
$this->_links[$link] = array($page, $y); |
} |
/** |
* Puts a link on a rectangular area of the page. Text or image links are |
* generally put via File_PDF::cell, File_PDF::Write or File_PDF::image, |
* but this method can be useful for instance to define a clickable area |
* inside an image. |
* |
* All coordinates can be negative to provide values from the right or |
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2). |
* |
* @param float $x Abscissa of the upper-left corner of the rectangle. |
* @param float $y Ordinate of the upper-left corner of the rectangle. |
* @param float $width Width of the rectangle. |
* @param float $height Height of the rectangle. |
* @param mixed $link URL or identifier returned by File_PDF::addLink. |
* |
* @see File_PDF::addLink |
* @see File_PDF::cell |
* @see File_PDF::Write |
* @see File_PDF::image |
*/ |
function link($x, $y, $width, $height, $link) |
{ |
if ($x < 0) { |
$x += $this->w; |
} |
if ($y < 0) { |
$y += $this->h; |
} |
/* Set up the coordinates with correct scaling in pt. */ |
$x = $this->_toPt($x); |
$y = $this->hPt - $this->_toPt($y); |
$width = $this->_toPt($width); |
$height = $this->_toPt($height); |
/* Save link to page links array. */ |
$this->_link($x, $y, $width, $height, $link); |
} |
/** |
* Prints a character string. The origin is on the left of the first |
* character, on the baseline. This method allows to place a string |
* precisely on the page, but it is usually easier to use File_PDF::cell, |
* File_PDF::multiCell or File_PDF::Write which are the standard methods to |
* print text. |
* |
* All coordinates can be negative to provide values from the right or |
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2). |
* |
* @param float $x Abscissa of the origin. |
* @param float $y Ordinate of the origin. |
* @param string $text String to print. |
* |
* @see File_PDF::setFont |
* @see File_PDF::cell |
* @see File_PDF::multiCell |
* @see File_PDF::Write |
*/ |
function text($x, $y, $text) |
{ |
if ($x < 0) { |
$x += $this->w; |
} |
if ($y < 0) { |
$y += $this->h; |
} |
/* Scale coordinates into points and set correct Y position. */ |
$x = $this->_toPt($x); |
$y = $this->hPt - $this->_toPt($y); |
/* Escape any potentially harmful characters. */ |
$text = $this->_escape($text); |
$out = sprintf('BT %.2f %.2f Td (%s) Tj ET', $x, $y, $text); |
if ($this->_underline && $text != '') { |
$out .= ' ' . $this->_doUnderline($x, $y, $text); |
} |
if ($this->_color_flag) { |
$out = sprintf('q %s %s Q', $this->_text_color, $out); |
} |
$this->_out($out); |
} |
/** |
* Whenever a page break condition is met, the method is called, and the |
* break is issued or not depending on the returned value. The default |
* implementation returns a value according to the mode selected by |
* File_PDF:setAutoPageBreak. |
* This method is called automatically and should not be called directly |
* by the application. |
* |
* @return boolean |
* |
* @see File_PDF::setAutoPageBreak. |
*/ |
function acceptPageBreak() |
{ |
return $this->_auto_page_break; |
} |
/** |
* Prints a cell (rectangular area) with optional borders, background |
* color and character string. The upper-left corner of the cell |
* corresponds to the current position. The text can be aligned or |
* centered. After the call, the current position moves to the right or to |
* the next line. It is possible to put a link on the text. |
* If automatic page breaking is enabled and the cell goes beyond the |
* limit, a page break is done before outputting. |
* |
* @param float $width Cell width. If 0, the cell extends up to the right |
* margin. |
* @param float $height Cell height. Default value: 0. |
* @param string $text String to print. Default value: empty. |
* @param mixed $border Indicates if borders must be drawn around the |
* cell. The value can be either a number: |
* - 0: no border (default) |
* - 1: frame |
* or a string containing some or all of the |
* following characters (in any order): |
* - L: left |
* - T: top |
* - R: right |
* - B: bottom |
* @param integer $ln Indicates where the current position should go |
* after the call. Possible values are: |
* - 0: to the right (default) |
* - 1: to the beginning of the next line |
* - 2: below |
* Putting 1 is equivalent to putting 0 and calling |
* File_PDF::newLine just after. |
* @param string $align Allows to center or align the text. Possible |
* values are: |
* - L or empty string: left (default) |
* - C: center |
* - R: right |
* @param integer $fill Indicates if the cell fill type. Possible values |
* are: |
* - 0: transparent (default) |
* - 1: painted |
* @param string $link URL or identifier returned by |
* File_PDF:addLink. |
* |
* @see File_PDF::setFont |
* @see File_PDF::setDrawColor |
* @see File_PDF::setFillColor |
* @see File_PDF::setLineWidth |
* @see File_PDF::addLink |
* @see File_PDF::newLine |
* @see File_PDF::multiCell |
* @see File_PDF::Write |
* @see File_PDF::setAutoPageBreak |
*/ |
function cell($width, $height = 0, $text = '', $border = 0, $ln = 0, |
$align = '', $fill = 0, $link = '') |
{ |
$k = $this->_scale; |
if ($this->y + $height > $this->_page_break_trigger && |
!$this->_in_footer && $this->AcceptPageBreak()) { |
$x = $this->x; |
$ws = $this->_word_spacing; |
if ($ws > 0) { |
$this->_word_spacing = 0; |
$this->_out('0 Tw'); |
} |
$this->addPage($this->_current_orientation); |
$this->x = $x; |
if ($ws > 0) { |
$this->_word_spacing = $ws; |
$this->_out(sprintf('%.3f Tw', $ws * $k)); |
} |
} |
if ($width == 0) { |
$width = $this->w - $this->_right_margin - $this->x; |
} |
$s = ''; |
if ($fill == 1 || $border == 1) { |
if ($fill == 1) { |
$op = ($border == 1) ? 'B' : 'f'; |
} else { |
$op = 'S'; |
} |
$s = sprintf('%.2f %.2f %.2f %.2f re %s ', $this->x * $k, ($this->h - $this->y) * $k, $width * $k, -$height * $k, $op); |
} |
if (is_string($border)) { |
if (strpos($border, 'L') !== false) { |
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $this->x * $k, ($this->h - $this->y) * $k, $this->x * $k, ($this->h - ($this->y + $height)) * $k); |
} |
if (strpos($border, 'T') !== false) { |
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $this->x * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - $this->y) * $k); |
} |
if (strpos($border, 'R') !== false) { |
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', ($this->x + $width) * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k); |
} |
if (strpos($border, 'B') !== false) { |
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $this->x * $k, ($this->h - ($this->y + $height)) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k); |
} |
} |
if ($text != '') { |
if ($align == 'R') { |
$dx = $width - $this->_cell_margin - $this->getStringWidth($text); |
} elseif ($align == 'C') { |
$dx = ($width - $this->getStringWidth($text)) / 2; |
} else { |
$dx = $this->_cell_margin; |
} |
if ($this->_color_flag) { |
$s .= 'q ' . $this->_text_color . ' '; |
} |
$text = str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $text))); |
$test2 = ((.5 * $height) + (.3 * $this->_font_size)); |
$test1 = $this->fhPt - (($this->y + $test2) * $k); |
$s .= sprintf('BT %.2f %.2f Td (%s) Tj ET', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $height + .3 * $this->_font_size)) * $k, $text); |
if ($this->_underline) { |
$s .= ' ' . $this->_doUnderline($this->x + $dx, $this->y + .5 * $height + .3 * $this->_font_size, $text); |
} |
if ($this->_color_flag) { |
$s .= ' Q'; |
} |
if ($link) { |
$this->link($this->x + $dx, $this->y + .5 * $height-.5 * $this->_font_size, $this->getStringWidth($text), $this->_font_size, $link); |
} |
} |
if ($s) { |
$this->_out($s); |
} |
$this->_last_height = $height; |
if ($ln > 0) { |
/* Go to next line. */ |
$this->y += $height; |
if ($ln == 1) { |
$this->x = $this->_left_margin; |
} |
} else { |
$this->x += $width; |
} |
} |
/** |
* This method allows printing text with line breaks. They can be |
* automatic (as soon as the text reaches the right border of the cell) or |
* explicit (via the \n character). As many cells as necessary are output, |
* one below the other. |
* Text can be aligned, centered or justified. The cell block can be |
* framed and the background painted. |
* |
* @param float $width Width of cells. If 0, they extend up to the right |
* margin of the page. |
* @param float $height Height of cells. |
* @param string $text String to print. |
* @param mixed $border Indicates if borders must be drawn around the cell |
* block. The value can be either a number: |
* - 0: no border (default) |
* - 1: frame |
* or a string containing some or all of the |
* following characters (in any order): |
* - L: left |
* - T: top |
* - R: right |
* - B: bottom |
* @param string $align Sets the text alignment. Possible values are: |
* - L: left alignment |
* - C: center |
* - R: right alignment |
* - J: justification (default value) |
* @param integer $fill Indicates if the cell background must: |
* - 0: transparent (default) |
* - 1: painted |
* |
* @see File_PDF::setFont |
* @see File_PDF::setDrawColor |
* @see File_PDF::setFillColor |
* @see File_PDF::setLineWidth |
* @see File_PDF::cell |
* @see File_PDF::write |
* @see File_PDF::setAutoPageBreak |
*/ |
function multiCell($width, $height, $text, $border = 0, $align = 'J', |
$fill = 0) |
{ |
$cw = &$this->_current_font['cw']; |
if ($width == 0) { |
$width = $this->w - $this->_right_margin - $this->x; |
} |
$wmax = ($width-2 * $this->_cell_margin) * 1000 / $this->_font_size; |
$s = str_replace("\r", '', $text); |
$nb = strlen($s); |
if ($nb > 0 && $s[$nb-1] == "\n") { |
$nb--; |
} |
$b = 0; |
if ($border) { |
if ($border == 1) { |
$border = 'LTRB'; |
$b = 'LRT'; |
$b2 = 'LR'; |
} else { |
$b2 = ''; |
if (strpos($border, 'L') !== false) { |
$b2 .= 'L'; |
} |
if (strpos($border, 'R') !== false) { |
$b2 .= 'R'; |
} |
$b = (strpos($border, 'T') !== false) ? $b2 . 'T' : $b2; |
} |
} |
$sep = -1; |
$i = 0; |
$j = 0; |
$l = 0; |
$ns = 0; |
$nl = 1; |
while ($i < $nb) { |
/* Get next character. */ |
$c = $s[$i]; |
if ($c == "\n") { |
/* Explicit line break. */ |
if ($this->_word_spacing > 0) { |
$this->_word_spacing = 0; |
$this->_out('0 Tw'); |
} |
$this->cell($width, $height, substr($s, $j, $i-$j), $b, 2, $align, $fill); |
$i++; |
$sep = -1; |
$j = $i; |
$l = 0; |
$ns = 0; |
$nl++; |
if ($border && $nl == 2) { |
$b = $b2; |
} |
continue; |
} |
if ($c == ' ') { |
$sep = $i; |
$ls = $l; |
$ns++; |
} |
$l += $cw[$c]; |
if ($l > $wmax) { |
/* Automatic line break. */ |
if ($sep == -1) { |
if ($i == $j) { |
$i++; |
} |
if ($this->_word_spacing > 0) { |
$this->_word_spacing = 0; |
$this->_out('0 Tw'); |
} |
$this->cell($width, $height, substr($s, $j, $i - $j), $b, 2, $align, $fill); |
} else { |
if ($align == 'J') { |
$this->_word_spacing = ($ns>1) ? ($wmax - $ls)/1000 * $this->_font_size / ($ns - 1) : 0; |
$this->_out(sprintf('%.3f Tw', $this->_word_spacing * $this->_scale)); |
} |
$this->cell($width, $height, substr($s, $j, $sep - $j), $b, 2, $align, $fill); |
$i = $sep + 1; |
} |
$sep = -1; |
$j = $i; |
$l = 0; |
$ns = 0; |
$nl++; |
if ($border && $nl == 2) { |
$b = $b2; |
} |
} else { |
$i++; |
} |
} |
/* Last chunk. */ |
if ($this->_word_spacing > 0) { |
$this->_word_spacing = 0; |
$this->_out('0 Tw'); |
} |
if ($border && strpos($border, 'B') !== false) { |
$b .= 'B'; |
} |
$this->cell($width, $height, substr($s, $j, $i), $b, 2, $align, $fill); |
$this->x = $this->_left_margin; |
} |
/** |
* This method prints text from the current position. When the right |
* margin is reached (or the \n character is met) a line break occurs and |
* text continues from the left margin. Upon method exit, the current |
* position is left just at the end of the text. |
* It is possible to put a link on the text. |
* |
* Example: |
* //Begin with regular font |
* $pdf->setFont('Arial','',14); |
* $pdf->write(5,'Visit '); |
* //Then put a blue underlined link |
* $pdf->setTextColor(0,0,255); |
* $pdf->setFont('','U'); |
* $pdf->write(5,'www.fpdf.org','http://www.fpdf.org'); |
* |
* @param float $height Line height. |
* @param string $text String to print. |
* @param mixed $link URL or identifier returned by AddLink(). |
* |
* @see File_PDF::setFont |
* @see File_PDF::addLink |
* @see File_PDF::multiCell |
* @see File_PDF::setAutoPageBreak |
*/ |
function write($height, $text, $link = '') |
{ |
$cw = &$this->_current_font['cw']; |
$width = $this->w - $this->_right_margin - $this->x; |
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size; |
$s = str_replace("\r", '', $text); |
$nb = strlen($s); |
$sep = -1; |
$i = 0; |
$j = 0; |
$l = 0; |
$nl = 1; |
while ($i < $nb) { |
/* Get next character. */ |
$c = $s{$i}; |
if ($c == "\n") { |
/* Explicit line break. */ |
$this->cell($width, $height, substr($s, $j, $i - $j), 0, 2, '', 0, $link); |
$i++; |
$sep = -1; |
$j = $i; |
$l = 0; |
if ($nl == 1) { |
$this->x = $this->_left_margin; |
$width = $this->w - $this->_right_margin - $this->x; |
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size; |
} |
$nl++; |
continue; |
} |
if ($c == ' ') { |
$sep = $i; |
$ls = $l; |
} |
$l += (isset($cw[$c]) ? $cw[$c] : 0); |
if ($l > $wmax) { |
/* Automatic line break. */ |
if ($sep == -1) { |
if ($this->x > $this->_left_margin) { |
/* Move to next line. */ |
$this->x = $this->_left_margin; |
$this->y += $height; |
$width = $this->w - $this->_right_margin - $this->x; |
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size; |
$i++; |
$nl++; |
continue; |
} |
if ($i == $j) { |
$i++; |
} |
$this->cell($width, $height, substr($s, $j, $i - $j), 0, 2, '', 0, $link); |
} else { |
$this->cell($width, $height, substr($s, $j, $sep - $j), 0, 2, '', 0, $link); |
$i = $sep + 1; |
} |
$sep = -1; |
$j = $i; |
$l = 0; |
if ($nl == 1) { |
$this->x = $this->_left_margin; |
$width = $this->w - $this->_right_margin - $this->x; |
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size; |
} |
$nl++; |
} else { |
$i++; |
} |
} |
/* Last chunk. */ |
if ($i != $j) { |
$this->cell($l / 1000 * $this->_font_size, $height, substr($s, $j, $i), 0, 0, '', 0, $link); |
} |
} |
/** |
* Writes text at an angle. |
* |
* All coordinates can be negative to provide values from the right or |
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2). |
* |
* @param integer $x X coordinate. |
* @param integer $y Y coordinate. |
* @param string $text Text to write. |
* @param float $text_angle Angle to rotate (Eg. 90 = bottom to top). |
* @param float $font_angle Rotate characters as well as text. |
* |
* @see File_PDF::setFont |
*/ |
function writeRotated($x, $y, $text, $text_angle, $font_angle = 0) |
{ |
if ($x < 0) { |
$x += $this->w; |
} |
if ($y < 0) { |
$y += $this->h; |
} |
/* Escape text. */ |
$text = $this->_escape($text); |
$font_angle += 90 + $text_angle; |
$text_angle *= M_PI / 180; |
$font_angle *= M_PI / 180; |
$text_dx = cos($text_angle); |
$text_dy = sin($text_angle); |
$font_dx = cos($font_angle); |
$font_dy = sin($font_angle); |
$s= sprintf('BT %.2f %.2f %.2f %.2f %.2f %.2f Tm (%s) Tj ET', |
$text_dx, $text_dy, $font_dx, $font_dy, |
$x * $this->_scale, ($this->h-$y) * $this->_scale, $text); |
if ($this->_draw_color) { |
$s = 'q ' . $this->_draw_color . ' ' . $s . ' Q'; |
} |
$this->_out($s); |
} |
/** |
* Prints an image in the page. The upper-left corner and at least one of |
* the dimensions must be specified; the height or the width can be |
* calculated automatically in order to keep the image proportions. |
* Supported formats are JPEG and PNG. |
* |
* All coordinates can be negative to provide values from the right or |
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2). |
* |
* For JPEG, all flavors are allowed: |
* - gray scales |
* - true colors (24 bits) |
* - CMYK (32 bits) |
* |
* For PNG, are allowed: |
* - gray scales on at most 8 bits (256 levels) |
* - indexed colors |
* - true colors (24 bits) |
* but are not supported: |
* - Interlacing |
* - Alpha channel |
* |
* If a transparent color is defined, it will be taken into account (but |
* will be only interpreted by Acrobat 4 and above). |
* The format can be specified explicitly or inferred from the file |
* extension. |
* It is possible to put a link on the image. |
* |
* Remark: if an image is used several times, only one copy will be |
* embedded in the file. |
* |
* @param string $file Name of the file containing the image. |
* @param float $x Abscissa of the upper-left corner. |
* @param float $y Ordinate of the upper-left corner. |
* @param float $width Width of the image in the page. If equal to zero, |
* it is automatically calculated to keep the |
* original proportions. |
* @param float $height Height of the image in the page. If not specified |
* or equal to zero, it is automatically calculated |
* to keep the original proportions. |
* @param string $type Image format. Possible values are (case |
* insensitive) : JPG, JPEG, PNG. If not specified, |
* the type is inferred from the file extension. |
* @param mixed $link URL or identifier returned by File_PDF::addLink. |
* |
* @see File_PDF::addLink |
*/ |
function image($file, $x, $y, $width = 0, $height = 0, $type = '', |
$link = '') |
{ |
if ($x < 0) { |
$x += $this->w; |
} |
if ($y < 0) { |
$y += $this->h; |
} |
if (!isset($this->_images[$file])) { |
/* First use of image, get some file info. */ |
if ($type == '') { |
$pos = strrpos($file, '.'); |
if ($pos === false) { |
return $this->raiseError(sprintf('Image file has no extension and no type was specified: %s', $file)); |
} |
$type = substr($file, $pos + 1); |
} |
$type = strtolower($type); |
$mqr = get_magic_quotes_runtime(); |
set_magic_quotes_runtime(0); |
if ($type == 'jpg' || $type == 'jpeg') { |
$info = $this->_parseJPG($file); |
} elseif ($type == 'png') { |
$info = $this->_parsePNG($file); |
} else { |
return $this->raiseError(sprintf('Unsupported image file type: %s', $type)); |
} |
if (is_a($info, 'PEAR_Error')) { |
return $info; |
} |
set_magic_quotes_runtime($mqr); |
$info['i'] = count($this->_images) + 1; |
$this->_images[$file] = $info; |
} else { |
$info = $this->_images[$file]; |
} |
/* Make sure all vars are converted to pt scale. */ |
$x = $this->_toPt($x); |
$y = $this->_toPt($y); |
$width = $this->_toPt($width); |
$height = $this->_toPt($height); |
/* If not specified do automatic width and height calculations. */ |
if (empty($width) && empty($height)) { |
$width = $info['w']; |
$height = $info['h']; |
} elseif (empty($width)) { |
$width = $height * $info['w'] / $info['h']; |
} elseif (empty($height)) { |
$height = $width * $info['h'] / $info['w']; |
} |
$this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', $width, $height, $x, $this->hPt - ($y + $height), $info['i'])); |
/* Set any link if requested. */ |
if ($link) { |
$this->_link($x, $y, $width, $height, $link); |
} |
} |
/** |
* Performs a line break. The current abscissa goes back to the left |
* margin and the ordinate increases by the amount passed in parameter. |
* |
* @param float $height The height of the break. By default, the value |
* equals the height of the last printed cell. |
* |
* @see File_PDF::cell |
*/ |
function newLine($height = '') |
{ |
$this->x = $this->_left_margin; |
if (is_string($height)) { |
$this->y += $this->_last_height; |
} else { |
$this->y += $height; |
} |
} |
/** |
* Returns the abscissa of the current position in user units. |
* |
* @return float |
* |
* @see File_PDF::setX |
* @see File_PDF::getY |
* @see File_PDF::setY |
*/ |
function getX() |
{ |
return $this->x; |
} |
/** |
* Defines the abscissa of the current position. If the passed value is |
* negative, it is relative to the right of the page. |
* |
* @param float $x The value of the abscissa. |
* |
* @see File_PDF::getX |
* @see File_PDF::getY |
* @see File_PDF::setY |
* @see File_PDF::setXY |
*/ |
function setX($x) |
{ |
if ($x >= 0) { |
/* Absolute value. */ |
$this->x = $x; |
} else { |
/* Negative, so relative to right edge of the page. */ |
$this->x = $this->w + $x; |
} |
} |
/** |
* Returns the ordinate of the current position in user units. |
* |
* @return float |
* |
* @see File_PDF::setY |
* @see File_PDF::getX |
* @see File_PDF::setX |
*/ |
function getY() |
{ |
return $this->y; |
} |
/** |
* Defines the ordinate of the current position. If the passed value is |
* negative, it is relative to the bottom of the page. |
* |
* @param float $y The value of the ordinate. |
* |
* @see File_PDF::getX |
* @see File_PDF::getY |
* @see File_PDF::setY |
* @see File_PDF::setXY |
*/ |
function setY($y) |
{ |
if ($y >= 0) { |
/* Absolute value. */ |
$this->y = $y; |
} else { |
/* Negative, so relative to bottom edge of the page. */ |
$this->y = $this->h + $y; |
} |
} |
/** |
* Defines the abscissa and ordinate of the current position. If the |
* passed values are negative, they are relative respectively to the right |
* and bottom of the page. |
* |
* @param float $x The value of the abscissa. |
* @param float $y The value of the ordinate. |
* |
* @see File_PDF::setX |
* @see File_PDF::setY |
*/ |
function setXY($x, $y) |
{ |
$this->setY($y); |
$this->setX($x); |
} |
/** |
* Returns the raw PDF file. |
* |
* @see File_PDF::output |
*/ |
function getOutput() |
{ |
/* Check whether file has been closed. */ |
if ($this->_state < 3) { |
$this->close(); |
} |
return $this->_buffer; |
} |
/** |
* Function to output the buffered data to the browser. |
* |
* @param string $filename The filename for the output file. |
* @param boolean $inline True if inline, false if attachment. |
*/ |
function output($filename = 'unknown.pdf', $inline = false) |
{ |
/* Check whether file has been closed. */ |
if ($this->_state < 3) { |
$this->close(); |
} |
/* Check if headers have been sent. */ |
if (headers_sent()) { |
return $this->raiseError('Unable to send PDF file, some data has already been output to browser.'); |
} |
/* If HTTP_Download is not available return a PEAR_Error. */ |
if (!include_once 'HTTP/Download.php') { |
return $this->raiseError('Missing PEAR package HTTP_Download.'); |
} |
/* Params for the output. */ |
$disposition = !$inline ? HTTP_DOWNLOAD_ATTACHMENT : HTTP_DOWNLOAD_INLINE; |
$params = array('data' => $this->_buffer, |
'contenttype' => 'application/pdf', |
'contentdisposition' => array($disposition, $filename)); |
/* Output the file. */ |
return HTTP_Download::staticSend($params); |
} |
/** |
* Function to save the PDF file somewhere local to the server. |
* |
* @param string $filename The filename for the output file. |
*/ |
function save($filename = 'unknown.pdf') |
{ |
/* Check whether file has been closed. */ |
if ($this->_state < 3) { |
$this->close(); |
} |
$f = fopen($filename, 'wb'); |
if (!$f) { |
return $this->raiseError(sprintf('Unable to save PDF file: %s', $filename)); |
} |
fwrite($f, $this->_buffer, strlen($this->_buffer)); |
fclose($f); |
} |
function _toPt($val) |
{ |
return $val * $this->_scale; |
} |
function &_getFontFile($fontkey, $path = '') |
{ |
static $font_widths; |
if (!isset($font_widths[$fontkey])) { |
if (!empty($path)) { |
$file = $path . strtolower($fontkey) . '.php'; |
} else { |
$file = 'File/PDF/fonts/' . strtolower($fontkey) . '.php'; |
} |
include $file; |
if (!isset($font_widths[$fontkey])) { |
return $this->raiseError(sprintf('Could not include font metric file: %s', $file)); |
} |
} |
return $font_widths; |
} |
function _link($x, $y, $width, $height, $link) |
{ |
/* Save link to page links array. */ |
$this->_page_links[$this->_page][] = array($x, $y, $width, $height, $link); |
} |
function _beginDoc() |
{ |
/* Start document, but only if not yet started. */ |
if ($this->_state < 1) { |
$this->_state = 1; |
$this->_out('%PDF-1.3'); |
} |
} |
function _putPages() |
{ |
$nb = $this->_page; |
if (!empty($this->_alias_nb_pages)) { |
/* Replace number of pages. */ |
for ($n = 1; $n <= $nb; $n++) { |
$this->_pages[$n] = str_replace($this->_alias_nb_pages, $nb, $this->_pages[$n]); |
} |
} |
if ($this->_default_orientation == 'P') { |
$wPt = $this->fwPt; |
$hPt = $this->fhPt; |
} else { |
$wPt = $this->fhPt; |
$hPt = $this->fwPt; |
} |
$filter = ($this->_compress) ? '/Filter /FlateDecode ' : ''; |
for ($n = 1; $n <= $nb; $n++) { |
/* Page */ |
$this->_newobj(); |
$this->_out('<</Type /Page'); |
$this->_out('/Parent 1 0 R'); |
if (isset($this->_orientation_changes[$n])) { |
$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $hPt, $wPt)); |
} |
$this->_out('/Resources 2 0 R'); |
if (isset($this->_page_links[$n])) { |
/* Links */ |
$annots = '/Annots ['; |
foreach ($this->_page_links[$n] as $pl) { |
$rect = sprintf('%.2f %.2f %.2f %.2f', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]); |
$annots .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . '] /Border [0 0 0] '; |
if (is_string($pl[4])) { |
$annots .= '/A <</S /URI /URI ' . $this->_textString($pl[4]) . '>>>>'; |
} else { |
$l = $this->_links[$pl[4]]; |
$height = isset($this->_orientation_changes[$l[0]]) ? $wPt : $hPt; |
$annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>', 1 + 2 * $l[0], $height - $l[1] * $this->_scale); |
} |
} |
$this->_out($annots.']'); |
} |
$this->_out('/Contents ' . ($this->_n + 1) . ' 0 R>>'); |
$this->_out('endobj'); |
/* Page content */ |
$p = ($this->_compress) ? gzcompress($this->_pages[$n]) : $this->_pages[$n]; |
$this->_newobj(); |
$this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>'); |
$this->_putStream($p); |
$this->_out('endobj'); |
} |
/* Pages root */ |
$this->_offsets[1] = strlen($this->_buffer); |
$this->_out('1 0 obj'); |
$this->_out('<</Type /Pages'); |
$kids = '/Kids ['; |
for ($i = 0; $i < $nb; $i++) { |
$kids .= (3 + 2 * $i) . ' 0 R '; |
} |
$this->_out($kids . ']'); |
$this->_out('/Count ' . $nb); |
$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $wPt, $hPt)); |
$this->_out('>>'); |
$this->_out('endobj'); |
} |
function _putFonts() |
{ |
$nf = $this->_n; |
foreach ($this->_diffs as $diff) { |
/* Encodings */ |
$this->_newobj(); |
$this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' . $diff . ']>>'); |
$this->_out('endobj'); |
} |
$mqr = get_magic_quotes_runtime(); |
set_magic_quotes_runtime(0); |
foreach ($this->_font_files as $file => $info) { |
/* Font file embedding. */ |
$this->_newobj(); |
$this->_font_files[$file]['n'] = $this->_n; |
$size = filesize($file); |
if (!$size) { |
return $this->raiseError('Font file not found.'); |
} |
$this->_out('<</Length ' . $size); |
if (substr($file, -2) == '.z') { |
$this->_out('/Filter /FlateDecode'); |
} |
$this->_out('/Length1 ' . $info['length1']); |
if (isset($info['length2'])) { |
$this->_out('/Length2 ' . $info['length2'] . ' /Length3 0'); |
} |
$this->_out('>>'); |
$f = fopen($file, 'rb'); |
$this->_putStream(fread($f, $size)); |
fclose($f); |
$this->_out('endobj'); |
} |
set_magic_quotes_runtime($mqr); |
foreach ($this->_fonts as $k => $font) { |
/* Font objects */ |
$this->_newobj(); |
$this->_fonts[$k]['n'] = $this->_n; |
$name = $font['name']; |
$this->_out('<</Type /Font'); |
$this->_out('/BaseFont /' . $name); |
if ($font['type'] == 'core') { |
/* Standard font. */ |
$this->_out('/Subtype /Type1'); |
if ($name != 'Symbol' && $name != 'ZapfDingbats') { |
$this->_out('/Encoding /WinAnsiEncoding'); |
} |
} else { |
/* Additional font. */ |
$this->_out('/Subtype /' . $font['type']); |
$this->_out('/FirstChar 32'); |
$this->_out('/LastChar 255'); |
$this->_out('/Widths ' . ($this->_n + 1) . ' 0 R'); |
$this->_out('/FontDescriptor ' . ($this->_n + 2) . ' 0 R'); |
if ($font['enc']) { |
if (isset($font['diff'])) { |
$this->_out('/Encoding ' . ($nf + $font['diff']).' 0 R'); |
} else { |
$this->_out('/Encoding /WinAnsiEncoding'); |
} |
} |
} |
$this->_out('>>'); |
$this->_out('endobj'); |
if ($font['type'] != 'core') { |
/* Widths. */ |
$this->_newobj(); |
$cw = &$font['cw']; |
$s = '['; |
for ($i = 32; $i <= 255; $i++) { |
$s .= $cw[chr($i)] . ' '; |
} |
$this->_out($s . ']'); |
$this->_out('endobj'); |
/* Descriptor. */ |
$this->_newobj(); |
$s = '<</Type /FontDescriptor /FontName /' . $name; |
foreach ($font['desc'] as $k => $v) { |
$s .= ' /' . $k . ' ' . $v; |
} |
$file = $font['file']; |
if ($file) { |
$s .= ' /FontFile' . ($font['type'] == 'Type1' ? '' : '2') . ' ' . $this->_font_files[$file]['n'] . ' 0 R'; |
} |
$this->_out($s . '>>'); |
$this->_out('endobj'); |
} |
} |
} |
function _putImages() |
{ |
$filter = ($this->_compress) ? '/Filter /FlateDecode ' : ''; |
foreach ($this->_images as $file => $info) { |
$this->_newobj(); |
$this->_images[$file]['n'] = $this->_n; |
$this->_out('<</Type /XObject'); |
$this->_out('/Subtype /Image'); |
$this->_out('/Width ' . $info['w']); |
$this->_out('/Height ' . $info['h']); |
if ($info['cs'] == 'Indexed') { |
$this->_out('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal'])/3 - 1) . ' ' . ($this->_n + 1).' 0 R]'); |
} else { |
$this->_out('/ColorSpace /' . $info['cs']); |
if ($info['cs'] == 'DeviceCMYK') { |
$this->_out('/Decode [1 0 1 0 1 0 1 0]'); |
} |
} |
$this->_out('/BitsPerComponent ' . $info['bpc']); |
$this->_out('/Filter /' . $info['f']); |
if (isset($info['parms'])) { |
$this->_out($info['parms']); |
} |
if (isset($info['trns']) && is_array($info['trns'])) { |
$trns = ''; |
$i_max = count($info['trns']); |
for ($i = 0; $i < $i_max; $i++) { |
$trns .= $info['trns'][$i] . ' ' . $info['trns'][$i].' '; |
} |
$this->_out('/Mask [' . $trns . ']'); |
} |
$this->_out('/Length ' . strlen($info['data']) . '>>'); |
$this->_putStream($info['data']); |
$this->_out('endobj'); |
/* Palette. */ |
if ($info['cs'] == 'Indexed') { |
$this->_newobj(); |
$pal = ($this->_compress) ? gzcompress($info['pal']) : $info['pal']; |
$this->_out('<<' . $filter . '/Length ' . strlen($pal) . '>>'); |
$this->_putStream($pal); |
$this->_out('endobj'); |
} |
} |
} |
function _putResources() |
{ |
$this->_putFonts(); |
$this->_putImages(); |
/* Resource dictionary */ |
$this->_offsets[2] = strlen($this->_buffer); |
$this->_out('2 0 obj'); |
$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); |
$this->_out('/Font <<'); |
foreach ($this->_fonts as $font) { |
$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R'); |
} |
$this->_out('>>'); |
if (count($this->_images)) { |
$this->_out('/XObject <<'); |
foreach ($this->_images as $image) { |
$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R'); |
} |
$this->_out('>>'); |
} |
$this->_out('>>'); |
$this->_out('endobj'); |
} |
function _putInfo() |
{ |
$this->_out('/Producer ' . $this->_textString('Horde PDF')); |
if (!empty($this->_info['title'])) { |
$this->_out('/Title ' . $this->_textString($this->_info['title'])); |
} |
if (!empty($this->_info['subject'])) { |
$this->_out('/Subject ' . $this->_textString($this->_info['subject'])); |
} |
if (!empty($this->_info['author'])) { |
$this->_out('/Author ' . $this->_textString($this->_info['author'])); |
} |
if (!empty($this->keywords)) { |
$this->_out('/Keywords ' . $this->_textString($this->keywords)); |
} |
if (!empty($this->creator)) { |
$this->_out('/Creator ' . $this->_textString($this->creator)); |
} |
$this->_out('/CreationDate ' . $this->_textString('D:' . date('YmdHis'))); |
} |
function _putCatalog() |
{ |
$this->_out('/Type /Catalog'); |
$this->_out('/Pages 1 0 R'); |
if ($this->_zoom_mode == 'fullpage') { |
$this->_out('/OpenAction [3 0 R /Fit]'); |
} elseif ($this->_zoom_mode == 'fullwidth') { |
$this->_out('/OpenAction [3 0 R /FitH null]'); |
} elseif ($this->_zoom_mode == 'real') { |
$this->_out('/OpenAction [3 0 R /XYZ null null 1]'); |
} elseif (!is_string($this->_zoom_mode)) { |
$this->_out('/OpenAction [3 0 R /XYZ null null ' . ($this->_zoom_mode / 100).']'); |
} |
if ($this->_layout_mode == 'single') { |
$this->_out('/PageLayout /SinglePage'); |
} elseif ($this->_layout_mode == 'continuous') { |
$this->_out('/PageLayout /OneColumn'); |
} elseif ($this->_layout_mode == 'two') { |
$this->_out('/PageLayout /TwoColumnLeft'); |
} |
} |
function _putTrailer() |
{ |
$this->_out('/Size ' . ($this->_n + 1)); |
$this->_out('/Root ' . $this->_n . ' 0 R'); |
$this->_out('/Info ' . ($this->_n - 1) . ' 0 R'); |
} |
function _endDoc() |
{ |
$this->_putPages(); |
$this->_putResources(); |
/* Info */ |
$this->_newobj(); |
$this->_out('<<'); |
$this->_putInfo(); |
$this->_out('>>'); |
$this->_out('endobj'); |
/* Catalog */ |
$this->_newobj(); |
$this->_out('<<'); |
$this->_putCatalog(); |
$this->_out('>>'); |
$this->_out('endobj'); |
/* Cross-ref */ |
$o = strlen($this->_buffer); |
$this->_out('xref'); |
$this->_out('0 ' . ($this->_n + 1)); |
$this->_out('0000000000 65535 f '); |
for ($i = 1; $i <= $this->_n; $i++) { |
$this->_out(sprintf('%010d 00000 n ', $this->_offsets[$i])); |
} |
/* Trailer */ |
$this->_out('trailer'); |
$this->_out('<<'); |
$this->_putTrailer(); |
$this->_out('>>'); |
$this->_out('startxref'); |
$this->_out($o); |
$this->_out('%%EOF'); |
$this->_state = 3; |
} |
function _beginPage($orientation) |
{ |
$this->_page++; |
$this->_pages[$this->_page] = ''; |
$this->_state = 2; |
$this->x = $this->_left_margin; |
$this->y = $this->_top_margin; |
$this->_last_height = 0; |
/* Page orientation */ |
if (!$orientation) { |
$orientation = $this->_default_orientation; |
} else { |
$orientation = strtoupper($orientation[0]); |
if ($orientation != $this->_default_orientation) { |
$this->_orientation_changes[$this->_page] = true; |
} |
} |
if ($orientation != $this->_current_orientation) { |
/* Change orientation */ |
if ($orientation == 'P') { |
$this->wPt = $this->fwPt; |
$this->hPt = $this->fhPt; |
$this->w = $this->fw; |
$this->h = $this->fh; |
} else { |
$this->wPt = $this->fhPt; |
$this->hPt = $this->fwPt; |
$this->w = $this->fh; |
$this->h = $this->fw; |
} |
$this->_page_break_trigger = $this->h - $this->_break_margin; |
$this->_current_orientation = $orientation; |
} |
} |
function _endPage() |
{ |
/* End of page contents */ |
$this->_state = 1; |
} |
function _newobj() |
{ |
/* Begin a new object */ |
$this->_n++; |
$this->_offsets[$this->_n] = strlen($this->_buffer); |
$this->_out($this->_n . ' 0 obj'); |
} |
function _doUnderline($x, $y, $text) |
{ |
/* Set the rectangle width according to text width. */ |
$width = $this->getStringWidth($text, true); |
/* Set rectangle position and height, using underline position and |
* thickness settings scaled by the font size. */ |
$y = $y + ($this->_current_font['up'] * $this->_font_size_pt / 1000); |
$height = -$this->_current_font['ut'] * $this->_font_size_pt / 1000; |
return sprintf('%.2f %.2f %.2f %.2f re f', $x, $y, $width, $height); |
} |
function _parseJPG($file) |
{ |
/* Extract info from a JPEG file. */ |
$img = @getimagesize($file); |
if (!$img) { |
return $this->raiseError(sprintf('Missing or incorrect image file: %s', $file)); |
} |
if ($img[2] != 2) { |
return $this->raiseError(sprintf('Not a JPEG file: %s', $file)); |
} |
if (!isset($img['channels']) || $img['channels'] == 3) { |
$colspace = 'DeviceRGB'; |
} elseif ($img['channels'] == 4) { |
$colspace = 'DeviceCMYK'; |
} else { |
$colspace = 'DeviceGray'; |
} |
$bpc = isset($img['bits']) ? $img['bits'] : 8; |
/* Read whole file. */ |
$f = fopen($file, 'rb'); |
$data = fread($f, filesize($file)); |
fclose($f); |
return array('w' => $img[0], 'h' => $img[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data); |
} |
function _parsePNG($file) |
{ |
/* Extract info from a PNG file. */ |
$f = fopen($file, 'rb'); |
if (!$f) { |
return $this->raiseError(sprintf('Unable to open image file: %s', $file)); |
} |
/* Check signature. */ |
if (fread($f, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) { |
return $this->raiseError(sprintf('Not a PNG file: %s', $file)); |
} |
/* Read header chunk. */ |
fread($f, 4); |
if (fread($f, 4) != 'IHDR') { |
return $this->raiseError(sprintf('Incorrect PNG file: %s', $file)); |
} |
$width = $this->_freadInt($f); |
$height = $this->_freadInt($f); |
$bpc = ord(fread($f, 1)); |
if ($bpc > 8) { |
return $this->raiseError(sprintf('16-bit depth not supported: %s', $file)); |
} |
$ct = ord(fread($f, 1)); |
if ($ct == 0) { |
$colspace = 'DeviceGray'; |
} elseif ($ct == 2) { |
$colspace = 'DeviceRGB'; |
} elseif ($ct == 3) { |
$colspace = 'Indexed'; |
} else { |
return $this->raiseError(sprintf('Alpha channel not supported: %s', $file)); |
} |
if (ord(fread($f, 1)) != 0) { |
return $this->raiseError(sprintf('Unknown compression method: %s', $file)); |
} |
if (ord(fread($f, 1)) != 0) { |
return $this->raiseError(sprintf('Unknown filter method: %s', $file)); |
} |
if (ord(fread($f, 1)) != 0) { |
return $this->raiseError(sprintf('Interlacing not supported: %s', $file)); |
} |
fread($f, 4); |
$parms = '/DecodeParms <</Predictor 15 /Colors ' . ($ct == 2 ? 3 : 1).' /BitsPerComponent ' . $bpc . ' /Columns ' . $width.'>>'; |
/* Scan chunks looking for palette, transparency and image data. */ |
$pal = ''; |
$trns = ''; |
$data = ''; |
do { |
$n = $this->_freadInt($f); |
$type = fread($f, 4); |
if ($type == 'PLTE') { |
/* Read palette */ |
$pal = fread($f, $n); |
fread($f, 4); |
} elseif ($type == 'tRNS') { |
/* Read transparency info */ |
$t = fread($f, $n); |
if ($ct == 0) { |
$trns = array(ord(substr($t, 1, 1))); |
} elseif ($ct == 2) { |
$trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))); |
} else { |
$pos = strpos($t, chr(0)); |
if (is_int($pos)) { |
$trns = array($pos); |
} |
} |
fread($f, 4); |
} elseif ($type == 'IDAT') { |
/* Read image data block */ |
$data .= fread($f, $n); |
fread($f, 4); |
} elseif ($type == 'IEND') { |
break; |
} else { |
fread($f, $n + 4); |
} |
} while ($n); |
if ($colspace == 'Indexed' && empty($pal)) { |
return $this->raiseError(sprintf('Missing palette in: %s', $file)); |
} |
fclose($f); |
return array('w' => $width, 'h' => $height, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data); |
} |
function _freadInt($f) |
{ |
/* Read a 4-byte integer from file. */ |
$i = ord(fread($f, 1)) << 24; |
$i += ord(fread($f, 1)) << 16; |
$i += ord(fread($f, 1)) << 8; |
$i += ord(fread($f, 1)); |
return $i; |
} |
function _textString($s) |
{ |
/* Format a text string */ |
return '(' . $this->_escape($s) . ')'; |
} |
function _escape($s) |
{ |
/* Add \ before \, ( and ) */ |
return str_replace(array(')','(','\\'), |
array('\\)','\\(','\\\\'), |
$s); |
} |
function _putStream($s) |
{ |
$this->_out('stream'); |
$this->_out($s); |
$this->_out('endstream'); |
} |
function _out($s) |
{ |
/* Add a line to the document. */ |
if ($this->_state == 2) { |
$this->_pages[$this->_page] .= $s . "\n"; |
} else { |
$this->_buffer .= $s . "\n"; |
} |
} |
} |
/trunk/jrest/lib/SpreadsheetProductor.php |
---|
New file |
0,0 → 1,15 |
<?php |
Class SpreadsheetProductor { |
function initSpreadsheet() { |
require_once("Spreadsheet/Excel/Writer.php"); |
} |
} |
?> |
/trunk/jrest/lib/PDFProductor.php |
---|
New file |
0,0 → 1,14 |
<?php |
Class PDFProductor { |
function initPDF() { |
require_once("PDF.php"); |
} |
} |
?> |