You want to do WHAT with PHP? Chapter 9

e_schrade writes; There is a bunch I could say to introduce this chapter.  However, I think that by reading the first few paragraphs you will know what I’m talking about.  For those who are experienced developers some of these items might seem a little basic, but there are reams and reams of PHP developers who do not follow several of these rules.

In other news, “You want to do WHAT with PHP?” is now available for purchase in the Amazon store.

Chapter 1: Networking and Sockets
Chapter 2: Binary Protocols
Chapter 3: Character Encoding
Chapter 4: Streams
Chapter 5: SPL
Chapter 6: Asynchronous Operations with Some Encryption Thrown In
Chapter 7: Structured File Access
Chapter 8: Daemons
Chapter 9: Debugging, Profiling, and Good Development
Chapter 10: Preparing for Success

via Read part of chapter 9.

Zend_Log with multiple writers

eschrader writes; So I was sitting here thinking to myself “This is Friday and I’m not getting much of anything done.  Maybe I should write another Friday Framework Highlight.”  I figured that it was a good idea so I pondered what I should write.  I came up blank and so I asked Matthew Weier O’Phinney.  “Multiple writers for Zend_Log,” he said.  I agreed.

If you were not aware, Zend_Log provides facilities for writing to multiple logs through the same log instance.  Additionally, you can do this via configuration options when using a Zend_Applicatin resource plugin.  Together those make for very powerful logging mechanisms.  “How?” you ask?  It’s really easy.  Take your application.ini file, which you use to configure your Zend_Application instance, and make it look something like this.  I’ll highlight the pertinent parts

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0
resources.log.stream.writerName = "Stream"
resources.log.stream.writerParams.stream = APPLICATION_PATH "/logs/application.log"
resources.log.stream.writerParams.mode = "a"
resources.log.stream.filterName = "Priority"
resources.log.stream.filterParams.priority = 4

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.log.stream.filterParams.priority = 7
resources.frontController.params.displayExceptions = 1
resources.log.firebug.writerName = "FireBug"
resources.log.firebug.filterName = "Priority"
resources.log.firebug.filterParams.priority = 7

What this does is say that “in production, log warnings and above to the log file, but in development, log debug to the log file AND send the log items to FirePHP.”  Then, in our index controller we put this:

class IndexController extends Zend_Controller_Action
{

public function indexAction()
{
$this->getInvokeArg('bootstrap')->log->debug("I'm at indexAction");
}

}

When we execute this code we get both the output in the application log

$ tail -f application.log
2010-09-10T16:27:25-05:00 DEBUG (7): I'm at indexAction

and in the Firebug log

X-Wf-Protocol-1            http://meta.wildfirehq.org/Protocol/JsonStream/0.2
X-Wf-1-Structure-1    http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1
X-Wf-1-Plugin-1            http://meta.firephp.org/Wildfire/Plugin/ZendFramework/FirePHP/1.6.2
X-Wf-1-1-1-1            122|[{"Type":"LOG","File":"C:\\workspace\\Test\\application\\controllers\\IndexController.php","Line":8},"I'm at indexAction"]|

In production we wouldn’t get anything since this code would filter out debug logging, due to the resources.log.stream.filterParams.priority setting in the production section in application.ini.  Simple.  Done.

via e_schrade.

Finding syntax errors in your PHP Project files

Till posted this little snippet;
It’s so useful I just had to share it 🙂

find . \( -name "*.php" -o -name "*.phtml" \) -exec php -l {} \;

Just go to your project directory and fire it off, it will help you find those pesky unmatched {}

Search each class for function names that match except for the underscore prefix

Bill Karwin posts a useful little snippet that will list and search each class for function names that match except for the underscore prefix, private / protected functions.

< ?php /** * Find methods that differ only by the underscore prefix. * by Bill Karwin August 2010 * * I release this code under the terms of the New BSD License: * http://framework.zend.com/license/new-bsd */ // Change this to suit your environment define("LIBRARY_DIR", "/Users/bill/Library/PHP/ZF/library"); // Pre-load some files to satisfy the autoloader. // We could also add the local PEAR library dir to the autoloader. require_once("PHPUnit/Framework/SelfDescribing.php"); require_once("PHPUnit/Framework/AssertionFailedError.php"); require_once("PHPUnit/Framework/Assert.php"); require_once("PHPUnit/Framework/Test.php"); require_once("PHPUnit/Extensions/Database/DataSet/ITable.php"); require_once("PHPUnit/Extensions/Database/DataSet/AbstractTable.php"); require_once("PHPUnit/Extensions/Database/DataSet/IDataSet.php"); require_once("PHPUnit/Extensions/Database/DataSet/AbstractDataSet.php"); require_once("PHPUnit/Extensions/Database/ITester.php"); require_once("PHPUnit/Extensions/Database/AbstractTester.php"); require_once(LIBRARY_DIR . "/Zend/Loader/Autoloader.php"); Zend_Loader_Autoloader::getInstance(); // Find every PHP file under the library dir and slurp them in. // Yes that's a lot of files. Deal with it. $Directory = new RecursiveDirectoryIterator(LIBRARY_DIR); $Iterator = new RecursiveIteratorIterator($Directory); $Regex = new RegexIterator($Iterator, '/^.+\.php$/i'); foreach ($Regex as $filename) { require_once($filename); } // Loop over each class now in the PHP runtime. // Filter by classes named Zend*. $classes = get_declared_classes(); $zendclasses = new RegexIterator(new ArrayIterator($classes), '/ ^Zend/'); foreach ($zendclasses as $classname) { // Search each class for function names that match except for the underscore prefix // Note this includes duplicates and magic methods, so you have to do some sorting // on the output. Hint: `sort -u`. $class = new ReflectionClass($classname); $methods = $class->getMethods();
foreach ($methods as $method) {
if (preg_match("/^__*(.*)/", $method->name, $matches)) {
$underscore = $method;
if ($class->hasMethod($matches[1])) {
$nonunderscore = $class->getMethod($matches[1]);
echo $underscore->getDeclaringClass()->name
. "::" . $underscore->name . "()" . " => ";
if ($underscore->getDeclaringClass() != $nonunderscore->getDeclaringClass()) {
echo $nonunderscore->getDeclaringClass()->name;
}
echo "::" . $nonunderscore->name . "()" . "\n";
}
}
}

}

Adding Zend_Cache to Flex/Flash Builder 4 Projects

I have som rather large and time consuming queries running in the Statistics screen of an NOC (Network Operations Center) Flex/Flash Builder 4 application i’we been tinkering with, to prevent the database server to be boggen down by multiple queries fired by this app in multiple places I had to implement caching.

And to do this is alot easier than it might sound like especially for the (PHP) Zend_AMF based services.

Once you have setup your Data Centric client/server  connection like it’s described in this Article @ DevZone you begin by editing the gateway.php;

// Store configuration in the registry
Zend_Registry::set("amf-config", $amf);

// Configure Zend_Cache
$frontendOptions = array(
'lifetime' => 12*3600,
'automatic_serialization' => true,
'default_options' => array(
'cache_with_get_variables' => true,
'cache_with_post_variables' => true,
'cache_with_session_variables' => true,
'cache_with_files_variables' => true,
'cache_with_cookie_variables' => true,
'make_id_with_get_variables' => true,
'make_id_with_post_variables' => true,
'make_id_with_session_variables' => true,
'make_id_with_files_variables' => true,
'make_id_with_cookie_variables' => true
)
);
$backendOptions = array(
'cache_dir' => '/tmp/'
);

$cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);

// Store cache configuration in the registry
Zend_Registry::set("cache", $cache);

Then modify your service class to look similar to this;

public function getCustomerStatsByMonth() {
// Get the Cache from Registry
$cache = Zend_Registry::get("cache");
$id = 'getCustomerStatsByMonth';
if(!($rows = $cache->load($id)))
{
// We didnt find anything in the cache so lets get it from DB
$stmt = mysqli_prepare($this->connection, "SELECT
customers.name,
SUBSTR(FROM_UNIXTIME(`cdrs`.`start`),1,7) AS `month`,
outgroups.name_invoices,
ROUND(SUM(`cdrs`.`talktime`)/60) AS `minutes`,
COUNT(`cdrs`.`start`) AS `calls`
FROM
es.outgroups
INNER JOIN es.cdrs
ON (outgroups.id = cdrs.outgroup)
INNER JOIN es.customers
ON (customers.id = cdrs.scustomer)
WHERE (outgroups.name_invoices LIKE 'Sweden%' AND cdrs.start > UNIX_TIMESTAMP('2010-01-01 00:00:00') AND cdrs.status = 'answer' AND cdrs.talktime > 0 AND custome\
rs.parent IN (7,42))
GROUP BY customers.id,SUBSTR(FROM_UNIXTIME(`cdrs`.`start`),1,7),outgroups.name_invoices
ORDER BY SUBSTR(FROM_UNIXTIME(`cdrs`.`start`),1,7),outgroups.name_invoices;");
$this->throwExceptionOnError();

mysqli_stmt_execute($stmt);
$this->throwExceptionOnError();

$rows = array();

mysqli_stmt_bind_result($stmt, $row->name, $row->month, $row->name_invoices, $row->minutes, $row->calls);

while (mysqli_stmt_fetch($stmt)) {
$row->month = new DateTime($row->month.'-01 00:00:00');
$rows[] = $row;
$row = new stdClass();
mysqli_stmt_bind_result($stmt, $row->name, $row->month, $row->name_invoices, $row->minutes, $row->calls);
}

mysqli_stmt_free_result($stmt);
mysqli_close($this->connection);
// Save collected rows into the cache
$cache->save($rows,$id,array('customer_stats'),3*3600);
}
return $rows;
}

Your queries will now be cached after the first time you run them for the specified amount of time (TTL), and boom you’re done. If you require the update function in your service classes to purge the cache simply insert;

// To remove or invalidate in particular cache id, you can use the remove() method :
$cache->remove('idToRemove');

Read more about cache cleaning in the Zend Cache reference.

Hope this little article helps and please comment if you guys have better suggestions.
(Yes I know I can use APC & Memcached, but in this example I didnt have to 🙂 )

Regards
Danny Froberg

Talk: PHP Best Practices – Matthew Weier O’Phinney and Lorna Jane Mitchell

Writing maintainable code is an art that takes effort and practice to master.
Part of that art is learning what tools and strategies will assist you in that effort. In this tutorial, we will cover a variety of practices and tools that can make your life, and the lives of your team members, easier as you develop your applications. Among them, we will provide overviews of:

  • Version Control
  • Coding Standards
  • Unit Testing basics
  • QA tools: phpcs, phploc, phpmd, continuous integration, and more
  • Team Collaboration tools, such as Skype, IRC, issue trackers, and more

via Talk: PHP Best Practices – Joind.in.

PHP 5.3 namespaces for the rest of us

According to the official documentation, PHP namespaces have been designed to prevent name collisions between classes from different packages and to avoid the use of very long names in the code to refer to classes or functions—nobody really wants to have to deal with something called Zend_Db_Adapter_Mysqli or PHPUnit_Framework_Constraint_IsInstanceOf, after all. This means that namespaces help a developer write code that is both more concise and clearer—a direction which is always an improvement towards expressiveness.

Within the PHP implementation of namespaces, these names will be ideally refactored to Zend\Db\Adapter\Mysqli and PHPUnit\Framework\Constraint\IsInstanceOf, where \ is the namespace separator. In the codebase, however, there will typically be very few references to these classes with their fully qualified name, because it is possible to import entire namespaces in a script and then use the class names directly, making the code easier to follow and unambiguous to write.

In fact, the definition of a namespace class itself does not contain its fully qualified name. For example, this would be the source file of an hypothetical MyLibrary\TypeOfComponents\MyClass class:

<?PHP
namespace MyLibrary\TypeOfComponents;
class MyClass
{
// ...
}

The convention when writing namespace-enabled code is that of creating a folder structure that reflects the individual components of a namespace (for example, MyClass would be in the MyLibrary/TypeOfComponents directory. This helps standardizing the autoloading process.

Read the full story PHP 5.3 namespaces for the rest of us | php|architect.

App_Controller_Helper_Params for JSON and XML POSTs

Matthew Weier O’Phinney shares a bit of very useful code to inject request params into a Zend Framework request object from a JSON or XML POST request.

“Below is a plugin I use to translate JSON or XML raw post request data to request user parameters.
Note that it expects a “Content-Type” header of either “application/json” or “application/xml”. If those are detected, it then does the translation and injection.
Once it has, you can then simply access the parameters from your request object like any others.
I actually use this with dojox.data.JsonRestStore already, so you should be set. :)”

class App_Controller_Helper_Params extends Zend_Controller_Action_Helper_Abstract { 
/**
 * @var array Parameters detected in raw content body
 */
 protected $_bodyParams = array(); 
/**
 * Do detection of content type, and retrieve parameters from raw body if
 * present
 *
 * @return void
 */
 public function init() {
 $request = $this->getRequest();
 $contentType = $request->getHeader('Content-Type');
 $rawBody = $request->getRawBody();
 if (!$rawBody) {
   return;
 }
switch (true) {
 case (strstr($contentType, 'application/json')):
   $this->setBodyParams(Zend_Json::decode($rawBody));
   break;
 case (strstr($contentType, 'application/xml')):
   $config = new Zend_Config_Xml($rawBody);
   $this->setBodyParams($config->toArray());
   break;
 default:
   if ($request->isPut()) {
    parse_str($rawBody, $params);
   $this->setBodyParams($params);
 }
 break;
 }
}

/**
* Set body params
*
* @param array $params
* @return Scrummer_Controller_Action
*/
public function setBodyParams(array $params)
{
  $this->_bodyParams = $params;
  return $this;
}

/**
* Retrieve body parameters
*
* @return array
*/
public function getBodyParams()
{
  return $this->_bodyParams;
}

/**
* Get body parameter
*
* @param string $name
* @return mixed
*/
public function getBodyParam($name)
{
  if ($this->hasBodyParam($name)) {
    return $this->_bodyParams[$name];
  }
  return null;
}

/**
* Is the given body parameter set?
*
* @param string $name
* @return bool
*/
public function hasBodyParam($name)
{
  if (isset($this->_bodyParams[$name])) {
    return true;
  }
  return false;
}

/**
 * Do we have any body parameters?
 *
 * @return bool
 */
 public function hasBodyParams()
 {
   if (!empty($this->_bodyParams)) {
     return true;
   }
   return false;
 }
 
 /**
 * Get submit parameters
 *
 * @return array
 */
 public function getSubmitParams()
 {
   if ($this->hasBodyParams()) {
     return $this->getBodyParams();
    }
   return $this->getRequest()->getPost();
 } 

 public function direct()
 {
   return $this->getSubmitParams();
 }
}

11 easy steps for installing Apache ActiveMQ for PHP

Take a peak at Web Developer Juice’s writeup on how to configure and use ActiveMQ (Message Queues), defenitely worth the read.

Apache ActiveMQ is one good option for implementing message queue in your PHP application. It can be easily installed on your server and it’s web accessible admin interface really makes administrator’s life easy. It can be easily connected with PHP via STOMP. I will suggesst to use MySql for Data persistance and start ActiveMQ as unix service.

Basic requirements: java, php, mysql.

via Web Developer Juice.