The PQP profiler from Particletree is a very handy thing to have in your development toolbox. However, it doesn’t deal with the ever-increasing amount of work done via Ajax requests. Or, at least, it didn’t.
Back in the days when FireBug actually worked reliably, FirePHP was a very handy plugin that enabled you to view trace statements and debug information generated by PHP scripts requested by an XMLHttpRequest object. It occurred to me that I could use the same method of piggybacking JSON encoded messages in the HTTP headers, parse them with JavaScript and dynamically update the PQP Console. This way also means that a native cross-browser solution would be available.
The Prototype library has great Ajax support. Two features in particular allowed me to implement this new functionality extremely quickly: it adds a header that identifies requests as being of type XMLHttpRequest (as does jQuery) and has a global responders object (which jQuery lacks) that means any Ajax request created can automatically have the required callbacks registered. In short, you won’t need to change your code – as long as you’re using Prototype as well that is, although I’m sure one of you clever jQuery types can knock something up fairly quickly!
I made some fairly minimal changes to the PQP classes to add the new functionality (and to satisfy my obsession with coding standards). I also tidied up the JavaScript to ensure that the Prototype library would be available and take advantage of it for tab switching and DOM manipulation.
I’ll polish it a little, integrate it with my ongoing PDO work and document it properly later on but for now you can go and have a look at an example over at my playground.
A few years ago, while PHP 5 was still in a state of flux, a change was made to the way that PDO handles parameters bound to prepared statements. Somewhere between versions 5.2.0 and 5.2.1 a change was made that gave rise to much annoyance and debate in bug 40417. Long story short, it used to be acceptable to reuse a placeholder in statement several times and bind a single variable to all of the instances thusly:
< ?php
// Connect to the database with defined constants
$dbh = safePDO_Factory::getInstance(PDO_DSN, PDO_USER, PDO_PASSWORD);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
// Construct SQL query
$query = '
SELECT
*
FROM
tblnews
WHERE
title ILIKE :search
OR content ILIKE :search
';
// Prepare the statement
$stmt = $dbh->prepare($query);
// Bind the search string variable to the statement
$smt->bindParam(':search', $search, PDO::PARAM_STR);
// Execute the query
$stmt->execute();
// Check to see if we have any results
if ($stmt->rowCount() > 0) {
// Process the results here . . .
} else {
echo 'No search results were returned.';
}
} catch (Exception $e) {
echo $e->getMessage();
)
// Destroy the database connection
$dbh = null;
?>
But all of a sudden the above rudimentary news searching code would cease to work if you upgraded to PHP 5.2.1 and there was no notice in the PHP change log at the time, which obviously led to much confusion. The issue was that it was no longer acceptable to bind a single variable to multiple placeholders – each placeholder required a unique name and explicit variable binding.
In much the same way that I subclassed the PDO connection class, the individual PDO Statement class can be extended to restore this multiple placeholder / single variable behaviour.
Continue reading ‘Restoring PDO functionality’
After previously explaining how to harden PDO I’m going to expand on the basic class I developed with the help of some design patterns.
A large part of using design patterns lies in recognising the situations in which each one should be used. The temptation is to implement the safePDO database class as a singleton so that only one instance of the object is used and extra connections are not made. An issue arises immediately as the constructor must be defined as public – the same as the parent PDO class – and therefore it will be able to be instantiated from anywhere. Besides, occasionally, you may also need to connect to another database which renders the singleton pattern useless.
A better choice for this situation, in my opinion, is the flyweight pattern. This is ordinarily used where a large number of similar items are to be used but can easily be applied in this situation so that database connection requests that have the same credentials can be reused.
In the following code a singleton factory pattern is used to handle and distribute requests for database connections, using the dsn and username as an array key for a pool of safePDO instances. You can rest assured that the database connection is wrapped in a try/catch block. As an added bonus, as all safePDO construction should be made from the factory class, we can make a check for this using a debug backtrace and therefore prevent a safePDO object from being instantiated at random.
If PHP code isn’t your thing – and assuming you’ve not already stopped reading by now – then you may want to give the code after the jump a miss. If your curiosity is piqued, read on . . .
Continue reading ‘I am the one and only’
If your application does not catch the exception thrown from the PDO constructor, the default action taken by the zend engine is to terminate the script and display a back trace. This back trace will likely reveal the full database connection details, including the username and password.
- PDO Connections and Connection management
Ideally you shouldn’t be displaying any error messages to the outside world at all – especially on a production server. Even during development you should probably limit debugging output to approved IP addresses only.
You can take a very simple step to avoid displaying your username and password should the connection fail and you (or somebody else) forget to catch the error. By subclassing the PDO database abstraction layer you can make sure that you implicitly catch the exception message by temporarily changing the PHP exception handler.
You could generate your own backtrace here as well and sanitize the data to remove any passwords before display but I shall leave that as an exercise for the reader.
All you need to do now is remember to use the SafePDO class instead of instantiating a standard PDO object.
< ?php
Class SafePDO extends PDO {
public static function exception_handler($exception) {
// Output the exception details
die('Uncaught exception: ', $exception->getMessage());
}
public function __construct($dsn, $username='', $password='', $driver_options=array()) {
// Temporarily change the PHP exception handler while we . . .
set_exception_handler(array(__CLASS__, 'exception_handler'));
// . . . create a PDO object
parent::__construct($dsn, $username, $password, $driver_options);
// Change the exception handler back to whatever it was before
restore_exception_handler();
}
}
try {
// Connect to the database with defined constants
$dbh = new SafePDO(PDO_DSN, PDO_USER, PDO_PASSWORD);
// The rest of your code goes here . . .
// Destroy the database connection
$dbh = null;
} catch (Exception $e) {
SafePDO::exception_handler($e);
}
?>
The phpinfo() command comes in very useful when you want to check details about your PHP install and server setup. However, there is a lot of output and you won’t need it all. Not everyone knows that you can pass parameters to the function to narrow the scope of the information displayed.
I came up with this simple method to view the results of using these parameters and make it easy to switch between them.
< ?php
$options = array('CONFIGURATION', 'ENVIRONMENT', 'MODULES', 'VARIABLES', 'GENERAL', 'CREDITS', 'LICENSE', 'ALL');
$display = (empty($_GET['display']) || !in_array(strtoupper($_GET['display']), $options)) ? 'ALL' : strtoupper($_GET['display']);
$navigation = array();
foreach($options as $key=>$value) {
$navigation[] = ($value != $display) ? '<a href="' . $_SERVER['SCRIPT_NAME'] . '?display=' . $value . '">' . $value . '</a>' : '<strong>' . $value . '</strong>';
}
ob_start();
switch($display) {
case 'CONFIGURATION':
phpinfo(INFO_CONFIGURATION);
break;
case 'ENVIRONMENT':
phpinfo(INFO_ENVIRONMENT);
break;
case 'MODULES':
phpinfo(INFO_MODULES);
break;
case 'VARIABLES':
phpinfo(INFO_VARIABLES);
break;
case 'GENERAL':
phpinfo(INFO_GENERAL);
break;
case 'CREDITS':
phpinfo(INFO_CREDITS);
break;
case 'LICENSE':
phpinfo(INFO_LICENSE);
break;
case 'ALL': default:
phpinfo();
break;
}
$content = ob_get_clean();
if (('CREDITS' === $display) & preg_match('/<h1><a href="([^"]+?)">PHP Credits< \/a>< \/h1>/', $content, $matches) & !empty($matches)) {
$content = file_get_contents('http://' . $_SERVER['HTTP_HOST'] . $matches[1]);
}
echo str_replace('<body>', '</body><body><div class="center options"><p>' . implode(' | ', $navigation) . '</p></div>', $content);
?>
It’s also worth pointing out that the information exposed by this function can be useful to people who want to locate any weaknesses in your system so I would recommend restricting access to this file with a password, by an IP address range or only uploading the file to your webserver temporarily and removing it as soon as you’re finished with it.