Oct 1, 2012

PHP Best Practices

This guide will give you solutions to common PHP design problems. It also provides a sketch of an application layout that I developed during the implementation of some projects.
php.ini quirks
Some settings in the php.ini control how PHP interpretes your scripts. This can lead to unexpected behaviour when moving your application from development to the productive environment. The following measures reduce dependency of your code on php.ini settings.
short_open_tagAlways use the long PHP tags: php
echo "hello world"; ?>
Do not use the echo shortcut .
asp_tagsDo not use ASP like tags: <% echo "hello world"; %>
gpc_magic_quotesI recommend that you include code in a global include file which is run before any $_GET or $_POST parameter or $_COOKIE is read. That code should check if the gpc_magic_quotes option is enabled and run all $_GET, $_POST and $_COOKIE values through the stripslashes function.
register_globalsNever rely on this option beeing set. Always access all GET, POST and COOKIE values through the 'superglobal' $_GET, $_POST and $_COOKIE variables. For convenience declare $PHP_SELF = $_SERVER['PHP_SELF']; in your global include file after the gpc_magic_quotes quirk.
File uploads:The maximum size of an uploaded file is determined by the following parameters:

  • file_uploads must be 1 (default)
  • memory_limit must be slightly larger than the post_max_size and upload_max_filesize
  • post_max_size must be large enough
  • upload_max_filesize must be large enough
Have one single configuration file
You should define all configuration parameters of your application in a single (include) file. This way you can easily exchange this file to reflect settings for your local development site, a test site and the customer's production environment. Common configuration parameters are:

  • database connection parameters
  • email addresses
  • options
  • debug and logging output switches
  • application constants
Keep an eye on the namespace
As PHP does not have a namespace facility like Java packages, you must be very careful when choosing names for your classes and functions.

  • Avoid functions outside classes whenever possible and feasible. Classes provide some extra namespace for the methods and variables that live inside them.
  • If you declare global functions use a prefix. Some examples are dao_factory(),db_getConnection(), text_parseDate() etc.
Use a database abstraction layer
In PHP there are no database-independent functions for database access apart from ODBC (which nobody uses on Linux). You should not use the PHP database functions directly because this makes it expensive when the database product changes. Your customer may move from MySQL to Oracle one day or you will need an XML database maybe. You never know. Moreover an abstraction layer can ease development as the PHP database functions are not very userfriendly.Use Value Objects (VO)
VOs are actually a J2EE pattern. It can easily be implemented in PHP. A value object corresponds directly to a C struct. It's a class that contains only member variables and no methods other than convenience methods (usually none). A VO corresponds to a business object. A VO typically corresponds directly to a database table. Naming the VO member variables equal to the database fields is a good idea. Do not forget the ID column.class Person {
var $id, $first_name, $last_name, $email;
}
Use Data Access Objects (DAO)
DAO is actually a J2EE pattern. It can easily be implemented in PHP and helps greatly in separating database access from the rest of your code. The DAOs form a thin layer. The DAO layer can be 'stacked' which helps for instance if you want to add DB caching later when tuning your application. You should have one DAO class for every VO class. Naming conventions are a good practice. class PersonDAO {
var $conn;
function PersonDAO(&$conn) {
$this->conn =& $conn;
}
function save(&$vo) {
if ($v->id == 0) {
$this->insert($vo);
} else {
$this->update($vo);
}
}
function get($id) {
#execute select statement
#create new vo and call getFromResult
#return vo
}
function delete(&$vo) {
#execute delete statement
#set id on vo to 0
}
#-- private functions
function getFromResult(&vo, $result) {
#fill vo from the database result set
}
function update(&$vo) {
#execute update statement here
}
function insert(&$vo) {
#generate id (from Oracle sequence or automatically)
#insert record into db
#set id on vo
}
}
A DAO typically implements the following methods:

  • save: inserts or updates a record
  • get: fetches a record
  • delete: removes a record
The DAO may define additional methods as required by your application's needs. The should only perform actions that require the database (maybe only for performance reasons) and can not be implemented in a different mannor. Examples: isUsed(), getTop($n), find($criteria).
The DAO should only implement basic select / insert / update operations on onetable. It must not contain the business logic. For example the PersonDAO should not contain code to send email to a person. For n-to-n relationships create a separate DAO (and even a VO if the relationships has additional properties) for the relation table.
Write a factory function that returns the proper DAO given the class name of a VO.Caching is a good idea here. function dao_getDAO($vo_class) {
$conn = db_conn('default'); #get a connection from the pool
switch ($vo_class) {
case "person": return new PersonDAO($conn);
case "newsletter": return new NewsletterDAO($conn);
...
}
}
Generate code
99% of the code for your VOs and DAOs can be generated automatically from your database schema when you use some naming conventions for your tables and columns. Having a generator script ready saves you time when you are likely to change the database schema during development. I successfully used a perl script to generate my VOs and DAOs for a project. Unfortunately I am not allowed to post it here.Business logic
Business logic directly reflects the use cases. The business logic deals with VOs, modifies them according to the business requirements and uses DAOs to access the persistence layer. The business logic classes should provide means to retrieve information about errors that occurred. class NewsletterLogic {
function NewsletterLogic() {
}
function subscribePerson(&$person) {
...
}
function unsubscribePerson(&$person) {
...
}
function sendNewsletter(&$newsletter) {
...
}
}
Page logic (Controller)
When a page is called, the page controller is run before any output is made. The controller's job is to transform the HTTP request into business objects, then call the approriate logic and prepare the objects used to display the response.The page logic performs the following steps:
1. The cmd request parameter is evaluated. 2. Based on the action other request parameters are evaluated.3. Value Objects (or a form object for more complex tasks) are created from the parameters.4. The objects are validated and the result is stored in an error array.5. The business logic is called with the Value Objects.6. Return status (error codes) from the business logic is evaluated.7. A redirect to another page is executed if necessary.8. All data needed to display the page is collected and made available to the page as variables of the controller. Do not use global variables.
Note: it is a good idea to have a utility function that returns a parameter that is sent via GET or POST respectivly and provide a default value if the parameter is missing. The page logic is the only non-HTML include file in the actual page! The page logic file must include all other include files used by the logic (see base.inc.php below). Use the require_once PHP command to include non-HTML files.class PageController {
var $person; #$person is used by the HTML page
var $errs;
function PageController() {
$action = Form::getParameter('cmd');
$this->person = new Person();
$this->errs = array();
if ($action == 'save') {
$this->parseForm();
if (!this->validate()) return;
NewsletterLogic::subscribe($this->person);
header('Location: confirmation.php');
exit;
}
}
function parseForm() {
$this->person->name = Form::getParameter('name');
$this->person->birthdate = Util::parseDate(Form::getParameter('birthdate');
...
}
function validate() {
if ($this->person->name == '') $this->errs['name'] = FORM_MISSING;
#FORM_MISSING is a constant
...
return (sizeof($this->errs) == 0);
}
}
Presentation Layer
The top level page will contain the actual HTML code. You may include HTML parts that you reuse across pages like the navigation etc. The page expects the page logic to prepare all business objects that it needs. It's a good idea to document the business objects needed at the top of the page.The page accesses properties of those business objects and formats them into HTML. <?php require_once('control/ctl_person.inc.php'); #the page controller
$c =& new PageController();
?>
<html>
<body>
<form action="<?php echo htmlspecialchars($PHP_SELF) ?>" method="POST">
<input type="hidden" name="cmd" value="save">
<input type="text" name="name"
value="<?php echo htmlspecialchars($c->person->name); ?>">
<button type="submit">Subscribe</button>
</form>
</body>
</html>
Localization
Localization is a problem. You must choose among a) duplicating pages b) removing all hardcoded strings from your HTML.
As I work in a design company I usually take approach a). Approach b) is not feasible as it makes the HTML very hard to read and nearly impossible to edit in a visual web editor like Dreamweaver. Dynamic content is hard enough to edit with Dreamweaver. Removing also all strings, makes the page look quite empty...
So finish the project in one language first. The copy the HTML pages that need translation. Use a naming convention like index_fr.php to designate the French version of the index page. Always use the ISO two letter language codes. Do not invent your own language codes.
To keep track of the language the user selected you must choose among a) storing the language setting in a session variable or cookieb) reading the preferred language (locale) from the HTTP headers the browser sends youc) appending the language to the URL of every link in your application
While a) seems a lot more easier than c) it may be subject to session timeout. Option b) should only be implemented as an extension to a) or c). Strings in a database must be localized too!Making your application location independent
PHP has problems in some situations when include files are nested and reside in different folders and it is unclear at which directory level the file will be included. One can solve this by using absolute path names or using $_SERVER['DOCUMENT_ROOT']as a starting point. However this makes your code location dependent - it will not run anymore if you move it down your directory structure one level. Of cource we do not like that.I have found a convenient solution to this problem. The toplevel page (the one that is called by the browser) needs to know the relative path to the application root directory. Unfortunately there is no such function in PHP and the webapp context concept is completely absent in PHP. So we can not automatically determine the application root reliably in all situations (It is *really* impossible. Don't even try. It's not worth the effort.)Let's define a global variable called $ROOT in an include file in every directory that contains toplevel pages. The include file (call it root.inc.php) must be included by the page logic before any other include files. Now you can use the $ROOT variable to reference include files with their exact path!
Sample:We have toplevel pages in /admin/pages/. The $ROOT variable must therefore be set to$ROOT = '../..';. The page logic included by pages in that folder would reference their include files like require_once("$ROOT/lib/base.inc.php");.
In my suggested folder outline (see below) we don't even need that, since all toplevel pages reside in the webapp root directory anyway. So the webapp root directory is always the current directory.Folder outline
I suggest you make one file per class and follow a naming convention. Make sure that all your include files end with .php to avoid disclosure of your code to malicious users, which is a major security problem. I suggest the following folder structure:

/Webapp root directory. Contains the pages that are actually called by the browser.
/lib/Contains base.inc.php and config.inc.php
/lib/common/Contains libraries and tools reusable for other projects, like your database abstraction classes.
/lib/model/Contains the Value Object classes
/lib/dao/Contains the DAO classes and the DAO factory
/lib/logic/Contains the business logic classes
/parts/Contains partial HTML that is included by pages
/control/Contains the page logic.
For larger applications you may want additional sub-directories for the individual parts (e.g. /admin/, /pub/) of your application to make the root directory a little lighter. Each of them would have their own control sub-directory.
Provide a base.inc.php file that includes (require_once) in the right order:

  • frequently used stuff (database layer) from /lib/common
  • the config include file
  • all classes from /lib/model
  • all classes from /lib/dao

Of course you will have additional directories for your images, uploaded files, ... etc.
php.ini quirks
Some settings in the php.ini control how PHP interpretes your scripts. This can lead to unexpected behaviour when moving your application from development to the productive environment. The following measures reduce dependency of your code on php.ini settings.
short_open_tag
Always use the long PHP tags: php
echo "hello world"; ?>
Do not use the echo shortcut .
asp_tags
Do not use ASP like tags: <% echo "hello world"; %>
gpc_magic_quotes
I recommend that you include code in a global include file which is run before any $_GET or $_POST parameter or $_COOKIE is read. That code should check if the gpc_magic_quotes option is enabled and run all $_GET, $_POST and $_COOKIE values through the stripslashes function.
register_globals
Never rely on this option beeing set. Always access all GET, POST and COOKIE values through the 'superglobal' $_GET, $_POST and $_COOKIE variables. For convenience declare $PHP_SELF = $_SERVER['PHP_SELF']; in your global include file after the gpc_magic_quotes quirk.
File uploads:
The maximum size of an uploaded file is determined by the following parameters:

  • file_uploads must be 1 (default)
  • memory_limit must be slightly larger than the post_max_size and upload_max_filesize
  • post_max_size must be large enough
  • upload_max_filesize must be large enough
Have one single configuration file
You should define all configuration parameters of your application in a single (include) file. This way you can easily exchange this file to reflect settings for your local development site, a test site and the customer's production environment. Common configuration parameters are:

  • database connection parameters
  • email addresses
  • options
  • debug and logging output switches
  • application constants
Keep an eye on the namespace
As PHP does not have a namespace facility like Java packages, you must be very careful when choosing names for your classes and functions.

  • Avoid functions outside classes whenever possible and feasible. Classes provide some extra namespace for the methods and variables that live inside them.
  • If you declare global functions use a prefix. Some examples are dao_factory(),db_getConnection(), text_parseDate() etc.
Use a database abstraction layer
In PHP there are no database-independent functions for database access apart from ODBC (which nobody uses on Linux). You should not use the PHP database functions directly because this makes it expensive when the database product changes. Your customer may move from MySQL to Oracle one day or you will need an XML database maybe. You never know. Moreover an abstraction layer can ease development as the PHP database functions are not very userfriendly.
Use Value Objects (VO)
VOs are actually a J2EE pattern. It can easily be implemented in PHP. A value object corresponds directly to a C struct. It's a class that contains only member variables and no methods other than convenience methods (usually none). A VO corresponds to a business object. A VO typically corresponds directly to a database table. Naming the VO member variables equal to the database fields is a good idea. Do not forget the ID column.
class Person {
var $id, $first_name, $last_name, $email;
}
Use Data Access Objects (DAO)
DAO is actually a J2EE pattern. It can easily be implemented in PHP and helps greatly in separating database access from the rest of your code. The DAOs form a thin layer. The DAO layer can be 'stacked' which helps for instance if you want to add DB caching later when tuning your application. You should have one DAO class for every VO class. Naming conventions are a good practice.
class PersonDAO {
var $conn;
function PersonDAO(&$conn) {
$this->conn =& $conn;
}
function save(&$vo) {
if ($v->id == 0) {
$this->insert($vo);
} else {
$this->update($vo);
}
}
function get($id) {
#execute select statement
#create new vo and call getFromResult
#return vo
}
function delete(&$vo) {
#execute delete statement
#set id on vo to 0
}
#-- private functions
function getFromResult(&vo, $result) {
#fill vo from the database result set
}
function update(&$vo) {
#execute update statement here
}
function insert(&$vo) {
#generate id (from Oracle sequence or automatically)
#insert record into db
#set id on vo
}
}
A DAO typically implements the following methods:

  • save: inserts or updates a record
  • get: fetches a record
  • delete: removes a record
The DAO may define additional methods as required by your application's needs. The should only perform actions that require the database (maybe only for performance reasons) and can not be implemented in a different mannor. Examples: isUsed(), getTop($n), find($criteria).
The DAO should only implement basic select / insert / update operations on onetable. It must not contain the business logic. For example the PersonDAO should not contain code to send email to a person. For n-to-n relationships create a separate DAO (and even a VO if the relationships has additional properties) for the relation table.
Write a factory function that returns the proper DAO given the class name of a VO.Caching is a good idea here.
function dao_getDAO($vo_class) {
$conn = db_conn('default'); #get a connection from the pool
switch ($vo_class) {
case "person": return new PersonDAO($conn);
case "newsletter": return new NewsletterDAO($conn);
...
}
}
Generate code
99% of the code for your VOs and DAOs can be generated automatically from your database schema when you use some naming conventions for your tables and columns. Having a generator script ready saves you time when you are likely to change the database schema during development. I successfully used a perl script to generate my VOs and DAOs for a project. Unfortunately I am not allowed to post it here.
Business logic
Business logic directly reflects the use cases. The business logic deals with VOs, modifies them according to the business requirements and uses DAOs to access the persistence layer. The business logic classes should provide means to retrieve information about errors that occurred.
class NewsletterLogic {
function NewsletterLogic() {
}
function subscribePerson(&$person) {
...
}
function unsubscribePerson(&$person) {
...
}
function sendNewsletter(&$newsletter) {
...
}
}
Page logic (Controller)
When a page is called, the page controller is run before any output is made. The controller's job is to transform the HTTP request into business objects, then call the approriate logic and prepare the objects used to display the response.
The page logic performs the following steps:
1. The cmd request parameter is evaluated. 
2. Based on the action other request parameters are evaluated.
3. Value Objects (or a form object for more complex tasks) are created from the parameters.
4. The objects are validated and the result is stored in an error array.
5. The business logic is called with the Value Objects.
6. Return status (error codes) from the business logic is evaluated.
7. A redirect to another page is executed if necessary.
8. All data needed to display the page is collected and made available to the page as variables of the controller. Do not use global variables.
Note: it is a good idea to have a utility function that returns a parameter that is sent via GET or POST respectivly and provide a default value if the parameter is missing. The page logic is the only non-HTML include file in the actual page! The page logic file must include all other include files used by the logic (see base.inc.php below). Use the require_once PHP command to include non-HTML files.
class PageController {
var $person; #$person is used by the HTML page
var $errs;
function PageController() {
$action = Form::getParameter('cmd');
$this->person = new Person();
$this->errs = array();
if ($action == 'save') {
$this->parseForm();
if (!this->validate()) return;
NewsletterLogic::subscribe($this->person);
header('Location: confirmation.php');
exit;
}
}
function parseForm() {
$this->person->name = Form::getParameter('name');
$this->person->birthdate = Util::parseDate(Form::getParameter('birthdate');
...
}
function validate() {
if ($this->person->name == '') $this->errs['name'] = FORM_MISSING;
#FORM_MISSING is a constant
...
return (sizeof($this->errs) == 0);
}
}
Presentation Layer
The top level page will contain the actual HTML code. You may include HTML parts that you reuse across pages like the navigation etc. The page expects the page logic to prepare all business objects that it needs. It's a good idea to document the business objects needed at the top of the page.
The page accesses properties of those business objects and formats them into HTML.
<?php require_once('control/ctl_person.inc.php'); #the page controller
$c =& new PageController();
?>
<html>
<body>

php
echo htmlspecialchars($PHP_SELF) ?>" method="POST">

value="php echo htmlspecialchars($c->person->name); ?>">




Localization
Localization is a problem. You must choose among 
a) duplicating pages 
b) removing all hardcoded strings from your HTML.
As I work in a design company I usually take approach a). Approach b) is not feasible as it makes the HTML very hard to read and nearly impossible to edit in a visual web editor like Dreamweaver. Dynamic content is hard enough to edit with Dreamweaver. Removing also all strings, makes the page look quite empty...
So finish the project in one language first. The copy the HTML pages that need translation. Use a naming convention like index_fr.php to designate the French version of the index page. Always use the ISO two letter language codes. Do not invent your own language codes.
To keep track of the language the user selected you must choose among 
a) storing the language setting in a session variable or cookie
b) reading the preferred language (locale) from the HTTP headers the browser sends you
c) appending the language to the URL of every link in your application
While a) seems a lot more easier than c) it may be subject to session timeout. Option b) should only be implemented as an extension to a) or c). 
Strings in a database must be localized too!
Making your application location independent
PHP has problems in some situations when include files are nested and reside in different folders and it is unclear at which directory level the file will be included. One can solve this by using absolute path names or using $_SERVER['DOCUMENT_ROOT']as a starting point. However this makes your code location dependent - it will not run anymore if you move it down your directory structure one level. Of cource we do not like that.
I have found a convenient solution to this problem. The toplevel page (the one that is called by the browser) needs to know the relative path to the application root directory. Unfortunately there is no such function in PHP and the webapp context concept is completely absent in PHP. So we can not automatically determine the application root reliably in all situations (It is *really* impossible. Don't even try. It's not worth the effort.)
Let's define a global variable called $ROOT in an include file in every directory that contains toplevel pages. The include file (call it root.inc.php) must be included by the page logic before any other include files. Now you can use the $ROOT variable to reference include files with their exact path!
Sample:
We have toplevel pages in /admin/pages/. The $ROOT variable must therefore be set to$ROOT = '../..';. The page logic included by pages in that folder would reference their include files like require_once("$ROOT/lib/base.inc.php");.
In my suggested folder outline (see below) we don't even need that, since all toplevel pages reside in the webapp root directory anyway. So the webapp root directory is always the current directory.
Folder outline
I suggest you make one file per class and follow a naming convention. Make sure that all your include files end with .php to avoid disclosure of your code to malicious users, which is a major security problem. I suggest the following folder structure:
/Webapp root directory. Contains the pages that are actually called by the browser.
/lib/Contains base.inc.php and config.inc.php
/lib/common/Contains libraries and tools reusable for other projects, like your database abstraction classes.
/lib/model/Contains the Value Object classes
/lib/dao/Contains the DAO classes and the DAO factory
/lib/logic/Contains the business logic classes
/parts/Contains partial HTML that is included by pages
/control/Contains the page logic.
For larger applications you may want additional sub-directories for the individual parts (e.g. /admin/, /pub/) of your application to make the root directory a little lighter. Each of them would have their own control sub-directory.
Provide a base.inc.php file that includes (require_once) in the right order:

  • frequently used stuff (database layer) from /lib/common
  • the config include file
  • all classes from /lib/model
  • all classes from /lib/dao

Of course you will have additional directories for your images, uploaded files, ... etc.
Original Article: http://www.odi.ch/prog/design/php/guide.php



Original article:This guide will give you solutions to common PHP design problems. It also provides a sketch of an application layout that I developed during the implementation of some projects.

Jul 2, 2012

Create Thumbnail

To generate thumbnail, here is the  simple and easiest way. It will help you in creating thumb for jpg, png and gif images.


/* generateThumb()
 *
 * @param mixed $source Path to source file
 * @param mixed $destination Path to destination file
 * @param mixed $width Thumbnail file width
 * @param mixed $height Thumbnail file height
 * @return bool
 */
function generateThumb($source, $destination, $width = 100, $height = 100){ $ext = strtolower(substr($source, strrpos($source, ".")+1)); $format = ($ext == 'jpg')?'jpeg':$ext; $from_format = "imagecreatefrom".$format; $source_image = $from_format ( $source ); $source_width = imagesx ( $source_image ); $source_height = imagesy ( $source_image ); $ratio1 = $source_width/ $width; $ratio2 = $source_height / $height; if($ratio1 > $ratio2){ $width = $width; $height = $source_height/$ratio1; }else{ $width = $source_width/$ratio2; $height = $height; } $target_image = imagecreatetruecolor($width,$height); imagecopyresampled($target_image, $source_image, 0, 0, 0, 0, $width, $height, $source_width, $source_height); $image_function = "image" . $format; return $image_function ( $target_image, $destination, 100 ); }
Here is how you use it...


if(generateThumb("images/a.jpg", "images/thumbs/a.jpg", 150, 100)){ echo "done"; } else { echo "failed"; }

Jun 20, 2012

Compare 2 mysql databases

Below code will list all the tables with fields (& type) and total records in each table, so that we can verify whether fields are same in both database fields and record counts.
<?php
mysql_connect("localhost", "user1", "pwd1") or die(mysql_error());
mysql_select_db("db1") or die(mysql_error());

$tables = array('tbl1', 'tbl2','tbl3');

echo "<table  border='1' style='float: left'>";
foreach($tables as $tbl){
  $sel = "SELECT COUNT(1) FROM $tbl";
  $res = mysql_query($sel);
  $rec = mysql_fetch_row($res);
 
  echo "<tr><th colspan='2'>$tbl ($rec[0])</th></tr>";
  $sel1 = "SHOW FIELDS FROM $tbl";
  $res1 = mysql_query($sel1) or die($sel1.mysql_error());
  while($rec1 = mysql_fetch_row($res1)){
    echo "<tr><td>$rec1[0]</td><td>$rec1[1]</td></tr>";
  }
}
echo "</table>";
mysql_close();
mysql_connect("localhost", "user2", "pwd2") or die(mysql_error());
mysql_select_db("db2") or die(mysql_error());
echo "<table  border='1' style='float: left'>";
foreach($tables as $tbl){
  $sel = "SELECT COUNT(1) FROM $tbl";
  $res = mysql_query($sel);
  $rec = mysql_fetch_row($res);
 
  echo "<tr><th colspan='2'>$tbl ($rec[0])</th></tr>";
  $sel1 = "SHOW FIELDS FROM $tbl";
  $res1 = mysql_query($sel1) or die($sel1.mysql_error());
  while($rec1 = mysql_fetch_row($res1)){
    echo "<tr><td>$rec1[0]</td><td>$rec1[1]</td></tr>";
  }
}
echo "</table>";
?>

Jun 12, 2012

HTTP POST without cURL using PHP



I don't think we do a very good job of evangelizing some of the nice things that the PHP streams layer does in the PHP manual, or even in general. At least, every time I search for the code snippet that allows you to do an HTTP POST request, I don't find it in the manual and resort to reading the source. (You can find it if you search for "HTTP wrapper" in the online documentation, but that's not really what you think you're searching for when you're looking). 

So, here's an example of how to send a POST request with straight up PHP, no cURL:

<?php
     function do_post_request($url, $data, $optional_headers = null) {
          $params = array('http' => array(
                                'method' => 'POST',
                               'content' => $data
                         ));
         if ($optional_headers !== null) {
              $params['http']['header'] = $optional_headers;
          }
          $ctx = stream_context_create($params);
          $fp = @fopen($url, 'rb', false, $ctx);
          if (!$fp) {
                  throw new Exception("Problem with $url, $php_errormsg");
          }
         $response = @stream_get_contents($fp);
         if ($response === false) {
           throw new Exception("Problem reading data from $url, $php_errormsg");
          }
         return $response;
}
$optional_headers is a string containing additional HTTP headers that you would like to send in your request.
PHP's HTTP wrapper will automatically fill out the Content-Length header based on the length of the $data that you pass in. It will also automatically set the Content-Type to application/x-www-form-urlencoded if you don't specify one in the $optional_headers.
I find this very handy; I don't need to code in redirection logic, HTTP auth handling, user agent setting and so on; they are handled for me by PHP. This works for HTTPS as well, if you have openssl enabled.
You may also want to look into http_build_query() which is a convenience function that allows you to assemble query/post parameters from a PHP variable, applying appropriate escaping. You can find an example of this in the REST helper below.
Kudos to Sara Golemon for both http_build_query and exposing the HTTP context parameters up to userspace.

A Generic REST helper
Many web services offer a REST-ful interface for consuming their data, using GET requests for information retrieval and POST requests for making changes. Below you'll find a helper function that can very easily be used to consume a REST API.

The $url parameter is the HTTP or HTTPS URL for the web service. $params is an associative array of form parameters to pass to the web service; they will be passed as _GET parameters for GET requests or _POST parameters for POST requests. The $verb parameter can be GET or POST (and presumably any other valid HTTP REQUEST verb, such as PUT or DELETE, although I haven't tried those and can't say whether they will work as expected). The $format parameter can be "json" or "xml" and will automatically return a decoded json or XML document, respectively.

I've used simplexml here because it is... simple. You could very easily add a "dom" format to return the object using the richer and more complex DOM API instead.

This function uses the ignore_errors context parameter. Without this set (the default is false), PHP will treat 400 and 500 HTTP status codes as a failure to open the stream and won't return you any data. This is usually what you want when using fopen or file_get_contents, but REST services tend to set the HTTP status to indicate the error and will usually send back a payload that describes the error. We turn on ignore_errors so that we treat any returned payload as json or xml.

When using POST with REST, take care: PHP's HTTP redirection handler will drop your POST payload if the endpoint issues a redirect. If you experience problems using POST with the function below, it might be due to redirects. Most of the POST calls I've run into issue redirects if the URL is missing a trailing '/' character. In other words, if you experience problems where it seems like your parameters are not being sent in, try appending a '/' to the end of the URL and try it again.

<?php
 function rest_helper($url, $params = null, $verb = 'GET', $format = 'json') {
       $cparams = array( 'http' => array( 'method' => $verb,
                                  'ignore_errors' => true ) );
        if ($params !== null) {
              $params = http_build_query($params);
               if ($verb == 'POST') {
                     $cparams['http']['content'] = $params;
                 } else {
                        $url .= '?' . $params;
                }
          }
          $context = stream_context_create($cparams);
          $fp = fopen($url, 'rb', false, $context);
           if (!$fp) {
                 $res = false;
            } else {
               // If you're trying to troubleshoot problems, try uncommenting the
              // next two lines; it will show you the HTTP response headers across
              // all the redirects:
              // $meta = stream_get_meta_data($fp);
              // var_dump($meta['wrapper_data']);
               $res = stream_get_contents($fp);
            }
            if ($res === false) {
                  throw new Exception("$verb $url failed: $php_errormsg");
             }
             
              switch ($format) {
                  case 'json':
                            $r = json_decode($res);
                            if ($r === null) {
                                 throw new Exception("failed to decode $res as json");
                             }
                                     return $r;
                  case 'xml':
                             $r = simplexml_load_string($res);
                             if ($r === null) {
                                 throw new Exception("failed to decode $res as xml");
                             }
                             return $r;
               }
               return $res;
      }
      // This lists projects by Ed Finkler on GitHub:

    foreach ( rest_helper('http://github.com/api/v2/json/repos/show/funkatron') ->repositories as $repo) {
                echo $repo->name, "\n";
                echo htmlentities($repo->description), "\n";
                echo "\n";
       } // This incomplete snippet demonstrates using POST with the Disqus API

       var_dump(
             rest_helper( "http://disqus.com/api/thread_by_identifier/",
                        array(
                                  'api_version' => '1.1',
                                  'user_api_key' => $my_disqus_api_key,
                                  'identifier' => $thread_unique_id,
                                  'forum_api_key' => $forum_api_key,
                                  'title' => 'HTTP POST from PHP, without cURL', ),
                       'POST' ) );
You can find more documentation on the HTTP wrapper options in the HTTP and HTTPS page in the PHP manual, more on the GitHub API at github.com, more on the Disqus API and more on Ed Finkler at his blog.

Original Post

Jun 7, 2012

Apr 16, 2012

4 Most Important PHP Security Measures

We can say that PHP is a mature language with lot’s of useful, but potentially dangerous features. The rapid growth of the language and the dynamic nature of the Web let people easily create dynamic web pages without any prior knowledge in computer science or the architecture of the Internet.

In this tutorial we’ll have a look at 4 important PHP security measures that you should implement in order to develop a safer website.

1. Register Globals

Up until PHP version 4.2.0 the register_globals directive’s default value was On. One of the most controversial change in following versions was that the PHP core developers changed this default value to Off, not because the directive itself was insecure, but the common misuse of it was.
Note: This feature will be removed starting with PHP 6.0.0
When this directive is On, PHP will inject extra variables in the script such as HTML request variables, etc. The problem with this approach is that a developer cannot rely anything outside of his script and by injecting these variables an outside attacker could overwrite already defined variables or create potentially dangerous ones. For example:
PHP could inject these sort of variables in a script
$username = 'hacked_username';
Now if a $username variable was already set this would overwrite it. Another good example can be found on php.net
if ($authorized) {
 //show members only page
}
An attacker could alter the value of this variable simply by using GET auth.php?authorized=1 if the above code snippet is found in auth.php file
The best practice, that every developer should follow, is setting register_globals directive to Off and use the already defined PHP superglobals such as $_GET, $_POST.
register_globals directive is found in the php.ini file.

2. Error Reporting

When developing a complex website or web application enabling errors display is essential. A developer cannot fix the committed errors if he can’t see them, but once the website is in production the errors display should be disabled, because PHP errors provides detailed information to the outside attacker.
A good approach is to enable error display in development environment:
error_reporting(E_ALL);
ini_set('display_errors','On');
And once in production environment disable error display, but enable error logging to a file:
error_reporting(E_ALL);
ini_set('display_errors','Off');
ini_set('log_errors', 'On');
ini_set('error_log', '/path/to/error/log');
Alternatively you can use error_reporting(E_ALL | E_STRICT), this is the highest setting, offering suggestion for forward compatibility too.

3. Cross-Site Scripting (XSS)

Cross-site scripting vulnerability is the most common form of attack on websites. The mistake made by developers is not filtering input data from web forms and not escaping the output.
For example we have the following comment form
<form action="process.php" method="post" accept-charset="utf-8" enctype="multipart/form-data" name="frmComment">
 
  <textarea name="txtMessage" id="txtMessage"></textarea>  
 <input type="submit" name="submit" value="Send" id="submit" />
 
</form>
The application displays the following data like:
echo $_POST['txtMessage'];
The vulnerability is that the application doesn’t filter the input and escape the output. Let’s say someone writes the following javascript in the comment textarea:
alert ('hacked');
If an application doesn’t escape this output on every page request a Javascript alert box will pop up. The best a developer can do is to filter out any HTML tags from the data with:
$clean_message = strip_tags($_POST['txtComment']);
And escape it when outputting the date with htmlentities:
htmlentities($clean_message, ENT_QUOTES, 'UTF-8');
A better solution is to use HTML Purifier to filter out any unwanted malicious input and to test your web forms that it’s XSS proof use the XSS cheat sheet.

4. Exposing Sensitive Information

Many web developers store sensitive information in files such as database passwords and other credentials. If these files are not properly secured an attacker could see the contents of them, therefore hacking the applications database, etc.
The most common file extension for php include files is .inc. By using this extension and not properly creating parsing rules in Apache, a developer could create a major security hole in the web application.
In Apache configuration the default file type for unknown file extensions is text/plain. If the .inc file is not set to be parsed as a PHP file and it is in the document root then we can access this file and see the contents of it by visiting the corresponding URL.
The best solution to this problem is to store these files outside of your document root (e.g. /www, /public_html, etc.). A best practice is to place the most essential files in your document root.
If you don’t have access outside your document root then at least use the following 2 methods:
  1. Use an extra .php extension on the end of your file. E.g. sensitive.inc.php
  2. Secure the .inc file in a .htaccess file:
    <Files ~ ".inc$">
        Order allow,deny
        Deny from all
    </Files>
    

    Summary

    • Set register_globals directive to Off
    • Disable error display in production environment
    • Avoid XSS attacks, filter your input and escape your output
    • Move all your sensitive information outside of your document root, if that’s not possible add an extra .php extension to your .inc files and/or secure them in a .htaccess file