DocBlock and svn:keywords

Posted by Jad on September 04, 2007

In the application’s coding conventions (which I am almost done writing), I took the time to elaborate about code documentation and the use of phpDocumentor. Among the things discussed, there is the DocBlock, the header template of each PHP file and which looks something like that:

/**
 * Short description for file.
 *
 * Long description for file
 *
 * PHP 5
 *
 * Copyright (c) 2007, Company Name
 *                     Street address
 *                     City, State, Zip
 *
 *
 * @filesource   $HeadURL$
 * @copyright    Copyright (c) 2007, Company Name
 * @link         http://www.companywebsite.com CompanyName
 * @package      #### PACKAGE NAME ####
 * @sub-package  #### SUBPACKAGE NAME ####
 * @since        #.#.#  //Correct version number as needed
 * @version      $Revision$
 * @author       Your Name
 * @modifiedby   $LastChangedBy$
 * @lastmodified $Date$
 */

Now you might be asking yourself what are all those $HeadURL$, $Revision$, etc. Those are ‘keywords’ for Subversion which can be dynamically updated on every commit. By default, Subversion doesn’t substitute those keywords but you can easily set that directly from the shell using:

$ svn propset --recursive svn:keywords 'HeadURL Revision LastChangedBy Date' /path/to/repo

Or, in case you are using TortoiseSVN, from the right-click menu of your repository’s folder - TortoiseSVN > Properties > Add. You can then enter ’svn:keywords’ in the ‘property name’ field and ‘HeadURL Revision LastChangedBy Date’ in the ‘property value’ field. Don’t forget to check the ‘apply property recursively’, otherwise, make sure you are only setting it on a file not a directory.

From the svn propset help shell command:

The svn:keywords, svn:executable, svn:eol-style, svn:mime-type and svn:needs-lock properties cannot be set on a directory. A on-recursive attempt will fail, and a recursive attempt will set the property only on the file children of the directory.

SELECT DISTINCT in CakePHP

Posted by Jad on August 31, 2007

Even though CakePHP’s model already includes many of the database query functions, I found that the SELECT DISTINCT was missing. Ok, I know that you can always do it using either Model->query('SELECT DISTINCT `c1`, `c2`) or Model->findAll(null, 'DISTINCT `c1`, `c2`') but that would be like saying use Model->query() instead of Model->findAll().

The cool thing in CakePHP is that you can add your own functions to use in your app on top of the ones that come bundled in the core. For the Model, you just create an ‘app_model.php’ file that you place in our app’s main folder. The empty file should look like this:

/**
 * Custom AppModel that adds functionality to the core Model
 */
class AppModel extends Model
{
   //empty
}

Now inside your new AppModel class, add the following function:

   /**
    * Returns a resultset array with DISTINCT fields from database matching given conditions.
    *
    * @param   mixed    $conditions SQL conditions as a string or as an array('field' =>'value',...)
    * @param   mixed    $fields Either a single string of a field name, or an array of field names
    * @return  array    Array of records
    */
   function findDistinct($conditions = null, $fields = null)
   {
      $db =& ConnectionManager::getDataSource($this->useDbConfig);

      $str = 'DISTINCT ';
      if (!is_array($fields))
      {
         $str .= '`' . $fields . '`';
      }
      else
      {
         foreach ($fields as $field)
         {
            $str .= '`' . $field . '`, ';
         }
         $str = substr($str, 0, -2);
      }

      $queryData = array(
                     'conditions'   => $conditions,
                     'fields'       => $str,
                     );
      $data = $db->read($this, $queryData, false);

      return $data;
   }

You can now use Model->findDistinct('c1') or Model->findDistinct(array('c1', 'c2', 'c3')) to retrieve DISTINCT columns values.

Hoping you find it useful.

nusoap class or SOAP extension?

Posted by Jad on August 03, 2007

Back when the web app was to be hosted on the VPS (using PHP4), I had started coding some of the scrapers and parsers to retrieve data from different affiliate networks. After moving to the new servers and setting them up with the latest stable versions of PHP and MySQL, it was time I clean up, optimize and document my code. But no, before I could start doing that and while I was still testing it to refresh my memory on the different methods available within the DtSoap class, I started getting errors!

nusoap

My code required nusoap to communicate with the networks’ WSDL. However, just like for nusoap, PHP5 includes a class named ‘soapclient‘ which caused the conflict. The solution was simple, changing the class’ name to ‘nusoapclient’ and the constructor too before finally changing my code to call for it instead of the old name.

That was the lazy-guy in me reasoning. As soon as the geek kicked in, you guessed it, or maybe not: I decided to update the code and use the built-in soap extension available in PHP5. Continue reading…

Data Manipulation 101

Posted by Jad on July 30, 2007

One of the application’s core functionalities is monitoring changes on different sources. Some of which have some kind of webservices available while other don’t. By monitoring I mean getting, validating, storing and comparing the data over time. For that, different data manipulations are required:

  • fetching
  • scraping
  • parsing
  • cleansing
  • storing
  • mining

I will share in this serie of posts different resources, code snippets, benchmarks and techniques related to one or more of the above. Maybe if I get enough time, I can cover them all and group them in one easy-to-understand chapter: data manipulation 101.

The sandbox environement I am using:

- PHP 5.2.3
- libcurl
- dom.
- MySQL 5.1

And now, on with the show. Next up, Data Scraping.

Configuring CakePHP for easy deployment

Posted by Jad on July 29, 2007

Whenever you are developing an application, you are normally using at least 2 environments: development and production. Depending on how big your app and database are, deployment may become a long list of to-dos in order to have everything setup correctly (database changes, code merges, apache configuration, etc.). When you want to do things the right way, you usually have a 3rd environment, staging. Yes, another almost identical to-do list.

Now imagine you are developing in a team with each member working on a different part of the application in individual branches on the repository. New features are only merged with the team’s branch (development) after they are tested individually. So now, you have to deploy from individual to development, then to staging before finally pushing updates to production.

This time-consuming process is definitely not the best solution, put aside the fact that it is bound to break with any mistake while reconfiguring everything. Today, we will automate CakePHP’s configuration for easy deployment.
Continue reading…

Choosing a development framework

Posted by Jad on June 21, 2007

During the past couple of years, Ruby on Rails has been getting massive attention and I never really got the time to dive into it - learned a couple things here and there, but haven’t really developed any application with it. I liked it for the same reasons that everyone else does: it’s a rapid development framework using the MVC pattern and ActiveRecord.

Learning a new language (in this case 2, Ruby and Rails) is definitely exciting, but having to do that while developing a pretty advanced web application that needs to go live in less than 2 months, raises the bar pretty high.

So I opted for goold ol’ PHP. I have built all kinds of little scripts, hacked many open-source projects (as in modified them) and released 4 of my own turnkey solutions all based on PHP but never had I used an open-source framework for that. This time, things will change. I said in the first post something about CakePHP which is the one I selected for this application and probably more to come.

As Jonathan Snook says it so well:

I almost fear putting this kind of post together as it’s bound to pull the fanatics (in the negative sense of the word) out of the woodworks.

So instead of giving a comparison of the available PHP MVC frameworks, I will instead list what I was looking for and believed CakePHP would cover. Here it is:

  • Open Source / Free License
  • KISS
  • OO and MVC
  • ORM / Active Record
  • Security
  • Ajax interoperability
  • Good controller structure
  • Good helpers available
  • Scaffolding

I did try a couple before making my final decision, but here are the resources that helped me make up my mind:

- Taking a look at ten different PHP frameworks, by Dennis Pallett
- CodeIgniter vs. CakePHP, by Jonathan Snook
- New Year’s Benchmarks and A Bit About Benchmarks, by Paul M. Jones
- Glue vs. Full Stack and More framework fun, by Chris Hartjes
- Comparing Frameworks, by Tim Bray

AdWords API - only the beginning

Posted by Jad on June 19, 2007

Been pretty busy lately with csstester.com which is soon to be launched, finalizing stuff, etc.

I know, I know - I should only be concentrating on one application at a time, but that ain’t my kind. Am not smarter or anything, I just get bored too fast from working on the same thing project every single day, all day.

Anyhow, back to the reason for this post, Google AdWords API and the awful experience I had so far.

Back in early 2006, I had applied and received my developer token for the API (which Google stopped distributing as of October 2006). Developer token, email account to which the token was emailed and password in hand, I head to the login page. To my big surprise, Google replies back with a message along the lines of:

This account does not exist

Ok, so you emailed me a developer token and an API key in a separate email, to that exact same email address I am using to access my account, but you don’t believe I have an account. Great!

Since my developer email access is not the main admin access to the account, I log with my other email and invite the email Google had sent me the developer token to. A couple steps after, I am logged into my account using the developer credentials.

According to AdWords’ welcome email, I am supposed to find a ‘My Client Center’ link under the ‘Account’ tab.  But nothing there.

I go looking for help, reading pages and pages of information I could have skipped for the time being, but couldn’t find any kind of help about the issue I was having.

Last resort: email Google.

It’s been nearly 12 hours, and still no reply. One would have believed that between their  free meals or exercising, someone could have a look and just fix that in my account.

Waiting…

Parsing the use cases XML

Posted by Jad on June 11, 2007

I discussed the other day about writing the use cases. I also mentioned that I was going to write them all in XML so they could be easily parsed later on.

As you might have noticed by looking at only 3 use cases, it gets pretty long and hard to read in raw format, so first code had to be a parser for that.

Solution: Clean XML To Array by Ivan Enderlin, found on PHPClasses.

All you need is to download the lib.xml.php and create the a new file with the little code shown below.

<?php
include('lib.xml.php');
$xml = new Xml;
$out = $xml->parse('file.xml', 'FILE');
echo '<pre>'.print_r($out).'</pre>';
?>

All that was left to do was to parse it in an easy to read format. This will do for now as I don’t want to spend too much time on it.

<?php
include('lib.xml.php');
$xml = new Xml;
$out = $xml->parse('usercase.xml', 'FILE');
//echo '<pre>'.print_r($out).'</pre>';exit();

foreach ($out as $usecases) {
	for ($i=0; $i<count($usecases); $i++) {
		$usecase = $usecases[$i];

		echo '<div class="usecase">';
		echo '<h3>'.$usecase[name].': '.$usecase[description].'</h3>';
		echo '<p class="overview">';
		echo '<strong>Sitting:</strong> '.$usecase[sitting].'<br />';
		echo '<strong>Primary Actor:</strong> '.$usecase[primaryactor].'<br />';
		echo '<strong>Scope:</strong> '.$usecase[scope].'<br />';
		echo '<strong>Level:</strong> '.$usecase[level].'<br />';
		echo '<strong>Minimal Guarantee:</strong> '.$usecase[minimalguarantee].'<br />';
		echo '<strong>Success Guarantee:</strong> '.$usecase[successguarantee].'<br />';
		echo '</p>';

		echo '<h4>Stakeholders</h4><p class="stakeholders">';
		foreach($usecase[stakeholders][stakeholder] as $id => $stakeHolder){
			echo '<strong>'.$stakeHolder.':</strong> '.$usecase[stakeholders][interest][$id].'<br />';
		}
		echo '</p>';

		echo '<h4>Scenario</h4><p class="scenario">';
		if(is_array($usecase[scenario][step])){
			foreach($usecase[scenario][step] as $id => $step) echo $id.'. '.$step.'<br />';
		} else {
			echo $usecase[scenario][step];
		}
		echo '</p>';

		echo '<h4>Extensions</h4><p class="extensions">';
		foreach($usecase[extensions][extension] as $id => $extension){
			echo '<strong>Extends:</strong> '.$extension['extends'].' ('.$usecase[scenario][step][$extension['extends']].')<br />';
			echo $extension['xcase'].'<br />';
			echo '<strong>Steps:</strong><ol>';
			if(is_array($extension['step'])){
				foreach($extension['step'] as $id => $step) echo $id.'. '.$step.'<br />';
			} else {
				echo $extension['step'];
			}
			echo '</ol>';
		}
		echo '</p>';
	}
}

?>