Zend Framework

Authenticating A User With Zend_Auth

One of the nice things about the Zend Framework is being able to use it all, or only use individual parts that appeal to you. The Zend_Auth component is no exception, and makes it very easy to authenticate users. An important distinction needs to be made between authentication and authorization. Authentication is determining whether someone is who they say they are - in other words, assigning an identity to an end user. Authorization revolves around determining what actions a user can perform, and is typically handled by Zend_Acl.

Authenticate Against a Database

While using a 3rd party such as Facebook, Google, Twitter or OpenId is starting to gain a lot of traction, authenticating against a database is still one of the most common ways to authenticate a user. At it's most simplest (read the last paragraph to learn why you should never use this code), assuming we had a table called userTable with a field username for the username, and password for the password, we would have something like:

<?php
        $auth 
Zend_Auth::getInstance();
        
$db = new Zend_Db_Adapter_Pdo_Mysql(array(
            
'host' => 'localhost',
            
'username' => 'username',
            
'password' => 'password',
            
'dbname' => 'mydb'
        
));
         
$authAdapter Zend_Auth_Adapter_DbTable($db,'userTable','username','password');
        
$authAdapter->setIdentity($_POST['user'])->setCredential($_POST['password']);
        
$result $auth->authenticate($authAdapter);
        if(
$result->isValid()) {
            die(
'user authenticated');
        } else {
            die(
'user not authenticated');
        }
?>

In this example, we get the Zend_Auth instance using the static function getInstance. Zend_Auth is a singleton, meaning there should only be one instance of the class during request execution. The reason for this becomes apparent when you use Zend_Auth to get a user's identity. You want to ensure you are always accessing the same instance of Zend_Auth in your code.


We then set up the database connection with the variable $db, and set up the adapter used by Zend_Auth, referenced by the variable $authAdapter. $authAdapter takes the database adapter, then the name of the table that holds end users' credentials, then the field name of said table that has the username, and finally the field name of said table that stores the password. Finally, we set the identity and credential via a $_POST variable that would be supplied on a login form, and call authenticate on Zend_Auth while passing in the adapter and check if the result is valid. All in all, pretty straightforward.

Get My Identity


Once an end user has successfully logged in, we can get the end user's identity via Zend_Auth:


<?php
        $auth 
Zend_Auth::getInstance();
        if (
$auth->hasIdentity()) {
            
$identity $auth->getIdentity();
            
print_r($identity); // $authAdapter->setIdentity($_POST['user']);
        
}
?>

First, we grab the singleton Zend_Auth and then check if the user has an identity. If she does, then we get the identity. I can see the question bouncing around in your head - what is the identity. By default, when you log in with the code above, it will use the value from the setIdentity method, which is typically the user name. You can now tell whether a user is logged in, and you have their user name stored in a session.


What happens if you want to store something else besides a string as the identity? For example, you may be using Zend_Acl, and want to know the role of the user - let's create an object and store that as the identity. We have a couple of options to accomplish this, but first let's understand what Zend_Auth is doing when you call the authenticate method. Zend_Auth takes the identity returned by the Adapter (which in the case of the db adapter is a string) , and persists the identity in a session. Therefore, we could create our own adapter and return an object instead of a string, or we can interact directly with the db adapter and bypass Zend_Auth. The second option seems easiest to explain, so let's roll with that (subclassing db adapter would make a nice blog post...).

<?php
        $db 
= new Zend_Db_Adapter_Pdo_Mysql(array(
            
'host' => 'localhost',
            
'username' => 'root',
            
'password' => '',
            
'dbname' => 'bf'
        
));
        
$authAdapter = new Zend_Auth_Adapter_DbTable($db,'userTable','username','password');
        
$authAdapter->setIdentity($_POST['user'])->setCredential($_POST['password']);
        
$result $authAdapter->authenticate();
        if(
$result->isValid()) {
            echo 
'user authenticated<br />';
        } else {
            echo 
'user not authenticated<br />';
        }
        
$auth Zend_Auth::getInstance();
        
var_dump($auth->hasIdentity());
?>

We have similar code as before, except we are calling authenticate on the adapter. If you run this code in a browser, you would see that we authenticated the user (assuming you put in the right username and password), but we get false when calling $auth->hasIdentity(). Since we didn't use Zend_Auth to authenticate, the identity is not “saved.

To remedy this, we can update our code with the following lines:

<?php
        $auth 
Zend_Auth::getInstance();
        
$db = new Zend_Db_Adapter_Pdo_Mysql(array(
            
'host' => 'localhost',
            
'username' => 'root',
            
'password' => '',
            
'dbname' => 'bf'
        
));
        
$authAdapter = new Zend_Auth_Adapter_DbTable($db,'member','username','password');
        
$authAdapter->setIdentity($_POST['user'])->setCredential($_POST['password']);
        
$result $authAdapter->authenticate();
        if(
$result->isValid()) {
            echo 
'user authenticated<br />';
            
$auth->getStorage()->write($authAdapter->getResultRowObject(array(
                
'username''role'
            
)));
        } else {
            echo 
'user not authenticated<br />';
        }
        
        
var_dump($auth->hasIdentity()); //true
        
var_dump($auth->getIdentity()); // object(stdClass)#67 (2) { ["username"]=> string(3) "rob" ["role"]=> string(5) "admin" }
?>

Once the result is valid, we are getting the storage mechanism (usually a session) and telling the storage to save the result from the database as a standard object. The array of username and role are the fields we want to save. Notice we left out the password field, as that should not be persisted. For my applications, I actually have a user object that implements Zend_Acl_Role_Interface, so I instantiate my user object, and grab the values from the stdClass object that is returned from getResultRowObject.

Can You Say Insecure

What's so horrible about the above code? We are storing an end user's password in plain text within the database. If someone gains access to the database, that person now has access to all your users' password. Maybe that's not a big deal because you don't store any sensitive information like credit cards, but it's still a very bad idea. Not only will your street cred be ruined as the entire internet laughs at your folly of storing unencrypted passwords, but if you happen to store email addressees, a cracker would now have your user's email address and the password for your site. A decent percentage of your users will likely have the same password for your site and for their email account (who can remember unique passwords for every site...). You get the picture...

At a minimum, you need to encrypt the password stored in your database and add a salt to the password. You can read this article to brush up on the problems with storing passwords in a database. My next blog post will cover how to use a one way hash and a salt to make it more difficult for passwords to be cracked in the event your database is compromised.


Posted In: PHP, Zend Framework | 3 comments

Mocking Objects with Final Methods in PHPUnit

Having some issues with a mock in PHPUnit - the method on the mocked object was being called and the code run (bootstrap method on Zend_Application_Bootstrap_BootstrapAbstract). After some head scratching, I realized it was a final method, so PHPUnit can't override it, and thus, the code is executed. I extended Zend_Application_Bootstrap_BootstrapAbstract and overwrote the code that was cuasing problems (::bootstrap is final, but it calls ::_bootstrap, which you can override).

I 've been meaning to play around with Mockery, but until then, I've come up with some ways to deal with Mocks in PHPUnit (I personally don't think the implementation is too bad, but I'm not a die hard tester...). One of the issues I commonly run into is needing to return mocked objects from a mocked method. This is easy if the method is only called once, but what about if the same method is called a couple of times, and you need to return different mocks (e.g. ::bootstrap($name) will return different objects based on the $name parameter)?

I've used a closure with a callbackValue to get around this:

<?php
        $mapper 
$this->getMock('Bolton_MapperInterface');
        
$relatedMapper $this->getMock('Bolton_MapperInterface');
        
$callback = function($property)  use($mapper$relatedMapper) {
            if (
$property == 'model') {
               return 
$mapper;
            } else if (
$property == 'childModel') {
                return 
$relatedMapper;
            }
            throw new 
Exception(sprintf('Mapper factory mock passed an unexpected parameter (%s)',$property));
        };

        
$this->_mapperFactory->expects($this->any())->method('factory')->will($this->returnCallback($callback));
?>


What about needing to return a different value based on the number of times the method has been called? I'll use a callback function, but then have a static variable in the callback function that keeps track of how many times it has been called. In the test setUp() method, I will reset the static variable, so the function can be used across a number of different tests.

Posted In: Zend Framework | 1 comment

Zend_Auth and more than one record matches the supplied identity

The Zend_Auth Db adapter will fail if you have more than one record returned, which makes sense. However, when I have coded something like this in the past, I would check how many records were returned with a matching username and password. Zend_Auth looks at how many records are returned for the username field regardless of whether the password matches, so you need to have a unique username field. I was trying to use a person's last name and password to authenticate, but obviously people had the same last name, so it was failling.

Posted In: Zend Framework | 3 comments

Setting up a new Site with Zend Framework Application/Tool

It's been awhile since I had to set up a new site with Zend Framework, and I took the chance to play around with Zend_Tool_Project. Following the QuickStart guide, I had a site up and running in no time. But it did take me a little while to figure out how to get my modules and custom library working with the site.

Modules

To get modules enabled, you first need to create a module with Zend_Tool_Project, or you can manually create a modules directory and add the module folder there. If I wanted to add a module called cms, I would create application/modules/cms or use the command line zf create module cms.

Then, In application.ini, you need to add the following two lines:
resources.modules[] =
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"

Finally, in order to load models and views, you need to add a Bootstrap.php file in the root of the modules - i.e. application/modules/cms/Bootstrap.php. In the module Bootstrap.php, place the following:
[php]
class Cms_Bootstrap extends Zend_Application_Module_Bootstrap
{
}
[/php]

You should be all set with modules now. Models you create within the module will use the naming convention of Cms_Model_Foo and controllers will be Cms_IndexController (when using Zend_Tool_Project and issuing zf create controller index index-action-included=1 cms, it names the controller class IndexController instead of Cms_IndexController?)

Autoloading Custom Library


The last thing I needed was to autoload my personal library which uses the naming convention Bolton_. My custom library resides in the same folder as the Zend library and is within my include path set in php.ini. In application.ini, you need to add the following line:
autoloaderNamespaces[] = "Bolton_"

Posted In: Zend Framework | No Comments

Zend_Service_Technorati and Connection Errors

I had a site that uses Technorati's API via Zend Framework- e.g. Zend_Service_Technorati. I noticed I am getting TCP connection errors via an exception (Message: Unable to Connect to tcp://api.technorati.com:80. Error #111: Connection refused), and upon going to Technorati's site to look at the API, I found out the new API is under works and the old API is available until October 25th, 2009. It is November 3rd, the old API apparently doesn't work and the new API isn't released. Really? Am I missing something here? TCP connection failing tells me they are rejecting completely rejecting the TCP connection, which leads me to believe they have shut off the API. Searching on Twitter confirms this, although having an existing key doesn't seem to matter...

Posted In: Zend Framework | 3 comments

Zend_Acl with User Specific Permissions

There are many articles around explaining Zend_Acl and how to use it within a CMS like system where generic roles apply - i.e. an admin can do anything, a guest can leave a comment and an author can write articles. But I was having a hard time figuring out how to elegantly enforce user specific permissions in addition to generic permissions For example, an author can save a new article, but can only update or delete an article that they "own". I was trying to use assertions, but the role object in the assertion was turned into a generic Zend_Acl_Role object, even though I was using my own role object that implemented Zend_Acl_Role_Interface. Therefore, I couldn't check the userId of the role in the assertion and was trying to pass responsibility back onto the object that was checking the acl.

It looks like this has all been fixed in 1.9.1, and Ralph does a good job of explaining the details. I have yet to try the improvements, but looking forward to refactoring my code to use the new and improved assertions.

Posted In: PHP, Zend Framework | 1 comment

Zend_Http_Client and Garbled Response Body

Using Zend_Http_Client to make a simple REST GET request to a web service and the body of the response I got back was completely mangled – i.e. the characters were not even remotely readable. That’s strange I thought, and pasted the url in my browser and it came back fine. Looking at the response headers, I saw the content encoding was gzip ([Content-encoding] => gzip), which made sense as my browser would uncompress the response, while PHP would not automatically do that.

Rather than trying to uncompress the response, I modified the request header to not accept gzip and it came back un-encoded and ready to use in PHP:

[php]
$client = new Zend_Http_Client($url);
$client->setHeaders(array('Accept-encoding' => ''));
$response = $client->request();
print_r($response);
[/php]

Posted In: PHP, Zend Framework | 1 comment

Zend_Service_Technorati, Exceptions and partialLoop

I am utilizing Zend_Service_Technorati to tag search and when I return the results and loop through it via a partialLoop, an exception is thrown occasionally. One of the results does not have a valid uri and Zend_Uri_Http is throwing the exception based on the path (not sure whether Tehnorati is returning a invalid url or Zend_Uri_Http is declaring a valid url invalid). The problem is I am using partialLoop to dynamically loop through result sets other than Technorati, such as Youtube and Flickr, and it is nice just to call the appropriate partial based on the result set object.

However, there is no easy way to catch the Exception. If I wrap the entire partial loop in a try/catch, then it doesn't display any results when there is an exception, even though there is only one offending result:
[php]
try {
echo $this->partialLoop('adminexternalmedia/_result' . $this->collection->type . '.phtml',$this->resultSet);
} catch(Exception $e) {
echo 'problem';
}
[/php]
I can't catch the exception in the partial that handles the display, because the exception is created once the individual result is grabbed from the set and before the partial is "rendered" - i.e. triggered in the foreach call. Since the result sets implement SeekableIterator, I can use a while loop and call partial, instead of relying on the partialLoop, and handle my exception as I deem fit:
[php]
$this->partial()->setObjectKey('model');
while($this->resultSet->valid()) {
try {
$result = $this->resultSet->current();
echo $this->partial('adminexternalmedia/_result' . $this->collection->type . '.phtml',$result);
} catch (Exception $e) {
echo 'problem

';
}
$this->resultSet->next();
}
[/php]

Posted In: PHP, Zend Framework | No Comments

Zend_Gdata and Fatal error: Allowed memory size

Querying Youtube for videos via Zend_Gdata and ran into memory issues when searching for up to 20 videos. It was a simple fix, just had to up my memory_limit in php.ini. It was at 8M and I set it to 12M and am not running into any more issues. If you are on a shared hosting environment, you can use init_set within the page:
[php]
ini_set("memory_limit","12M");
[/php]
Just be careful what you set it to, as too high a limit could cause problems.If you have a page that consumes a lot of memory and you set it high, say 50M, and along comes something that requests the page a lot, maybe a search spider gone bad or malicious user, you could run out of memory on your server...

Posted In: PHP, Zend Framework | No Comments

Zend_Paginator and Zend_Paginator_Adapter_Null

I have been playing around with Zend_Paginator a little more, and came across a use case where I didn't want the Paginator to manage the data, i.e. I was feeling lazy and didn't feel like writing a custom adapter. You can use Zend_Paginator_Adapter_Null to manage the pagination controls and simply pass in the total number of results into the constructor. I.e
[php]
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Null('43'));
$paginator->setCurrentPageNumber('1');
$paginator->setItemCountPerPage('20');
$this->view->paginator = $paginator;
[/php]
The 43 I am passing into the constructor is the total number of results, and would likelu not be a static number, but coming from some other data source. For example, if you were using Zend_Service Flickr, it would look like this, assuming the variable $flickr has already been set up:
[php]
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Null($flickr->totalResultsAvailable));
$paginator->setCurrentPageNumber('1');
$paginator->setItemCountPerPage('20');
$this->view->paginator = $paginator;
[/php]
Follow the manual on setting up the actual display of the pagination. Now I need to get some more spare time and write a custom adapter for flickr and other Zend Services and also familiarize myself with caching of the paginator.

Posted In: PHP, Zend Framework | No Comments

Zend_Paginator

I needed some pagination for a website I am building, so I took a look at Zend_Paginator. It is pretty straightforward and easy to use, especially if you are using the rest of the framework. The code below assume the class Foo extends Zend_Db_Table_Abstract, and grabs a Zend_Db_Select object from the table to pass in the constructor of Zend_Paginator. Zend_Paginator uses that select object to run 2 queries, one to determine the overall count of objects, and one to grab the rowset used on the page. This rowset is then accessed via getItemsByPage();
[php]
$foo = new Foo();
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_DbTableSelect($foo->select()));
$paginator->setCurrentPageNumber($this->_getParam('page',1));
$paginator->setItemCountPerPage('2');
$this->view->paginator = $paginator;
$this->view->rowset = $paginator->getItemsByPage($this->_getParam('page',1));
[/php]
Of course, most of the time you wont be using a vanilla select statement, so your code may end up looking more like this:
[php]
$foo = new Foo();
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_DbTableSelect($foo->select()->where('publish = ?','yes')));
[/php]
I don't like to have my logic for grabbing a rowset spread throughout my controller class and try to isolate it within the object.
[php]
$foo->getLatest(); //return rowset with custom query
[/php]
My Foo object also doesn't directly extend Zend_Db_Table_Abstract. So, I contemplated creating a customized Zend_Paginator_Adapter, but opted instead to have an option on my "get" methods to return a select object instead of a rowset
[php]
$foo = new Foo();
$select = $foo->getLatest(array('paginator' => true));
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_DbTableSelect($select));
[/php]
I'm not entirely sold on this approach yet... We'll see how it pans out as more use cases are tested out. Is it bad that the getLatest method can return different types of objects based on an input parameter?

Posted In: PHP, Zend Framework | No Comments

Zend_Acl, jQuery and HTTP Status Codes


In my controllers, I use Zend_Acl to check whether or not a user is allowed to perform an action (i.e. should the code in the method be executed). I also have to check permissions once in a method, as someone might be trying to access an object that does not belong to them - i.e. edit a blog post of someone else. In that case, I was forwarding to a method in the Error controller and adding a 403 to denote that the user could not access the page, as well as displaying a generic page saying the user does not have the proper permissions :
[php]
$this->getResponse()->setRawHeader('HTTP/1.1 403 Forbidden');
[/php]

I am using jQuery with AJAX to run methods on occasion, and whenever returning a 403, my jQuery callbacks never executed : $.post('/tripreport/admintr/saveroute',doThis);. In reading the jQuery documentation, I need to use the $.ajax function instead of $.post : $.ajax({type:"POST",url:"/tripreport/admintr/saveroute",error:handleError,success:handleSuccess});. In the handleError function, I can test whether or not the status code is a 403, and then take the appropriate action : XMLHttpRequest.status == '403'.

On a related note, if you are using $this->_forward in your methods in the Action Controllers like I mentioned above, be sure to use "return" right after, if you don't want to execute any more code in that method.

Posted In: PHP, JavaScript, Zend Framework | No Comments

Throw Exceptions


I pushed some changes out from my development server to my production server and ran into an issue with uploading/moving/resizing photos. It only took about 15 to 20 minutes to track down the issue, but it would have been a lot quicker if I hadn't been sloppy with my code. In using the Zend Framework a lot, I have really grown to rely on Exceptions being thrown. It really helps track down issues, as you can look through the stack and determine rather quickly and with a fair amount of accuracy where the true problem lies.

In my case, I had a class in my models directory that was creating a folder to store pictures. I wasn't checking if the folder was created, and when Zend_File_Transfer_Adapter_Http went to move the file from the /tmp directory to the target directory, it threw an exception. But since I didn't throw an exception when the folder was not created, I started troubleshooting by looking at the Zend Class and trying to determine why it wouldn't move the file on the production server, but would on the development server. I have since updated my model class to throw an Exception when it can't create the folder. I altered the permissions and tried it out on my development box, and viola, the Exception was thrown and displayed on screen (would log on prod site), making it very quick and easy to determine what the problem was.

Lesson learned. When you rely on certain actions to be taken, check if they succeed and throw an Exception if they don't. Now if I could get rid of all my other sloppy coding habits...

Posted In: PHP, Zend Framework | No Comments

Zend Controller Error Plugin and 404's


The Zend Framework provides a nice plugin to handle errors that bubble up in your controllers. It will automatically route Exceptions to the Error Controller, assuming you have flagged the Front Controller to not display exceptions (which should be the case in a production site). I implemented code for handling the Error Controller that was similar to the code found in this posting. But then I couldn't figure out how to trigger the switch statement for the no action exception (i.e. Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION). Whenever I threw a Zend_Controller_Action_Exception it went to the default case, for which I had a message saying a server error occurred.

Digging into Zend_Controller_Plugin_ErrorHandler, I found I needed to put a 404 code into the constructor. So now my code looks like:
[php]
throw new Zend_Controller_Action_Exception('This page dont exist',404);
[/php]

Zend_Controller_Plugin_ErrorHandler now sets the type as EXCEPTION_NO_ACTION. Now I can throw that when I need a 404 Not Found page. It isn't the cleanest approach, since I am throwing that in a method, and technically I do have an action. But that seems to be the easiest way I have found, rather than forwarding the request to the Error Handler.

Posted In: Zend Framework | 1 comment

Zend Framework - Zend_Db_Table and Zend_Loader


I am using Zend Framework and have a different directory structure than normal. I separate modules into a folder in /home and have website specific code in in an app folder for that website. In other words, /home/__common/app/blog would be where code for a blog sits that is used across many websites. And could for one respective site would sit in /home/website/app.

This has worked fine, as I can set the module directory in my bootstrap : $front->addModuleDirectory(MODULE_PATH);. But Zend_Db_Table_Abstract uses Zend_Loader to call Rowset and Row classes. When I define a custom Row or Rowset class from my modules, Zend_Loader throws an Exception, as it is hard coded to call Zend_Loader. Even though I have the logic built in my _autoload function in my bootsrap to handle my directory structure, Zend_Loader bypasses this, which is frustrating. This bug report has the same issue and identifies a workaround by modifying Zend_Loader, which I don't want to do, as I don't want to have to modify Zend_Loader every time I checkout the latest version of the code.

Hopefully the patch will get applied, in the meantime I am manually requiring my Row and or Rowset class in my Class that extends Zend_Db_Table_Abstract, and that solves the problem (although the Row class is now "loaded" even when it may not be needed).

Update : I was able to get it working without an include in the model class by putting a symlink from the website's model directory to the model directory in the app. Part of the reason this problem came up is my unique directory structure and the way I am naming model classes in my modules. To make things work with my __autoload, I append the module name to the model class. I.e. a class called Post in the Blog module would be named Blog_Post - that makes it easy to figure out where to find the class in my __autoload function in the bootstrap. I guess I could add all the modules to the include path, but that would require a lot of rework. I initially decided not to do just that, as it seemed like that would add a lot of paths to the default include path, and would require the include path to be updated every time I needed to use a new module. I guess if I ran a benchmark and having 10 extra paths in the include path didn't matter, it would have been smarter to do it that way, since I now have to add a symlink everytime I want to use a module.

Posted In: Zend Framework | No Comments

Technorati API Down?



I have been working with a couple of web services lately (see prior post on Zend framework and Gdata), and upon deploying my application found out the technorati part of my site wasn't working. I ran my unit tests, and multiple errors occured, saying the script wasn't able to connect to the url:

Fatal error: Uncaught exception 'Zend_Http_Client_Adapter_Exception' with message 'Unable to Connect to tcp://api.technorati.com:80. Error #110:
Connection timed out'

This happened last night, and again this morning. I tried to access the API straight through the browser (http://api.technorati.com/search?key=[apikey]&query=[words]), and the service is unavailable. So fairly sure it isn't a result of the Zend Framework or my code. Can't find any info on the web, so not sure what is going on... I'll have to check in another day or two and see if it is back up or any other info is available.

Posted In: PHP, Zend Framework | 1 comment

Zend Framework and Youtube API



I have been messing around with the different Zend_Service components available in the framework, and in general am very pleased with the ease of use and functionality. However, Zend_Gdata seems somewhat complicated. Now I must admit that I have not spent much time with classes, and I am sure the structure is set up to accommodate the large amount of services that Google provides, but there is very little documentation and the API doesn't seem easily comprehensible, atleast compared to the Yahoo and Technorati APIs.

It appears that Google staff wrote the code, and I guess that is why it breaks with the naming convention - i.e. all the other Services fall under Zend_Services_***, while Google's is Zend_Gdata. I am interested in the Youtube API, and it was easy to construct the query and get the result set back, but I found the result set to contain a lot of information (most of which was extraneous from my point of view), with many classes in it. Again, I was quickly trying to get it working and not at all interested in spending a lot of time learning the ins and outs of Google's API's. I am sure it will all click as I learn the other parts of Zend_Gdata, but for now, it is a pain.

For example, using the code from the documentation, when you grab a result set and iterate through it, how do you grab the src for the flash movie? Digging through the code, I found it was $videoEntry->mediaGroup->content[0]->getUrl(). That's not to difficult, but then I noticed some of the results didn't have any data for $videoEntry->mediaGroup->content. Is that a fluke, or by design? What do the values in the content object mean? I can guess what isDefault means and most of them are obvious, but the comments in the code are completely worthless (well not completely, as it does tell me that the channels property of the MediaContent object is a integer...). What's the deal with the array of values - is the first value in the thumbnail array the preferable image, or just the first frame from the movie? I tried looking at the Google API docs, but that surprisingly didn't seem to shed much light either.

I guess I should get off my ass, learn the API and contribute to the documentation. But its just so much easier to complain. Anyways, here is what I have quickly worked out. Once I start playing with the other Google services and have a better understanding of how all the classes fit together, I'll let you know...

[php]
$yt = new Zend_Gdata_YouTube();
$query = $yt->newVideoQuery();
$query->videoQuery = 'cat';
$query->startIndex = 10;
$query->maxResults = 20;
$query->orderBy = 'viewCount';

echo $query->queryUrl . "\n";
$videoFeed = $yt->getVideoFeed($query);

foreach ($videoFeed as $videoEntry) {
echo "---------VIDEO----------\n";
echo "Title: " . $videoEntry->mediaGroup->title->text . "\n";
echo "Description:" . $videoEntry->mediaGroup->description->text . "\n";
echo "Url to the Youtube page: " . $videoEntry->mediaGroup->player[0]->getUrl() . "\n";
echo "Thumbnail image of the video: " . $videoEntry->mediaGroup->thumbnail[0]->getUrl() . "\n";
if(!empty($videoEntry->mediaGroup->content[0])) {
echo "Src to the video : " . $videoEntry->mediaGroup->content[0]->getUrl() . "\n";
}
echo "\n\n\n";
}
[/php]

Posted In: PHP, Zend Framework | 5 comments

Book Review : Zend Framework in Action



I signed up for the Manning early access program for Zend Framework in Action to get information on the ACL functionality in the framework and the book provided some useful examples. I was a bit underwhelmed by the content in the first release, but I guess that is to be expected when viewing a rough cut.

This release (the 2nd) is a vast improvement, and the book is really starting to come together. I like how the authors go into detail about testing, showing not only how to test your models, but also how to test controllers, and showing some common set up steps. It doesn’t cover all the details of testing, nor should it, but the book provides a solid background to build upon. This is refreshing compared to most books which simply mention testing and say whole books have been written about it.

The book does a good job of logically building upon previous examples, and I felt the flow was good when introducing more complex topics like Plugins and ActionHelpers. This version was updated to include Zend_Layout, including info on partials and ActionStack plugin, but the chapter on forms is not complete yet – looking forward to reading that when it comes out.

I thought the AJAX chapter was a little shaky. Maybe it’s me, but I felt the authors spent too much time going over a general ajax request sans the framework. By the time they work the framework into the equation, they are using an include file in a controller action to bring in some procedural code written earlier. And that procedural code lives in the models folder – it just doesn’t seem right to me. I think this would have been a good opportunity to use Zend_Filter, although it is used in later in the book when discussing Zend_Search_Lucene (the chapter on this is very thorough and easy to understand). Also, they recommend using separate controllers/actions for Ajax requests and not rendering the view in each action method. That’s fine, but I would also like to have seen using a Plugin in the dispatch loop to detect an Ajax request and automatically turn off Zend_View/Zend_Layout.

I like how the authors occasionally work in design patters, such as the registry, and the observer pattern in the search chapter. The authors describe using the observer pattern with hooks in the DB class (hooks which I didn’t know existed) to update the search index.

The chapter on deployment was good; most books don’t seem to tackle this. My only quibble is when discussing setting up virtual hosts, they say to make sure to enable AllowOverride to use .htaccess. I don’t have any problem with this, but since the book is talking about Virtual Hosts, they should recommend putting the redirects in the httpd.conf file, rather than relying on .htaccess, as it is more efficient due to the fact that Apache doesn’t need to read a file on every request.

At times I felt like there may be too much ancillary topics covered, however, after finishing the book, I felt overall it was good that topics such as testing, deployment, and version control were covered. While there isn’t enough information to provide in depth coverage, it exposures the reader to these topics and provides enough information to get started, rather than just briefly mentioning it. And the coverage of the different components with the Zend Framework is comprehensive and understandable. If you are looking at working with the Zend Framework, I would definitely recommend this book.

Posted In: PHP, Book Review, Zend Framework | 2 comments

Loading Zend Config file for a Cron Job

The other day I talked about using Apache Environment variables to determine which section of a config file to use for a Zend Framework website. That works swimmingly, however, I had a cron job that was running for the same site, and since it was piped directly to php, Apache Environment variables were not an option. Fortunately, PHP has the function php_uname('n') which will output the hostname of the computer. You can then load the appropriate config section based on that:

[php]
$environment = (php_uname('n') == 'rlbolton-desktop') ? 'development' : 'production';
$config = new Zend_Config_Ini(APP_PATH . 'configs/config.ini', $environment);
[/php]

Posted In: PHP, Zend Framework | No Comments

Traits for PHP

Via blog, found a link to a proposal for Traits in PHP. I wasn't familiar with the practice, but as I read the proposal it struck a cord with me. I have been grappling with how to reuse certain functionality throughout disparate classes.

I looked at Mixins, which I also have been playing around with in JavaScript. Problem is, how do you combine a couple of mixins together. Say I am using the Zend Framework, and I have some classes that inherit from the Zend_Db_Table_Row and I want a couple to use some Mixin classes. Do I extend the Zend class for my Mixin, and extend my other classes off the Mixin? What happens if some of the classes need other functionality found in a different Mixin? Needless to say, it doesn't seem like a great solution.

Lets jump into an example. I have a number of classes extending Zend_Db_Table_Row, and they all have a user entered attribute, like title or name, which gets turned into a SEO friendly url and saved to the database. Additionally, I don't want the url to change, so there has to be code from preventing another developer from updating the url.For example,
[php]
class Article extends Zend_Db_Table_Row
{
public function insert() {
$this->url = $this->_createUrl();
parent::insert()
}

protected function _createUrl($name) {
//code to create the url - remove spaces, etc...
return $url
}
}
[/php]
How do I share that functionality across multiple classes, without using inheritance??

Posted In: PHP, Zend Framework | No Comments

Order By On findManyToManyRowset method for Zend Framework

Select statements (via Zend_Db_Table_Select) can now be passed into the findManyToManyRowset method of Zend_Db_Table_Row_Abstract. In ZF 1.0, I couldn't order my result sets coming back from MySQL, which was a pain. Now, I can use the following code, where $m is a Row object:

[php]
$select = $m->select()->order(array('startDate DESC','endDate ASC'));
$tripreportRowset = $m->findManyToManyRowset('Tripreport', 'TripreportMountain',NULL,NULL,$select);
[/php]

If you don't need to order by multiple columns, then don't pass an array...

Posted In: PHP, Zend Framework | 2 comments