My Ideal Development Setup
I finally took the plunge and got a new laptop (with the kind help of my parents with a generous Christmas gift). It's been a few years really and I did need a new computer. What did I end up with? Well, I almost got a Macbook Pro (of course) being the good web developer (and graphic designer) that I am. Apple finally came down on their pricing a while ago so I was all ready to do that but then saw this HP ENVY ended up being half the cost for better hardware. Apple needs to put a 1GB graphics card in their Macbook Pros, 512MB on a $3,000 laptop is just pathetic.
So reasoning aside, how was I going to setup this machine for web development, design, graphic design, and photography? Prior to this computer, I had dual boot Windows (Vista, ick) and Fedora Linux. Awesome, it ran fast it was simply to setup a webserver and it basically mirrored the servers I worked on. Photo editing was tough in Linux and forget Adobe (though there is a portable version of Photoshop that works); GIMP was the real tool. So I had to go back to Windows for any design work. Not that I do a whole lot these days and that's more because I've learned to deal w/o those nice programs and get by with GIMP (and GIMP is good don't get me wrong).
Now this time around I decided that since I have 4 cores (8 virtual) why not run virtualized machines? No more dual booting, no more hard drive partitioning, etc. So I setup Fedora 14 virtualized, and here's in my opinion, the very best setup for web development/design that you can get (and you can do this if you have a Mac too):
Base Host OS: Windows 7 (or OS X)
Virtualized Guest OS: Linux and Windows (or OS X, oops yea you can virtualize this, but its slow, that's ok though read down further why)
So you virtualize Linux so you can setup a web server that's practically identical to the ones you'll be uploading your files to for the live sites. Why setup a web server with Mac ports, homebrew, or MAMPP? That's silly. Why use XAMPP or setup IIS if using a Windows host? Oh god no IIS. OS X is better but at the same time, you just can't beat a Linux package manager for speed and ease (and grabbing all the libraries you need when you do need to compile something). Also, you really want to mirror as close as you can the actual server that your files will live on because then you'll run into less accidents. You know that everything will work, not "should" work. Rolling with updates is also much easier.
So the trick is you have to make sure your virtual machine has internet access and it's IP is static. With VMWare and Virtualbox you can set things to NAT and then within the guest OS (in Linux' settings for the network card) you can assign a manual IP address within the range of allowed addresses. I have mine at like 192.168.126.200 or something. It won't conflict with any other machine on my LAN. It's not even in the same range.
You will want to probably disable SELinux since you aren't too concerned with security and it'll make things a lot nicer when it comes to shared folders. Make sure your firewall settings are letting Apache through as well. You'll want to setup a shared folder so your files are hosted on your host machine. So in my case Windows 7 holds my web site files. I have a different folder for each site and the Apache conf settings uses virtual hosts to assign a new domain to each site/folder. You can Google all of this. I don't want to make this a tutorial (unless I get some requests) but just a general idea for how to set things up. Pointing the Apache conf to the location for the shared folders. Awesome. Now you can edit files on the host OS with the editor of your choice, no problemo. Use git, svn, etc. The most important thing about this is that your files are now always available whether running the virtual machine or not and they are a bit better protected. Say your virtual machine gets corrupt somehow? Oh crap! Well, not anymore. Now it's just setting it up again worst case scenario, a few Apache config files lost. No big deal.
How do you access this webserver from the host OS? could setup a DNS server (like BIND) if you were nuts. Then set your host OS to look at that DNS before going out to your ISP. I personally didn't want to do that because I figured it would make things just ever so slightly slower. Then if Linux wasn't running, maybe even slower until things timed out before moving on to the next DNS. Instead, I alter my hosts file. In Linux this is easy because Gnome (and I'm sure other desktops) has a GUI to add entries to your hosts file. Very nice, but wait just one minute! There's an app for that! Windows and OS X both have 3rd party apps that will allow you to change up your hosts file in a nicer way. The one on Windows 7 that I'm using now is called HostsMan. It's actually quite cool and can provide some other handy features to help warn you about possible hijacks and such. Anyway, I just use it for my sites. So I have a lithium.local for example that points to the IP of the Linux virtual machine. Save that...Open up the web browser and bingo! Again, there's apps for OS X too and some that also work in your dashboard I believe, which is handy.
Yes, I do need to add a new setting (line) each time I setup a new site, but I have to also do that in Linux under the Apache configuration for the virtual host anyway. No big deal, it takes less than 5 minutes one time every time I setup a new site. I could figure some sort of automation script maybe or something through the web browser. You could setup a free control panel like you see on hosting providers...But I'm ok with typing in the config manually from a terminal.
Bonus! I don't need to use Putty (or Kitty) because I can use the terminal from Linux. Putting the virtual machine into "unity" mode, or "seamless" mode for Virtualbox users, will make this so much nicer. I basically now look at things and get confused...Am I running Windows? Or Linux? Nice, best of both worlds.
Now, virtualizing OS X isn't kosher. It's a no no. But...You can actually do it. I find that I can just test with Chrome and Firefox and be ok. I don't use OS X. There's also a Safari for Windows (though it's slightly different I hear). However, Firefox and Chrome are pretty good and I don't typically end up with OS X only CSS issues. Conversely, if you're doing this in OS X, you can virtaulize Windows XP or 7 or ME? Vista? No no, you'll be ok with XP or 7, haha. Now you have two virtual machines giving you ALL of the major operating systems. Why do this? Well, dual booting is a pain in the rear and it doesn't matter how fast your Windows or OS X guest runs because you're just going to use it for browser testing. Microsoft used to have images available just for this purpose. They were incomplete operating systems, but gave you just enough to test browsers. Personally I think it was genius, but also...They owed it to the world because of Internet Explorer.
So there you have it. My idea of the perfect setup for a web developer/designer. I can run all of the programs that I want to for design (since Linux isn't great for design of course) and also have my proper webserver with Linux. There's also a few helpful apps that Linux has that neither OS X or Windows has so now I won't miss out on those. I can test just about every web browser and make sure my site looks the same on all. The things we have to go through in order to make web sites. It's absurd...But at least we don't need 5 computers to do it. Running two operating systems full time is not a big deal either on any newer i5 or i7 processor. Also as an added bonus I can play Starcraft 2 :)
Yes, Apple fanboys will say they can do it all with just OS X...Well, no you can't actually. You can't test all the browsers without virtualizing and your web server is just going to be a joke running on OS X. I've setup several web servers before on just about every operating system short of Solaris. So believe me, you want the "real deal" you want something that's going to reflect the final live environment. You don't see hosting companies offering up OS X to run your web sites now do ya? Also, it's good to have your web server virtualized because it doesn't take a lot of resources to run and it will familiarize you with a Linux machine. That's important! It's also still just as convenient as setting up (through port/brew or compiling from scratch) a web server on OS X. In fact, it's more convenient. While MAMPP is probably the most convenient, it doesn't offer enough when it comes to all those PHP extensions. Now this is strictly speaking to PHP developers...Now how about you want to run other things on the server? Some things simply aren't available for Windows or OS X, and that's why you really want to virtualize Linux for your web development. Plus, you can have multiple machines and simulate/setup/test out load balancing and database clustering...But of course, to each their own.
Decompression: Discoveries and Current Projects
So I want to make sure that I keep posting content on my blog. I not only want people to come back to my site, but I want to get in a good habit of writing as well as make sure that I'm jotting down some of my thoughts. The amount of crap that runs through my head is probably more than the average person's. That's not a pat on my own back, that's actually quite sad because I just want things to turn "off" sometimes. Sleep deprivation, lack of focus sometimes, and overall insanity is really what it leads to. Sticky notes and endless reams of printer paper, and sketchbooks (not that I sketch anymore like I should) really aren't cutting it. I need to decompress on my blog as well. So I'm adding some new categories to help stay organized.
Believe it or not, I use my own blog for reference. I do come back to what I wrote down and use it to copy and paste code snippets and keep tabs on where I was and where I am now. Sometimes I rant sure and those posts may not do any good for anyone...But I'm bored, and probably angry at the moment. Or, maybe I'm procrastinating. Like I am now...It's about the middle of the evening and I should be working on my little lightweight CMS but instead I'm writing...Hmm...Oh well.
So in the spirit of keeping it interesting for you all, check this out! Have an Android phone? Remotedroid can be found in the app market, but the server can be found on its Google code site. It's cross-platform, just run the jar file. Make sure that you have port 57110 open for UDP traffic (check computer firewall and router).
You can hold down on the track pad area with one finger and swipe the other up and down and it should scroll. It may support some other gestures too, I haven't tried. It works really well as a mouse. The keyboard I found to be a little slow, but bearable for simple things like surfing the internet or perhaps your media center computer (ie. Boxee, etc.). That's really what I intend to use it for. Sure, there's a Boxee remote app for nearly all phones, but this works much better in my opinion...Especially if you want to do more or Boxee crashes or you don't run it all the time. Boxee seems to get a weird resolution change issue after my computer wakes up...So I have to exit it and restart it. Can't do that with the remote app. However, the remote app does have a novel picture of whatever is playing.
Anyway, a nice little discovery. What else? Well, just boring stuff. Things that I'm working on as I mentioned...A lightweight CMS. It's coming along well. It was the basis of the previous post about including external JavaScript files from another JavaScript file. This lightweight CMS doesn't use any framework...Or database even. It's intended for very basic, static, sites. Old sites. Imagine those sites for small businesses that were designed a while ago, or not necessarily a while ago, but perhaps designed very statically...This is quite common actually even in a world where we have Wordpress and Joomla and Drupal and Croogo! Don't forget that nice CMS. One day add Minerva to that too. Another project of mine for those of you who want to laugh, go ahead...But I promise that one will get finished as well.
I'm calling this lightweight CMS "Argos" with the metaphor/slogan of "Who says you can't teach an old dog new tricks?" So these very simple (1-20 page) sites are really the target. You can't use Argos on a dynamic site. Argos actually writes directly to the HTML or PHP page and alters the HTML contents of it. It of course stores data in JSON files to keep backups (also for historic rollbacks) and also backs up the entire site on installation (well, it will when I'm done). This ensures that the site using Argos doesn't get messed up and also helps to prevent user mistakes...Or rather, allows a user to "undo" things...Something I think that's missing from many CMS' out there. It's also designed to be quite compatible. Basically PHP 5 is the only real server requirement. It has to have the JSON PHP extension. That's the extent really of the requirements. Of course many shared hosts do offer MySQL but I figured I'd keep it as compatible as possible. Plus, do I really want to setup a database? What if I'm not installing the CMS? I want to deliver it with one PHP script. Let it download the files it needs and walk the user through installation. I'm assuming that they don't know what MySQL is and they certainly don't know how to setup a database and then a user to access said database.
The CMS does have a backend, but it's very simple for now. It does include a nifty file manager script that I found. So, there's another great find for you all...phpFileManager. It's just one PHP script actually, it works really well! I was thinking about expanding upon it and adding a few features and then just using it as a "swiss army" knife for web development. Adding things like markItUp! to it and so on...But keeping it all one file. It doesn't matter if it ends up being a few megabytes even...Just being able to get onto a server, wget the file from my server or FTP it somewhere, then load it up to go work on something in a pinch would be great.
Anyway, enjoy the discoveries and updates. I do post minor things like this on my Twitter feed by the way, so follow me!
Including External JavaScript From Another JavaScript File
So I was looking around the internet for a way to include external JavaScript files in another JavaScript file. After a little bit of searching, the goal was clear...Use AJAX (XMLHttpRequest) to pull in the contents and then run evil on it. Err, I mean eval()... I definitely feel like it's wrong and against all the good rules we know, but it works and I think it's the only logical way to do it. Why on earth would one ever do it? Well, typically you wouldn't. Typically you'd just include your scripts on the HTML page...However, in a current project for a client I wanted to include one JavaScript file...Yet include jQuery and other scripts in that file. The reason being -- simplicity. It won't be me that will be including the JavaScript on the page(s) of the final site. So I don't want the (perhaps non-savvy) person to have to include all these scripts.
Reason number two. I don't want all this JavaScript loading for every person that hits these pages. It's actually a tool to maintain content on the site (let's just say a lightweight CMS). These tools are for editors only, despite that it won't do anything harmful if anonymous visitors hit the pages, it's still something that normal people don't need. It would technically slow down the page load time. However...Not if the embedded script is small and it selectively (based on "who" is loading the page) loads these external scripts. What if the site doesn't use jQuery? Why include jQuery and all this other stuff just so the client can edit the page? That sounds silly. Why is the JavaScript needed in the first place? Well, the CMS uses it for "edit in place" abilities and I have to assume the page is an HTML file so JavaScript would be the way to go.
So eval() isn't quite evil in this case...It's actually our saving grace. Of course I could have made a "bookmarklet" for the editor to drag onto their page which would load up the editing tools. This would eliminate the need for any script to be included on the page and the normal visitor wouldn't be able to tell that there was any CMS powering the site (except for perhaps some subtle clues in the div tags)...But, I don't think bookmarklets are that great of a work flow. I don't think they are obvious enough for the non-savvy user. They are a neat trick sure enough, but it's an awkward process. They are a work around themselves...So what's the difference?
So then, how does one include external JavaScript files from another one? Like so:
function IncludeJavaScript(sURL) {
var oRequest;
// if using a normal browser
if (window.XMLHttpRequest) {
oRequest = new XMLHttpRequest();
} else {
if (window.ActiveXObject) {
// if using IE 6 for some terrible reason
oRequest = new ActiveXObject('MSXML2.XMLHTTP.3.0');
}
}
oRequest.open("GET",sURL,false);
oRequest.setRequestHeader("User-Agent",navigator.userAgent);
oRequest.send(null)
if (oRequest.status==200) {
eval(oRequest.responseText);
} else {
// alert("Error executing XMLHttpRequest.");
}
}
// Now call the function, passing the script, here I'm including jQuery
IncludeJavaScript('/js/jquery-1.3.2.min.js');
Note that you will want the 3rd parameter of open() to be false so that the data loads synchronously. If it was asynchronously loaded, the rest of the code would execute and you'd probably have some undefined function errors and things not working. Another thing to note is that the external JavaScript MUST be on the same domain. JavaScript has a cross-domain security restriction. This is a good thing. You may be able to get around this though by having say a PHP "proxy" script on your server that grabs the contents of an external URL (the target JavaScript) and simply prints the results on the page.
Believe it or not, I found many incorrect answers to this problem out there so I hope reposting some of this research is helpful to someone else. I've tested this in Chrome and Firefox. I'm assuming it works in others too...Well, at least a modern version of IE.
PHP Frameworks and How Stuff is Built
Been a while since I wrote a post, I've just been swamped. I've been working on a new (big) project at my day job and was away in NYC for a week and just crazy busy. This new project got me to thinking though. I really would like to compare frameworks. Ok ok that's a dead horse but I don't mean in terms of performance. I don't want to go download a bunch and say "see! see! that one is better! because it's faster on my inaccurate benchmark test!" No, instead I really want to outline how things are built using these frameworks. I've made the decision to kinda stick in one camp of thinking. I've chosen CakePHP and Lithium for very particular reasons... I like how these frameworks work and the direction they are going.
Is it better? I think so, but I can't say it will make your app any faster. Again, I'm not looking at performance. What am I looking at? Things like how easy it is to learn the framework, how many features it has ready to go out of the box, how fast you can build things, how easy is it to work with a team using these frameworks, and so on. This is very important and people always skip it. Honestly, it's saved the company I work for literally thousands and thousands of dollars in man hours and helped the developers there greatly in terms of their stress level and skill level.
That said. What am I comparing? Well right now I can only really compare Lithium, CakePHP, and Symfony. I haven't really used any other frameworks in enough depth to compare. I understand some of their fundamentals and I know they exist, but without actually doing a project, it's hard to compare. That's what I also don't like about other comparisons you'll find out there. They just simply don't know how to use (or optimize) the framework so of course their benchmarks and opinions are going to be skewed!
CakePHP
CakePHP is perhaps the fastest framework I've seen (or heard of) to build web sites/apps with. You simply can't compete with it's "bake" feature and how much it provides for you out of the box and how many snippets and addons (helpers/components/behaviors) that you'll find for it.
Can it scale? Yes. Does it have the smallest "footprint" out there? No. I don't care about how it performs though because it can be used on large scale sites and it's very fast to develop with. It gets stuff done. Period. That's the basis of the entire PHP language. If we want to work toward some super efficient and fast performing application, we probably aren't going to use PHP. While PHP is become better and faster, it has always been the "get stuff done" language.
CakePHP has been the primary tool that I've used for several years now. The Croogo CMS (that I use on this site) is built on CakePHP and it's a very good CMS. It's flexible and since it's built on CakePHP, I immediately understand how to build plugins and work with the CMS. The idea of these "standards" and "convention" is what makes CakePHP a very solid framework. It also makes it easy to learn. The documentation for CakePHP has become very good. There were some somewhat valid arguments against the quality of documentation back in the CakePHP 1.1 days, but since 1.2 everything has blossomed.
CakePHP's approach is to "hide" things from the developer. You are presented with many helpers and classes to help you get stuff done as well. There is also convention and by following it, you can build web apps extremely fast. You might call it a "black box" you just put stuff in and voila. However, you can override everything and expand upon things. You can extend classes and add (nearly) anything you need to. Learning more of the advanced practices with CakePHP will take time, but because it's a friendly framework you can start simple and overtime get more advanced. Perfect.
Symfony
Symfony on the other hand is what you might call a "white box" framework. Everything is there and you have to do everything. Doctrine really helps automate things and the framework's use of YAML also automates things, but you still must configure it. So yes, some classes are generated for you automatically, but "how" you work with the framework is very much a known and open process. Making queries is only slightly different than CakePHP, but you have to type more. In general, there is a lot more actual coding that needs to be done with Symfony.
So it's a configuration monster and there's a million classes, files, and folders all over. It's extremely hard to follow (thank God for "Go To Definition") and the API documentation is terrible. So don't expect any help there. It does have a few decent "books" to follow for learning the basics of the framework.
This framework is very "exploded" for lack of better words. It starts you off with and kinda forces you to extend and abstract nearly everything. This is very good for very complex applications. This makes working in teams great because you probably won't bump heads often and it gives you a lot of flexibility. However, like I said that isn't to say you don't have the same flexibility with CakePHP...It just means you are starting off in a different direction.
My analogy is like both CakePHP and Symfony are PHP frameworks. Ok, so say a deck of cards. 52. CakePHP is that deck of cards in a box stacked and in order. Symfony is the same exact 52 cards in a bag scattered. You can play a game of cards or you can build a card house with both. The difference is if you want to easily find a single card, you're going to have an easier time with CakePHP.
That isn't to say Symfony is disorganized, but it's just this giant piece of IKEA furniture that you need to put together. That's another great analogy. You look at the exploded view and you're like WTF? Then at the end of it, you have a ton of left over screws, but perhaps an awesome looking piece of furniture.
Configuration is nice but my personal feeling is that Symfony is overkill. Oh and using YAML when you can just use PHP arrays/objects is just silly.
Lithium
Lithium, the new kid on the block. It's almost unfair to compare since it's built on PHP 5.3 and as a result can use namespaces. Namespaces are critical to a framework for organization and integration with other classes (and frameworks). However, Lithium has many of the same fundamentals that CakePHP has. Obviously no surprise if you know the people involved.
Again, I like this way of working. I like the way classes are named and how the code looks. I feel it's comfortable and efficient. However, Lithium is still young and there aren't nearly as many classes for it as there are for CakePHP. I wouldn't expect that to change too much either though because the goal of that framework is to not add a lot of bulk. It's also designed to work well with other frameworks. So you can use libraries from other frameworks...And this is where the "speed" comes back in. Since you will have more options of "what" you can use, the byproduct will be speed in terms of how quickly you can build something.
Lithium's documentation isn't the greatest of course because it's a young framework. However, it does have a very unique feature and that's the li3_docs plugin...That will basically go through your code and parse your comments to create API style documentation for you! Along with embedding code snippets. This is extremely important for working with a team of people. Also great for your own personal reference.
Lithium also has its own test suite built in. Another killer feature. It makes testing much easier because you don't have to setup a bunch of stuff. You're just immediately ready to go.
Lithium is very well organized, easy to follow it's classes and the API documentation is actually already good. It's far better than Symfony's API documentation. What Lithium doesn't have is a nice "book" that CakePHP and Symfony has...But give it time.
Perhaps the most unique feature Lithium has though that the other frameworks (and any that are not PHP 5.3) won't have is the filter system. I've written a blog post about it before, but essentially it's the idea of "aspect oriented" programming. It allows you to "tap into" the chain of events from other areas of your application. Used responsibly, this helps you overcome some of the problems that come up when developing applications using these frameworks. We've all been there...You know, when you say, "Oh I wish I could just do this here, but I can't get at this or that" ... or the best one, "I could...but I don't want to 'hack' the core."
Conclusion
So the winner in this round up? There is no winner. It's your preference. Again, I'm not trying to say which framework is better. My personal preference is Lithium right now because of the listed reasons above. It's also fast. Mad fast. That's important to me, but it's not important to a small site. You may want to use CakePHP to build a simple blog (or use Croogo which gives you most of what you need in 5 minutes) because it's simply faster to do the work and at the end of the day, you'll be able to keep up with traffic demands.
If you were to benchmark the frameworks, of course Lithium will blow the others out of the water, but again it's unfair to perform that benchmark when Lithium runs on PHP 5.3.
Symfony, ah...Symfony. I hate it. I'll be honest. However, it does have it's merits and value in a team environment. Is it better for working in teams than the other frameworks? No. We have revision control. Even without revision control (which you need regardless), it's still not "better." It's just different. If you prefer to work with a million little pieces and configure every tiny detail, then you will probably like Symfony. That doesn't make it "wrong" to use. It can scale and be used to do the same things that other frameworks do.
So at the end of the day, you have to look at it as a personal preference. However, I think there are definitely distinct "camps" for "how" we build things and my preference is in the CakePHP and Lithium camp. Obviously I'm busy, I'd love to learn different frameworks to compare those...But I won't have the time. So feel free to leave some comments on your experiences and preferences!
Facebook Connect & Lithium
It seems to be a week full of different authentication methods here. Last I wrote about an LDAP Auth class adapter for Lithium that I wrote and now I'm going to go over using Facebook Connect with your Lithium project. As it turns out, it's also very easy and you can put everything you need within a library to keep things modular.
First things first, you'll need the Facebook Connect PHP SDK....It also wouldn't hurt to grab the JavaScript SDK. You'll probably end up using it. Continuing here with "step 1" you're going to alter that single PHP SDK file. I know, I know...Hang on and listen. When we use Lithium, we really like namespaces. I really pray for the day that all PHP libraries we pick up are namespaced, but that just isn't a reality yet. Fortunately the Facebook PHP SDK is very small. It's just one file with a few classes. Cool! So within that you'll put up at the very top: namespace facebook; Then you'll also need to put a slash (\) in three places. That's where you see (also up top) new Exception. Ensure those instantiations read new \Exception... Also where the FacebookApiException extends Exception, make that FacebookApiException extends \Exception. Alternatively, up top put: namespace \Exception; and you should also be ok. So you're really only talking about 2-4 small tiny itsy bitsy changes. So you can relax.
I'd put this into a library, right? Why not? So within your app\libraries folder make a "facebook" folder and rename that "facebook.php" file you just downloaded and make it "Facebook.php" because it's just not going to work otherwise. Ok? All good? At this point you have a "app\libraries\facebook\Facebook.php" file that has a few tiny tweaks to use namespaces. Cool? Ok, on with step 2.
Now let's setup our authentication in a pretty common fashion. We're talking about applying filters during the framework bootstrap process. Create a "config" folder and add a "bootstrap.php" file, so you have a "app\libraries\facebook\config\bootstrap.php" file. Within here is where you'll be applying some filters to setup the authentication for your site. Now, if you've done this before, it's going to be fairly familiar. I'm going to paste an example that will provide you Facebook Connect authorization only. It's different than what I'm using because I'm allowing people to login using accounts in my local database or with Facebook Connect. I also create a new user record upon logging in with Facebook Connect. That way users can create their local profiles just the same whether they registered with my site or used Facebook Connect bypassing a registration process. Your needs are most likely going to differ. If not, then maybe I can be convinced to wrap up a complete "User & Auth" library. Part of this is going to look exactly like the example they provide along with the FB PHP SDK. So, here we go:
use \lithium\storage\Session;
use \lithium\security\Auth;
use \lithium\action\Dispatcher;
use \lithium\action\Response;
use \facebook\Facebook;
use \facebook\FacebookApiException;
Session::config(array(
'default' => array('adapter' => 'Php')
));
Dispatcher::applyFilter('run', function($self, $params, $chain) {
// Create our Application instance (replace this with your appId and secret).
$facebook = new Facebook(array(
'appId' => 'XXXXXXXXXX',
'secret' => 'XXXXXXXXXXXXXXXXXX',
'cookie' => true,
));
$session = $facebook->getSession();
$me = null;
// Session based API call.
if ($session) {
// Write the session
Session::write('fb_session', $session);
try {
$uid = $facebook->getUser();
$me = $facebook->api('/me');
} catch (FacebookApiException $e) {
error_log($e);
}
}
// login or logout url will be needed depending on current user state.
if ($me) {
// This will come in handy later
Session::write('fb_logout_url', $facebook->getLogoutUrl());
// So set the Auth and pass along (in the session) the data from FB API
Auth::set('user', $me);
} else {
// Again, this will come in handy (unless you're using the JavaScript SDK)
Session::write('fb_login_url', $facebook->getLoginUrl());
// If no FB session, clear any local session we may have set
Auth::clear('user');
}
// Here's one way of locking out different actions based on login status.
$blacklist = array(
'pages/index',
'pages',
'recipes/create',
'dashboards/my_account'
);
$matches = in_array((string)$params['request']->url, $blacklist);
if($matches && !Auth::check('user')) {
return new Response(array('location' => '/users/login'));
}
return $chain->next($self, $params, $chain);
});
That's pretty much all you need (in the least) in your bootstrap process. Step three; we're going to place a Login and Logout button on the site. So we need to go to the view template of your choice (could be a user's controller's "login" action or could be elsewhere). Let's say it's the login action. Again, I recommend using the JavaScript SDK as well. I'm also going to assume you'd be using jQuery. Here's what our template might look like:
<?php $html->script('http://connect.facebook.net/en_US/all.js', array('inline' => false)); ?>
<script>
// initialize the library with the API key
FB.init({
appId : 'XXXXXXXXXXXXXXXX',
session : <?php echo json_encode(\lithium\storage\Session::read('fb_session')); ?>,
status : true,
cookie : true,
xfbml : true
});
// fetch the status on load
FB.getLoginStatus(handleSessionResponse);
$('#fb-connect-login').bind('click', function() {
FB.login(handleSessionResponse, {perms:'read_stream,publish_stream'});
});
$('#logout').bind('click', function() {
FB.logout(handleSessionResponse);
});
$('#disconnect').bind('click', function() {
FB.api({ method: 'Auth.revokeAuthorization' }, function(response) {
clearDisplay();
});
});
// no user, clear display
function clearDisplay() {
$('#user-info').hide('fast');
}
// handle a session response from any of the auth related calls
function handleSessionResponse(response) {
// if we dont have a session, just hide the user info
if (!response.session) {
clearDisplay();
return;
}
// if we have a session, query for the user's profile picture and name
FB.api(
{
method: 'fql.query',
query: 'SELECT first_name, last_name, pic_square FROM user WHERE uid=' + FB.getSession().uid
}, function(response) {
var user = response[0];
$('#user-info').html('<img src="' + user.pic_square + '">' + user.first_name + ' ' + user.last_name).show('fast');
});
}
</script>
<div>
<button id="login"></button>
<button id="logout"Logout></button>
<button id="disconnect"></button>
</div>
<div id="user-info" style="display: none;"></div>
That's just about all you'll need. \lithium\storage\Session::read('fb_session'); is going to hold all your session data from Facebook, but you'll probably want to be using the JavaScript SDK to actually do anything useful with that information since it will be limited. Don't forget that facebook limits the information that you are allowed to store locally within your database. You of course could also continue to use the Facebook PHP SDK all over your site to get this information.
If you don't want to use the JavaScript SDK, you'll have the login URL set in the session data from the filters you applied. You'll grab it from: \lithium\storage\Session::read('fb_login_url');
Of course you can get more complex...You can setup a bunch of ACL based on your needs and make users perform other final registration tasks or you can, like I did, combine this with your User's model and allow people to register and login without Facebook Connect or login with Facebook Connect as an alternative.
Lithium and LDAP Authentication
The Lithium framework for PHP 5.3+ is without a doubt my favorite framework (which is evident of course if you've read my blog). I was trying to get LDAP working with Symfony 1.4 and it's sfGuard plugin without a whole lot of luck. While I'm sure possible with more time (perhaps quite a bit), I gave up and just used Lithium because it was much much easier.
The adapters that Lithium uses for various classes are really key. If you wanted to make an adapter for the Auth class, you can really do so without much trouble. Take a look at the current Form adapter as an example. The bulk of what is really required for an adapter is a return from the check() method of false or true or some data. The data returned can then be cached into the user's session data. So basically just returning an array for example will set that data to the session and authenticate the user. Returning false, doesn't. Pretty easy!
Of course there's a lot that might need to go on from A to B but, understanding that, you can make an adapter for just about anything. So I got to cracking on the LDAP authentication. It's a little odd because we aren't logging into a database to then check some records to see if it matches what the user passed. LDAP kinda has all of it's authentication process within itself. You just basically need to pass to it the username and password and it'll return true or false. If it's true you've just bound a connection and can perform searches, etc.
I'm also working on a datasource for LDAP that will really dive into all the CRUD operations. However, for our basic authentication needs all we need is to be able to connect and retrieve the record for the account used to login. Simple.
You can grab the adapter from the Lithium lab. You'll put it under app/extensions/adapters/auth/Ldap.php. Then you'll want to use it pretty much like you'd use the Form adapter. Here's an example configuration:
use \lithium\storage\Session;
use \lithium\security\Auth;
// This is just for an example, you may run with a different session adapter (I keep mine in MongoDB for example)
Session::config(array(
'default' => array('adapter' => 'Php')
));
// Here's what you're after.
Auth::config(array(
'user' => array(
'adapter' => 'Ldap',
'server' => array(
'host' => 'ds1-sjc.entic.net',
'basedn' => 'o=entic.net',
'targetdn' => 'ou=People'
),
'fields' => array('uid'),
'session' => array(
'options' => array('name' => 'default')
)
)
));
What's the entic server? It's an actual LDAP service, a free one (well, paid services too). They let you store some records as one of their services, kinda like a giant Rolodex. I'm not really sure what the status of their project is, but I do know it was one way to test the LDAP for free. They actually have a few interesting services hosted "in the cloud" (and they are working on some more) so you may want to give them a look.
So what is going on here? Well, we have our host then we have the "basedn" which is typically o=something. It could also commonly be something like: dc=example,dc=com. Anyway...Then we have a "targetdn" which is basically just some more properties to tack on. I've left this key separate purely for organization reasons and who knows, you may need to alter the property for some reason independent of the basedn value. It could have been defined under the basedn. Your distinguished name requirements are going to vary based on LDAP server. Then you have the fields key which is yet again more properties to append to this long DN string that's required to login. In the case of this server it's "uid" and again this list of fields (which also exist in your login form) are pieced together to form the DN. Since we aren't going to present the user with a form to enter this long DN in...We just have the "uid" field exposed in our form. We could have shown some other fields maybe as a drop down option for a group. Again, this depends on your LDAP server's configuration. Now we also need to pass from the form a "password" field. It won't be appended to the DN, but it is of course passed along to login to the LDAP server. Here's what our login view template looks like:
<?=$this->form->create(); ?>
<?=$this->form->field('uid', array('label' => 'Username')); ?>
<?=$this->form->field('password', array('type' => 'password')); ?>
<?=$this->form->submit('Log in'); ?>
<?=$this->form->end(); ?>
I'm not going to go over in great detail how to setup Auth. I'm assuming you've used the Form adapter before. However, if you haven't ever setup auth before, you'll want to put that first Auth::config() part somewhere in your bootstrap process. You'll also need to protect methods in one of many manners. You might apply a filter to the Dispacther's run() method for example in your bootstrap right after the Auth::config() part. You're going to be calling Auth::check() to assist with whatever you do to protect parts of your app. So let's say you also have a controller (let's say UsersController which is quite common) that has a login() that looks something like this:
public function login() {
if($this->request->data) {
$user = Auth::check('user', $this->request);
}
if($user) {
// Might want to redirect somewhere else
$this->redirect('/pages/login_successful');
}
$data = $this->request->data;
return compact('data');
}
That's all there is to it! So now when you to go /users/login and use the form to login. What will then happen is upon a successful login (bind) to the LDAP server, the account record will be retrieved and set to the session data. So when you call Auth::check('user'); you will be returned with an array of data. You can use this to do whatever you need.
Pretty easy, right? This is strictly authentication, you aren't writing anything back to the LDAP server. That's what you'll need a datasource for and I promise to share that as well. It's currently under construction. Again, you can grab the LDAP Auth adapter here.
MongoDB & Twilio Contest!
Being the recent MongoDB convert that I am, I stumbled upon this neat contest. MongoDB and Twilio are giving away some goods this week for the winning application. You need to make the best use of MongoDB and Twilio.
Pretty neat, and what happened in this process is that I learned about Twilio. Twilio is a really cool service that lets you handle phone calls and SMS instructions within your code and then return responses and perform actions. All for a really great cost without any strings attached. So for example, you could have a system where you send a text message (or call in and choose an option) and make it restart your web server or maybe report statistics back to you. Hmmm sounds like maybe a winning idea for an app!? Don't go stealing my idea, but if you do, type quickly, the contest is over at the end of the week. I personally probably will not get around to submitting to the contest, but I really like the MongoDB swag bag...Screw the netbook. I want that bag!
Anyway, be sure to give it all a look, in the least, you might learn something new and find a very valuable tool.
MongoDB Queries with Lithium: Part Three
This will probably be the last part of the series, unless I find some other type of query that may not be so obvious or that may be new in a future release of MongoDB. This will also probably contain the most obscure and complex examples.
Regex With $or
So, on with the good part first. You probably want to create some sort of search for content on your site, be it on the front end for visitors or the back for only admins...It's a common thing for a site and while it's always a good idea to use something like Lucene/Solr/Sphinx if you expect complex searches and lots of them...You can make a quick and dirty search with most databases such as MySQL via a FULL TEXT search. Well, MongoDB does one better and takes advantage of regex, which I posted about in the last article for this series...But this time we're going to use a new operator, the $or operator in conjunction with regex. This is new in MongoDB 1.5.3, so ensure that you at least that version.
What's the $or do? Well, the $or is important because if you make a query with regex or not and want to search multiple fields, you will have all results from both matches be returned. The $or keeps you from getting duplicate results. If you remember in part one we used $elemMatch when dealing with matching multiple conditions within objects. In this example, we aren't working with complex objects, we're just saying go find a regex pattern in the title or the body of some document. Then again, maybe you will need to also apply what you learned about $elemMatch in the previous article with something like this. Right, so get on with it.
// I'm making one call to MongoRegex() but you may need more.
$search_regex = new \MongoRegex('/'.$this->request->data['query'].'/i');
$conditions => array(
'public' => true,
'$or' => array(
array('title' => $search_regex),
array('directions' => $search_regex)
)
)
);
// Now you can put the query together (or define $conditions inline with the find() call)
$recipes = Recipe::find('all', array(
'fields' => array('title', 'url'),
'conditions' => $conditions
));
My head exploded at first, but it's quite easy to follow along with. A little tip? Reference the MongoDB documentation, make your queries in a console window and when you get back the results you want...Take the text you entered and print it out on your page where you're making the query. I just print out the guts between the parenthesis. Then with your $conditions array that you build, run json_encode() and also print that out on the screen. It's a good way to see how the array format is getting translated. It makes it a lot easier if you're not a human PHP->JSON translator. For complex objects it helps quite a bit.
So what does that do above? A little context. I have some recipes and I only want those marked as being true for the "public" field to be included in the results first and foremost. You may have similar needs with some sort of publish status. Then we're running a regex on the query string passed via POST, but we're running it on two fields. In my case I have the title for the recipe and then some directions. I may also want to search ingredients too. Of course ingredients is in an object for me, so hey where's that good ol' $elemMatch? Yay, more complexity.
Ok. So sticking with this...What is returned is exactly what you'd expect, any recipe that's publicly visible that has the query in the title or directions (case insensitive match).
Lithium's "like" array key will not work in this case for two fields. It will for one. So in other words:
// Will NOT work
$conditions = array(
'title' => array('like' => '/chicken/i'),
'directions' => array('like' => '/cook/i')
);
// WILL work, but won't give us what we're after
$conditions = array(
'title' => array('like' => '/chicken/i')
);
Again, keep in mind we also need to use the $or operator for a very important reason. We don't want duplicate records in our search results. How about the other operators and other conditions? Sure! You can apply the $nin and $ne and all the other operators too. Those are a little different though. So where we put in $or as if it was a field, we have to use $nin and $ne like the other article explained. Let me write it out again all glued together with the first example here.
$conditions => array(
'public' => true,
'$or' => array(
array('title' => $search_regex),
array('directions' => $search_regex)
)
),
'title' => array(
'$nin' => array('BBQ Chicken', 'More Chicken')
)
);
You can probably start to imagine how you would build an "advanced" search where you allowed the user to fill out a few different form fields that narrowed the search down. Ok, so let's glue together a query that combines a little of everything that we've gone over in all three articles. This will be as if a visitor came to the site, searched for recipes that were shared with a specific family (let's say their family), had "chicken" in the title/directions, and was not shared with the public. So only a private chicken recipe currently approved and shared with a specific family (group).
$conditions = array(
'public' => false,
'$or' => array(
array('title' => $search_regex),
array('directions' => $search_regex),
array('ingredients.ingredient' => $search_regex)
),
'title' => array(
'$nin' => array('More Chicken')
),
'families' => array(
'$elemMatch' => array(
'family_id' => '4c59d5ec7f8b9a6279000000',
'approved' => true
)
)
);
Confused? You will be, in this episode of MongoDB & Lithium...Right, so your queries can get rather complex. Hopefully you won't be confused after reading my article, but to be fair, that's complex even if you were to type it straight up in the console written as JSON. It's no different really, but don't think that you need to make a custom query with Lithium, your query can be represented as a PHP array which really helps you out with dynamic values.
The above query also looks at ingredients, which in my case are within an array. In a production situation I probably would build a more complex regular expression or possibly several. The query could end up even more complex based on what's posted along to my search method. This should really give you some ideas though. How does this impact the database? I'm not really sure. I haven't benchmarked this at all or anything.
The $type Operator
One last query for the road? Sure. This is a simple one and probably very obscure. MongoDB lets you query by data type. Wha?? Crazy right? I personally haven't found a real great use for this yet, but you never know. Here's an example:
$conditions = array(
'title' => array(
'$type' => 2
)
);
That will return every recipe where the title is a string. The $type operator accepts a list of numbers that are assigned to a BSON type. You can see the list of types on the MongoDB documentation page.
That's all for now, hope you enjoyed!
3 Great CSS References
With newer browsers being released that take advantage of HTML5 and CSS3, especially the browsers on the mobile devices, now is the time we are able to start leveraging some really powerful features. A lot of seemingly simple effects had to rely on JavaScript, which is still fine and even better now - newer browsers run JavaScript faster. However, we are still in a period of migration. We fortunately have all decided as internet citizens to stop supporting IE6 (more or less), but we still have IE7 and IE8 to deal with. They're better, but still not on the same bandwagon as the other browsers. They're still running along side with us though so we have to feed them too. Let's cross our fingers for IE9.
Despite all this, I think there's some really clever things you can do with CSS and CSS3. These effects are now becoming feasible, whereas in the past they were not. So real quick here I'm going to list a few really great resources for you instead of re-inventing the wheel...er copying and pasting code snippets...
- CSS3, please! is just an awesome little site that shows you a few little tricks to get the effect you're after working in many browsers.
- Another great site is the simply and stylish CSS3 Generator. However, it doesn't really care much about Internet Explorer's needs.
- Boog Design has a very nice matrix calculator for CSS3 rotations, IE likes to use this Matrix thing with radians, yea like that makes sense, you can check out the goods here or read more from the blog post.
- Bonus: Hey, remember that pngfix.htc stuff for IE? There's one for rounded corners too!
Personally, I say go ahead and use these advanced CSS styles...If it's supported in IE great...If not, maybe you can find a graceful degradation solution. I just don't think IE will change or upgrade until we start building sites that are more in line with the standards and move in the direction the rest of the world is moving in. Microsoft will eventually comply..But not if we keep giving in to them. In the meantime, don't miss out on building a clean looking web site, with little hassle, for that iPad or Android based device.


Social Networks