Namespaces in PHP? Why *wouldn’t* you want them?

Earlier today I say the following tweet on the-often-down-but-always-interested Twitter from Matthew Weier-O’Phinney:

“What issues to _you_ see with using namespaces in PHP? Start blogging!”

Since I am one who likes to share his opinion on a wide variety of topics, I thought this is a good one to prompt discussion.

Namespaces are expected to be a feature in future releases of PHP. I’m an outsider when it comes to tracking the going-on in the world of PHP internals. So, because of that, I’m not going to comment on the actual syntax of how namespaces will be implemented or it’s impact on internals. That’s not really important as far as this particular discussion goes.

Imagine if you will that you are the user of a web application framework that allows you to easily use third-party libraries by dropping them into a specific location in your application structure. You go to use it and…what the fuck?!? Class name conflicts?!?!? ARRRRRRGGGGGGGHHH. You proceed to post to the mailing list for said framework, complaining bitterly about how you cannot use your favourite library with this stupid framework. Some grumpy guy from Canada flames you in response and it spirals down from there into oblivion.

There are many programming languages that support namespaces, but PHP was not one of them. Why are namespaces so important? They can help to solve the exact problem mentioned in the paragraph above. Why *wouldn’t* you want such a thing?!?! You can have 5 classes with the same name in 5 different namespaces and everything will be just fine. Except for the fact you have 5 classes with the same name. That’s not good design, but namespaces would let you do that.

So, to go back to our little framework example for a minute, the framework could assign itself a namespace (oh, I dunno, like ‘Cake’) and if you happen to want to use a 3rd-party library with your application that has a class with the same name, it’s all good…as long as you didn’t put your own code in the same namespace.

So, to answer Matthew’s original question: I cannot think of any reason to NOT have namespaces in PHP.

Article Tags >>

More XML-DB Fun!

We had a nice corporate retreat last week, an opportunity for all the members of XMLTeam to get together (and in some cases meet face-to-face for the first time) and discuss what's going on and figure out priorities over the next 12 months. If you're expecting some company secrets here, you'll be disappointed. :)

One of the things that got bandied around was whether or not we would continue using eXist to store the XML documents that the application is using or go with a database-driven solution. So, my partner on this project was tasked with figuring out how to update existing elements and attributes in this documents using eXist only.

How can I put this? Oh, I know: traversing through large complicated XML documents to find one element that you need to update when the location could be in multiple places sucks! Part of the reason for this may be my lack of XPath foo within PHP itself. Besides, if eXist can handle this for us then why shouldn't we leverage this? I liken it to being able to do an UPDATE statement in a database. I don't do a SELECT where I grab the whole data set, then drill down through the data looking for one specific row. You do an update with the proper key. So, Paul (that's the co-worker) got to work figuring out for me while I refactored code to be ready for it. He got one script working (yay!) so I hacked away and got a very simple one to work today. Here it is (with some info changed to protect the status of the project:

CODE:
  1. xquery version "1.0";
  2. declare namespace sportsml="http://iptc.org/std/SportsML/2006-10-18/";
  3. declare namespace xts="http://www.xmlteam.com";
  4. declare namespace request="http://exist-db.org/xquery/request";
  5. declare namespace xmldb="http://exist-db.org/xquery/xmldb";
  6. declare namespace util="http://exist-db.org/xquery/util";
  7.  
  8. declare variable $league := request:get-parameter("league", "");
  9. declare variable $league-name := request:get-parameter("league-name", "");
  10. declare variable $team-key := request:get-parameter("team-key", "");
  11. declare variable $colors := request:get-parameter("colors", "");
  12. declare variable $location := request:get-parameter("location", "");
  13. declare variable $name := request:get-parameter("name", "");
  14. declare variable $nickname := request:get-parameter("nickname", "");
  15. declare variable $abbreviation := request:get-parameter("abbreviation", "");
  16.  
  17. declare variable $league-doc := concat('/db/', $league, '/dynamic-resource-file/', $league, '.xml');
  18.  
  19. <dummy>
  20. {
  21. if (doc($league-doc)/sports-content/statistic/group//team-metadata/@team-key=$team-key) then
  22.  
  23. for $team-metadata in doc($league-doc)/sports-content/statistic/group//team-metadata[@team-key=$team-key]
  24. return
  25. update replace $team-metadata with
  26. <team -metadata>
  27. {attribute team-key {$team-key}}
  28. {attribute colors {$colors}}
  29. <name>
  30. {attribute first {$location}}
  31. {attribute full {$name}}
  32. {attribute nickname {$nickname}}
  33. {attribute abbreviation {$abbreviation}}
  34. </name>
  35. <affiliation>
  36. {attribute membership-type {"league"}}
  37. {attribute membership-key {$league}}
  38. {attribute membership-name {$league-name}}
  39. </affiliation>
  40. </team>
  41.  
  42. else
  43.  
  44. return
  45. }
  46. </dummy>

So, this sits in eXist, which is running on top of Tomcat and I send the data in by POST-ing to the script. I was all set to start using cURL for this task but a quick search on the net found this blog posting by Wez Furlong showing how to use streams to do what most people automatically associate with having to use cURL for. I liked it so much I went and refactored some other code that was using cURL to use that stuff instead. It's always nice to find out that something you've been doing can still be done, but with fewer lines of code and *still* be elegant.

I'm off to look closer at streams in PHP 5 and see what other hidden treats are there...

Article Tags >> || ||

More Fun With Capistrano and PHP Applications

So, I need to deploy changes to a work-related project from my laptop to a dev server. I was told that one of our other developers had a solution using shell scripts, but I couldn't figure out a problem with it and didn't want to hack away at his scripts in case I broke something he was depending on. So, I figured it was time to go back to Capistrano and simply hack my deploy script I had used for another deployment to fit this circumstance.

So, off I went hacking away at it and testing it. Then I discovered something: I need to be able to send a password for both running some commands on the remote server and for checking some stuff out of a SVN repository. So, I did some googling and here's what I came up with:

RUBY:
  1. task :deploy do
  2.      run "sudo cp -r #{deploy_to} #{deploy_to}-old" do |ch, stream, out|
  3.           ch.send_data "#{sudo_password}\n" if out =~ /Password:/
  4.      end
  5.  
  6.      run "sudo svn --quiet --force #{checkout} #{repository} #{deploy_to} do |ch, stream, out|
  7.           ch.send_data "#{svn_password}\n" if out =~ /.xmlteam.com's password:/
  8.      end
  9. end
  10.  
  11. task :rollback do
  12.      run "sudo mv -r #{deploy_to}-old #{deploy_to}" do |ch, stream, out|
  13.           ch.send_data "#{sudo_password}\n" if out =~ /Password:/
  14.      end
  15. end

All those #{...} values are simply variables I defined in the recipe file. You don't actually expect me to tell you what my passwords are for access to various machines, do you? :) I remember how difficult this stuff was to do in previous versions of Capistrano, requiring all sorts of hacks to make it deploy non-Rails applications but they removed that dependency with Capistrano 2.0, thus making it possible to use Capistrano with ANY project, not just a Rails one. Although you can use a lot of built-in magic if you use Capistrano to deploy a Rails project.

Now that I know how easy it is to pass data to the remote server, I can actually envision some fairly complicated deployment scripts. Hope this helps out other people who've come here and read my other post about using Capistrano to deploy their CakePHP projects.

Article Tags >> ||

Rails Fan Boys Missed The Point

I'm sure I'm not the only person who has been talking about this, but the response to Derek Sivers talking about how he switched back to PHP after 2 years of trying to rewrite CD Baby using Rails has been so predicatably idiotic. He even hired a Rails expert to help him out. Not what I would've done, but whatever. So, he had the courage at some point to pull the plug on it and got down to work rewriting his app in PHP. 2 months and 10K lines of code later, he was done. 2 years vs. 2 months.

Now, other people have done a much better job of deconstructing the childish, immature responses that basically seem to imply that Mr. Sivers' is an idiot (which he clearly is not) and that Rails is perfect and that he must be doing something wrong. But that's not what I'm here to discuss today. His experience is not atypical in the software industry. Many have been seduced by the promise of a new tool and have failed to anticipate that some of the same problems they were hoping to fix were going to show up no matter how hard they tried. And woe unto anyone who chooses to criticize that technology, as they will be flamed mercilessly for not accepting that the new is better than the old.

Anyway, the guy I feel bad for is the Rails expert Mr. Sivers' hired. He was being asked to shoehorn an existing application into not only a new language but probably a new paradigm. Was the old CD Baby a prime candidate for a rework using a framework that relies heavily upon convention over configuration in order to gain development time? Legacy apps are very hard to rewrite when you didn't create the original. Sure, Mr. Sivers was helping the Rails guy out but there was no way he (the Rails guy) could get this done. Mr. Sivers just knew way too much about all the hackery that inevitably creeps into an app to get it to work and no amount of time could get that info out of his head and into a form that Rails Guy could use.

Of *course* Mr. Sivers could rewrite this thing in 2 months. He knew all the intimate details. How could he *not* succeed? He had thrown away 2 years worth of work but was able to get it done in 2 months by himself. All he really had to do was go back and recognize the mistakes he had made and what was the simplest way to implement the functionality he had already written before. But here's the kicker: he knew how do it better BECAUSE of what he learned while doing Rails.

As I told my friend Kevin, the end result of all my talks on "What can PHP learn from Ruby on Rails" was me being able to stand up and say "there is nothing I can't build in PHP that I can build in Rails, but thanks for showing me all those best practices." That, my Rails fan boys, is the real lesson to be learned. Keep all that "PHP is ugly, Ruby is beautiful" nonsense to yourself and just build something with it. I'll be humming along building PHP sites using all those cool things I learned from Rails. You want to use Rails? Great! I'll be here working on my PHP stuff, let me know when you run into an application-level problem Rails can't solve because I'm willing to bet it's been solved with PHP. Numerous times.

Article Tags >> || ||

A Hopefully Useful Tutorial For Using CakePHP’s Auth Component

One of the parts of CakePHP that caused me the most grief while trying to learn the finer details of the framework was the Auth component. The problem at the time was that there was scant information available on how to actually use and configure it. Luckily, I was able to get some insider information via gwoo and Nate from the CakePHP core team. Plus there was stuff floating around on different web sites. So, here I will attempt to collect some of that information and show you some basic-to-intermediate info on how to use the component. Note, this code is for 1.2.x.x, and the API for the Auth component had not been declared 100% stable at the time of this post. In other words, don't blame me if this doesn't work 6 months from now. :)

So, in order to use this component, you have to add it to the list of components your controller is using. I tend to put this info in app_controller.php since I usually need Auth to work for all my controllers.

PHP:
  1. var $components = array('Auth');

So, now that we want to use it we suddenly have a huge variety of options that can and need to be set. For this example I use code from an existing project, with some info changed to protect the innocent. You set the various Auth options by putting code in your beforeFilter() method for your controller that will be using Auth.

PHP:
  1. $this->Auth->fields = array('username' => 'email', 'password' => 'pasword');

By default, Auth expects you to have a model called User that has a 'username' and 'password' field in it. You can override what fields contain the username and password information though.

PHP:
  1. $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');

You need to tell Auth what controller / action pair it needs to use to present the login form.

PHP:
  1. $this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'index');

Tell the Auth component where the user should be redirected after a successful authentication...

PHP:
  1. $this->Auth->logoutRedirect = '/';

... and where they should go when you logout.

PHP:
  1. $this->Auth->loginError = 'Invalid e-mail / password combination.  Please try again';

You can specify what error message you want displayed if authentication fails.

PHP:
  1. $this->Auth->autoRedirect = false;

Sometimes you don't want to send an authenticated user to the location specified in $this->Auth->loginRedirect because you need to do some work based on the user being authenticated. I've used this in this bit of code that checks to see if we have a cookie that has user credentials in it for logging in.

PHP:
  1. function login() {
  2.         if ($this->Auth->user()) {
  3.             if (!empty($this->data)) {
  4.                 if (empty($this->data['User']['remember_me'])) {
  5.                     $this->Cookie->del('User');
  6.                 }
  7.                 else {
  8.                     $cookie = array();
  9.                     $cookie['email'] = $this->data['User']['email'];
  10.                     $cookie['token'] = $this->data['User']['pasword'];
  11.                     $this->Cookie->write('User', $cookie, true, '+2 weeks');
  12.                 }
  13.  
  14.                 unset($this->data['User']['remember_me']);
  15.             }
  16.  
  17.             $this->redirect($this->Auth->redirect());
  18.         }
  19.     }

PHP:
  1. $this->Auth->authorize = 'controller';

This is a newer addition to Auth, where you can specify some additional information about where Auth can look for more information. Normally, Auth will expect you to have already setup stuff using Cake's own ACL stuff. I didn't need anything as complicated as that, so I was happy to see I could bypass it. When you set $this->Auth->authorize to 'controller', you're telling the Auth component to look for an action called 'isAuthorized' for more information on whether the user is authorized or not. Here's a sample one:

PHP:
  1. function isAuthorized() {
  2.         if (isset($this->params[Configure::read('Routing.admin')])) {
  3.             if ($this->Auth->user('admin') == 0) {
  4.                 return false;
  5.             }
  6.         }
  7.        
  8.         return true;
  9.    }

I assume that example is self-explanatory. Now, if you set $this->Auth->authorize to 'model' then you can create a special method in your User model that can do similar things. Thanks to AcidMax for this post that shows you what to do if you use 'model' for $this->Auth->authorize. There is also a 'crud' option for $this->Auth->authorize, but I have not used it myself. You can use $this->Auth->mapActions to then tell auth what actions map to the various CRUD actions:

PHP:
  1. $this->Auth->mapActions(array('read'=> array('display')));

Like I said, I haven't fooled around with it. Once I do, I'll update this blog post to expand on it some more.

PHP:
  1. $this->Auth->allow('add', 'view');

You need to tell Auth what actions in your controller can be accessed without needing to be authorized. What trips most beginners up is that 'login' and 'logout' are already actions that Auth expects to not needing to be authorized. If you put 'login' and 'logout' in there, Auth starts acting very weirdly and totally bypassing authorization. So, don't do that. :)

Now, there are also some other features that are useful to those using Auth:

PHP:
  1. function logout() {
  2.         $this->Session->setFlash("You've successfully logged out.");
  3.         $this->redirect($this->Auth->logout());
  4.     }

$this->Auth->logout will return the URL to whatever location you told Auth you wanted people to be sent to upon them being logged out.

PHP:
  1. $userId = $this->Auth->user('id');

You can retrieve info from the User model if you've been successfully authorized. Whatever fields are in your User model can be retrieved. Wouldn't it be nice to do stuff like this:

PHP:
  1. if ($this->Auth->user('admin') == true) {
  2.      $this->Session->setFlash('You are an admin');
  3. }

Phew. That's a lot of stuff to try and keep in your head when building apps using Auth. But there's still more. The Auth component will automatically hash whatever value you place in the 'password' field in your form. It will do this both upon login *and* if you are creating a record in your user model, so you need to make sure that your field in your table that stores the password is large enough that it will accept the hashed password. If you need to do something with that password before you store it, call the field something other than what you've told Auth your password field is, then you can get the value needed to store it by using $this->Auth->password('passwordtostorehere').

Anyhow, I'm sure I've missed some other more esoteric stuff, but I hope I've done a good job of showing just how easy the Auth component really is to use...once you understand how it actually works. :)

Want to advertise on this blog? Send email to chartjes@littlehart.net
GTcars Canadian Car Audio TurboDodge Car For Sale Sign
Audi Forum Mustang Forum Dodge Intrepid Miata Turbo
GTscene Pontiac Bonneville


@TheKeyboard is Digg proof thanks to caching by WP Super Cache!