Kohana
by jeff on Dec.24, 2009, under kohana
For a while now, I’ve had a project on my radar that required a small, easily configurable framework that I could just drop into existing projects with minimal fuss. What I’m doing is adding administrative interfaces to existing sites that are authored in a custom framework.
My predecessor integrated the backend of these sites into these frameworks. In some frameworks (symfony) this is easy to do; you just create a custom backend application and it’s pretty much separated from everything else. However, the framework we use doesn’t support this. All assets are shared and you’re stuck with this.
So I figured, hey, if I could make a self-contained system that happily runs out of a subdirectory, then I can build my own admin and drop it in any site I want. I only have to change one .htaccess rule to get requests for /admin to redirect to this folder.
Here’s what I considered:
Yii
Kohana
PHPTAL
Twig
Codeigniter
You may notice that some of these aren’t necessarily frameworks. PHPTAL and Twig bill themselves as template languages. Still, for my needs, they were considered because I figured I could still leverage my knowledge of PHP to simply develop faster.
My requirements were pretty simple. I don’t care that much about speed as this is for an admin interface and I don’t expect there to be much data being tossed around (our clients are pretty low-volume with updates). I don’t want to have to screw around with the commandline. I feel neutral about an all-inclusive ORM for this project - I would be fine just making a db.inc.php for my connections and a simple script to hold functions to query the db. It has to run out of a subdirectory. I need sufficient documentation since I don’t have a whole lot of time. I wanted to be able to set up nested templates with inheritance. Security is a major consideration. It has to be vendor-neutral towards javascript libraries.
Long story short: in the end I went with Kohana. I was intrigued by Yii, but it was simply more than I needed for this project. I had some issues getting enough documentation to really understand what I was doing with Twig. PHPTAL seemed to suffer from the same problems I had with Smarty…I often want to do simple things and don’t want to dig through forums looking for template-specific syntax. Codeigniter…well…I installed it and it seemed to be taking me forever to really get a login form displayed and working. I freely admit that this could be my own stupidity; I am pressed for time and if I couldn’t get a simple demo up within an hour I was going to move on.
So, there you have it. This is not an exhaustive discussion of the pros and cons of various frameworks and template languages, I can only say that for my ability and needs, Kohana was the right fit.
Admittedly, for new development of sites I will still use symfony + doctrine. I love the admin generators and the logical layout. However, since I had to retrofit a rather awkward framework with new functionality in a short amount of time, Kohana seems perfect.
I think what I like most about it, coming from a symfony background, is that most questions I have about what methods are available in a specific helper or why I would want to extend a Template_Controller instead of just a regular Controller can be answered by looking at the source of the helper. I think it’s easily one of the most well-commented projects I’ve seen. Considering that I have a horror story (for another entry at some point in the future) with trying to fix a small bug in the MODx CMS and finding that the only comment in the offending function was “New Improved Version! :)”, it means a lot.
Breadcrumbs
by jeff on Dec.08, 2009, under Uncategorized
I was recently looking at the documentation for the Yii framework for creating a breadcrumb widget, when I realized I had a slightly cleaner (I think) way of implementing breadcrumbs in sites.
Here’s some example code. Explanation follows.
<?php class jeffCrumbs { private $breadchain = array(); public function addCrumb($text, $crumb) { $this->breadchain[$text] = $crumb; return $this; } public function display() { echo "\n<!-- Breadcrumbs -->\n<ul id='breadcrumbs'>\n"; echo "\t<li>\n\t\t".link_to('home', '@homepage')."\n\t</li>\n"; $breadcrumbs = new CachingIterator(new ArrayIterator($this->breadchain)); foreach ($breadcrumbs as $key => $value) { if(!$breadcrumbs->hasNext()): echo "\t<li class='active'>\n\t\t".strtolower(trim($key))."\n\t</li>\n"; else: echo "\t<li>\n\t\t".link_to($key, strtolower(str_ireplace(" ", "-", trim($value))))."\n\t</li>\n"; endif; } echo "</ul>\n"; } } ?>
So, as you can see here, this takes advantage of PHP method chaining to generate crumbs. This example is for symfony (note the link_to helper) but with a slight modification, could reasonably work in any framework.
The syntax for creating breadcrumbs is thus:
<?php $crumbs = new jeffCrumbs(); $crumbs->addCrumb('Link Text Here', '@yourroute'); $crumbs->addCrumb('More Link Text Here', '@yourotherroute'); $crumbs->display(); ?>
The advantage here over some other crumbs is that your home page, which should always be the first item in a breadcrumb trail, is automatically inserted. The syntax is also a little bit easier to understand than a bunch of nested arrays. I’m using some SPL junk in there just because I like SPL.
You may also ask “what’s the deal with all the tabs and newlines”? Well that’s an attempt to keep generated markup looking clean, which I’m a fan of.
I’ve been using some iteration of jeffCrumbs for a few years now and I love it. Hopefully someone else will find it useful
P.S. If you’re using symfony, just drop this class into your /lib folder and instantiate a crumb on each template. Boo-ya!
Mexican’t
by jeff on Dec.08, 2009, under Uncategorized
Ok, this has nothing to do with PHP, jQuery or whatever else I may have ever talked about here…but it’s still important. This is about Mexican food.
I have been sort of a non-fan of Mexican food for as long as I can remember. I can’t pinpoint exactly what it is about it that I don’t like; suffice to say though that, at a minimum, I am indifferent about Mexican food. Surprisingly, my aversion to this has caused a measurable amount of conflict in my life. Not a year has ever gone without someone, upon being exposed to my food preferences, stating that I have just never had “real Mexican food”.
Let me set the record straight here. I have been to Mexico a couple of times. I have had Mexican food, in Mexico. You can’t get more real than that. Guess what? I still didn’t like it. I have met people that don’t like Italian food and they seem to have gotten a lot less flak for it than I ever have. What’s the deal?
Anyway.
At a recent team lunch, people were trying to decide where to go. In the spirit of honesty, when asked, I said I didn’t like Mexican food. Instantly I became a selfish indivdualist hellbent on disrupting this team lunch. It was a very strange phenomenon. In the end we compromised, but there still seems to be some latent aggravation floating around.
So, if you know someone else that says they don’t like Mexican food, please try and accept that maybe they really just don’t like it and are telling you this solely to inform you. On behalf of all Mexican food-nonlikers I urge you to consider their feelings. Thank you and we will now return to your regularly scheduled program.
JS void(0)
by jeff on Nov.03, 2009, under geeky, random crap
Halloween is over and it was pretty OK. I didn’t get many trick-or-treaters, which is fine since I ended up leaving to go to a semi-weird Halloween party anyway.
I’ve heard lots of rumblings and grumblings that Halloween “just ain’t what it used to be” but, you know, looking back I don’t remember it ever being much of anything. I was always the kid that didn’t care much about it until the last second. I came up with innovative costumes such as “cardboard box” wherein I just cut some holes in a box and wore it around.
“Hey, are you supposed to be a robot?”
“No, I’m a box.”
“Okaaaay.”
But anyway, enough about that. Let’s talk about Javascript.
There are a lot of times, when working with Javascript, that you want to fire off a function from a click without actually letting the browser navigate to the URI located therein. This kind of thing is pretty common; for instance you might have a form where you return false on the post action until all the fields have been validated. I’ve become accustomed to (and spoiled by) the jQuery way, which is necessarily more unobtrusive since events are bound dynamically instead of being coded in the markup. But anyway, the quick and dirty way to do this without a library is to use an onclick event. Or an ondblclick event if that’s how you roll.
So, for the longest time, I’ve been using javascript:void(0) as the default href attribute for JS events. The problem with using a pound sign instead is that it can cause the page to refresh and/or jump to a non-existent anchor at the top of the page and void(0) prevents this. Pound signs also used to mess up some analytics I used so I just got out of the habit.
Yesterday I had the opportunity to learn there is a better way. Apparently even Microsoft recommends against the use of void(0), although testing in IE6, 7 and 8 shows that it seems to work just as intended.
Simply write your onclick to call the relevant function, returning false afterwards. i.e.
<a onclick=”someCoolFunction(); return false;” href=”#”>Awesome Link</a>
Yes, there is a pound sign in there, but the return false prevents the browser from navigating to it. I’ve usually written “return false;” into the function body, but this way is especially handy and seems a little more foolproof.
More info here.
How to (not) run your business
by jeff on Oct.30, 2009, under true stories
I’m obviously not a renowned expert on business practices, but I’m at least smart enough to know that if you offer a paid service and your primary means of delivering this service to customers is via the Internet, your business should prioritize availability and expediency. Secondary to this, you should offer clear and current documentation of your services. Detailing exactly how your customers get the information they are paying for should be one of your major goals.
Pretty obvious, right?
Well, I recently had the occasion to review some code I’d written that was designed for exactly this scenario - fetching paid-for data from a provider. It brought back memories of the halcyon days wherein our business partnership was characterized by them having precisely none of the above-mentioned qualities.
Here’s the breakdown.
Day 1: Things begin amicably. Basics of the service are reviewed, the API documentation is perused, and friendly emails are exchanged with questions and answers. A transaction occurs wherein I pay them money in return for the services they have promised to provide. All is well in Wellville.
Day 2: Having followed their documentation to the letter, I have built the first of what would be many, many iterations of my system to consume their webservice. I test it and it fails. Further inspection reveals that I’ve been given a non-working API key. I email them and their first reaction is to instruct me in the ways of properly encoding the URI. I politely email them back, saying I have followed their own documentation to the letter. I’m getting an error from their own service that clearly indicates I have a non-working key. Time passes and the next day I am sent a new key.
Day 3: Armed with my new key, I launch another test. It was a great success…mostly. I find that I’m only getting about a fifth of the expected results, and that it only works once every hour. Assuming idiocy on my end, I perform a bunch of tests. I email them. They once again instruct me in the ways of properly encoding the URI, but I email them back and say we had that conversation yesterday and my local tests appear to be working fine. Time passes.
Day 4: The day begins with an email - they then say my IP is getting blacklisted due to consuming too much bandwidth. I then tell them I am actually using their own example calls and methodology listed in the documentation. Much time passes. They then say I should use an XML API, which was before this conversation totally unknown to me - I had made the mistake of assuming I had all the documentation they offered. I then receive a copy of their suspiciously freshly-minted XML API documentation. I have my suspicions and make a very simple script which posts some XML as their instructions state, just as a test. I don’t even get blacklisted - the URI for this webservice gives a 404. I send off an email.
Day 5: I come in to an email; they’ve whitelisted my IP. They suspiciously don’t mention the XML API at all assuming, apparently, that I have no ability to recall conversations. I test anyway and it’s still a 404. I then fire up the original non-XML script and a glorious thing happens - it works and I get all the data. The heavens open and the angels sing. Still not entirely trusting things, I set the daily cron job to send me an email with results.
Days pass. But not very many days. 3 to be exact.
Day 8: The automated emails indicate that suddenly NULL results are found when connecting to the remote service. Nothing has changed that I know of. I look at the latest API documentation and everything is in order. Another email is fired off and, guess what? I’m once again told how to encode the URI. We now have a long email chain going and, looking through the history, you can clearly see they’ve copied and pasted this same FAQ question and sent it off to me several times. I send off an email once again and a very strange thing happens. They fail to respond. Ever. The next day, the URI that worked previously goes to 404 as well.
The good news is that I got my money back. However, they still promote this service on their website. I have a theory that I just wasn’t a big enough player to really be bothered with. They probably really did roll out a final XML API and they transitioned larger customers to the new format. Me, as a smalltimer, well…all I get is stock responses and, at most, emails with one sentence that explain nothing. This seems to be confirmed by the fact that the plan I had has been eliminated from their offerings altogether.
I’m not upset. Really. I learned a lot in the process, both good and bad.
Arrays of Objects in PHP
by jeff on Oct.06, 2009, under geeky, symfony
I was recently working on a calendar application and needed a way to pass a large amount of data to a template because I wanted to minimize calls to the database. This data set represented various days, each day could have multiple events, etc. Anyway, it doesn’t matter why I guess, the fact is that I had to figure it out. First, I went with some parallel arrays - one held id’s of events and dates, the other held event information. Not a perfect solution. Then I went with a multidimensional array, which worked fine, although I had a need to be able to search days easily and it seemed overly complex trying to loop through multiple levels of an array just to retrieve one item.
So, then I decided upon an array of objects. Surprisingly, searching for this kind of thing on google didn’t reveal many relevant hits. This worked perfectly and was the answer I needed. Here is a demonstration:
<?php class Test { public $greetz = "hello"; public function makegreets() { $this->greetz = $this->greetz." and hello"; } public function saygreets() { return $this->greetz; } } //to prove basic functionality works $jeff = new Test(); echo $jeff->saygreets().'<br/>'; $jeff->makegreets(); echo $jeff->saygreets().'<br/>'; $testarray = array(); $x = 1; while($x<100) { $temp = new Test(); $temp->makegreets(); $testarray[] = $temp; $x++; } $testarray[34]->makegreets(); print_r($testarray);
Cool, right?
Well, it gets even better. After some playing around with it, I started seeing “stdClass Object” show up in the output when I tried to just manually set class variables on an uninitialized object. i.e.
$testarray[]->ham = "we have ham!";
This causes a new stdClass object to be initialized with one class variable called, predictably, ham. For my purposes, this worked even better than defining a class myself. Obviously, if you need methods in a given class, you’ll have to roll your own, but just as long as you want to set some simple properties in a generic class, stdClass seems ideal. For instance, this would work just as well as my other code…
<?php $testarray = array(); $x = 1; while($x<100) { $temp = new stdClass(); $temp->greetz = "hello"; $temp->greetz = $temp->greetz." and hello"; $testarray[] = $temp; $x++; } $testarray[34]->greetz .= " and hello"; print_r($testarray);
On Style
by jeff on Sep.22, 2009, under geeky
Lately, while doing Google searches for some insignificant coding-related idea, I came across a posting on a forum where a challenge was posed.
The challenge:
Make an array consist of about 8 value and a button in a single php page.
Echo each of the array value and put a break between them.
When the button is clicked, the arrangement of the array will be reversed so the display of array will be reversed.
Make the script repeatable, each time of click will reverse the array.
Eg: 1st click will make the array reverse but 2nd click will make the array arrangement normal again,
3rd click make the array reverse again…etc..
What was initially interesting is how many different ways people came up with to accomplish this. Most people met the objectives, although a few didn’t read the specifications. But what really took the cake was that one guy submitted a solution that implemented a possibly bad idea but it sure looked cool (if you didn’t know what it was doing), and then somehow everyone else absorbed this bad idea into their own code.
One of the first things I realized when programming professionally is that there is no relation between how complex a bunch of code appears to be and how secure, fast, or functional it really is. I seemed to have been applying this kind of reverse Occam’s Razor idea to code - I believed that if I couldn’t understand something, it must naturally be better than I could produce. It seems, from this brief exposure, that many others fall into the same trap.
In other news, if you’re curious, here’s my solution:
<?php $myarray = array("cows", "chickens", "dogs", "goats", "sheep", "yaks", "monkeys", "koalas"); $x = (isset($_POST['x'])) ? $_POST['x'] + 1 : 0; if($x%2==0) $myarray=array_reverse($myarray); foreach($myarray as $element): echo $element."<br/>"; endforeach; ?> <form method="post" action=""> <input type="hidden" name="x" value="<?php echo $x ?>" /> <input type='submit' name='Submitted' value='Get Jiggy With It' /> </form>
jpSpoofer updated!
by jeff on Sep.04, 2009, under Uncategorized
I’ve updated the package I posted yesterday. It still works the same way for now, it’s just a lot cleaner - I’ve moved all the file operations into its own class and created a simple php file (control.php) which is used to kick off the process.
You may also notice two empty classes called jpCurl and jpMultiCurl. These are for the next (possible) iteration of jpSpoofer. I’m not sure if there is anything to be gained by running multiple cURL operations in parallel considering that file i/o will likely be a chokepoint. But regardless, I’m going to give it a shot.
I will also be adding options to process the proxy list in a serial fashion as opposed to randomly, and an option to remove known non-working proxies from the source list. These two options make jpSpoofer a general proxy checker as well as a hit generator.
General syntax:
<?php require_once("classes/jpSpoofer.class.php"); $spoofer = new jpSpoofer('http://www.target.com'); $spoofer->setProxyList('proxylist.txt') ->setWorkingProxyList("good-list.txt") ->setVariance(5) ->setMode("cli") ->run(1000); ?>
PHP, cURL, and proxies oh my!
by jeff on Sep.03, 2009, under Uncategorized
I recently had a need to hit a certain page a whole lot of times. You know, drive up the hit counters. This isn’t an especially hard task with PHP; you can use cURL to request the page as many times as you want. The complication comes about when you want to get sneaky and have the hits recorded as if they come from different IP addresses.
As with most everything, I got started searching for simple solutions and found that, generally, people fell into one of two groups…those that said it was impossible and those who misapplied the concepts. One of my favorites was the site wherein someone was manually setting $_SERVER['REMOTE_ADDR'] and then checking the value of it a line later, in the same script. Obviously since PHP dutifully echoes out what had been set manually, it must be correctly spoofing the IP address, right? Well, umm, not really.
So, yeah, obviously completely spoofing the IP address using PHP only is inherently impossible since PHP can’t modify packets on the fly in this manner. But, we can use a proxy and achieve a similar effect, right? Right.
I found another blog that had an implementation of this idea (props to Anthony Shapley), but I wasn’t so jazzed about the graphical frontend since I intended to run it as a CLI script. Like his story relates, I also found that there are people out there charging for these scripts. Rubbish. I decided to jazzercise some code and add the features I want, then I wrote a simple test script. I offer it all to you here, free.
Here’s the general breakdown of the process flow:
You define your target site when instantiating a new jeffSpoofer object. Then you can use method chaining to set some options, such as whether to log those proxies that work, and finally run it. Then sit back and profit. Or, you know, whatever it is that you do.
I decided to set it up so that initially you give it a long list of proxies of unknown quality and, as it runs, it builds a list of those proxies that work. To increase hits, it will start randomly using some proxies from that list. There are some specific reasons I did it this way instead of just making it “lock on” to one specific working proxy and using that for all hits, most notably, random non-working proxies will cause some time variations between hits. This is a good thing - I know it’s pretty easy to see when someone is using an automated process for something when you notice every hit is exactly .5 sec apart. Anyway, the variance is adjustable, the script basically just has a 1 out of X chance to use a known good proxy so if you want to go that route you can set X to 1.
So, if you are so inclined, this script can be used to automagically test the validity of proxy lists and I have this vague suspicion that’s how it will likely end up being used because the cases where you need to generate hits on a site are pretty limited.
Instructions are fairly self-explanatory if you are familiar with PHP. An example is provided at the bottom of the script. You should set linebreak to “\n” if you are running this as a CLI script (the best way) or “<br/>” if you’re going oldschool with it and viewing it with your browser. If you run it in your browser, you will have to mess with it to override the default script timeout.
<?php /** * spoofer.php * * A class that does things with proxies * * @author Jeff Parker (jeff@NOSPAMsmackblast.com) */ class jeffSpoofer { private $numtimes = 1; private $target = null; private $proxylist = null; private $proxylength = null; private $workingproxylist = null; private $workingproxylength = null; private $workingarray = array(); public $variance = 5; public $linebreak = "\n"; public function __construct($target) { $this->target = $target; } public function run($numtimes) { $count = 0; if($this->proxylist == null) die("{$this->linebreak} Error: file not initialized. Exiting."); $this->setFileLengths(); while($count <= $numtimes) { echo"----- Run {$count} of {$numtimes} -----{$this->linebreak}"; $this->curlItUp($this->random_proxy()); $count++; } } public function setProxyList($filename) { $this->proxylist = new SplFileObject($filename, 'r'); return $this; } public function setWorkingProxyList($filename) { $this->workingproxylist = new SplFileObject($filename, 'a+'); return $this; } private function setFileLengths() { if($this->proxylist != null): $this->proxylength = $this->getFileLength($this->proxylist); endif; if($this->workingproxylist != null): $this->workingproxylength = $this->getFileLength($this->workingproxylist); $this->buildWorkingArray($this->workingproxylist); endif; } private function buildWorkingArray(SplFileObject $fileobject) { foreach($fileobject as $line): if($line != '') $this->workingarray[] = $line; endforeach; } private function addToArray($text) { $this->workingarray[] = $text; } private function getFileLength(SplFileObject $fileobject) { $count = 0; foreach($fileobject as $line): if($line != '') $count++; endforeach; return $count; } private function getLine($line, SplFileObject $fileobject) { echo "Tried to get line number: {$line}{$this->linebreak}"; $myline = $fileobject->current($fileobject->seek($line)); if($myline == ''): echo "Line was blank{$this->linebreak}"; endif; return $myline; } private function writeLine($text, SplFileObject $fileobject) { if(in_array($text, $this->workingarray)): return true; endif; $written = $fileobject->fwrite($text); if($written != null): $this->workingproxylength++; $this->addToArray($text); return true; else: return false; endif; } private function random_proxy() { if($this->workingproxylength != null && $this->workingproxylength >= 1): $switch = rand(1, $this->variance); if($switch == 1): //use a known working proxy $proxy = $this->getLine(rand(0, $this->workingproxylength-1), $this->workingproxylist); echo "attempting to use a known good proxy from the list... {$this->linebreak}"; else: $proxy = $this->getLine(rand(0, $this->proxylength-1), $this->proxylist); echo "using an unknown proxy... {$this->linebreak}"; endif; return $proxy; else: //do some crap with the regular list of proxies $proxy = $this->getLine(rand(0, $this->proxylength-1), $this->proxylist); endif; return $proxy; } private function curlItUp($proxy) { echo "Using proxy: {$proxy}"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->target); curl_setopt($ch, CURLOPT_AUTOREFERER, false); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT,7); curl_setopt($ch, CURLOPT_REFERER, "http://www.test.com"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_PROXY, $proxy); $data = curl_exec($ch); if(empty($data)): echo curl_error($ch)."{$this->linebreak}"; curl_close($ch); flush(); else: curl_close($ch); print "Connection successful.{$this->linebreak}"; $testwrite = $this->writeLine($proxy, $this->workingproxylist); echo ($testwrite) ? "Write successful.{$this->linebreak}" : "Write failed.{$this->linebreak}"; flush(); endif; echo $this->linebreak; } } $spoofer = new jeffSpoofer('http://www.target.com'); $spoofer->setProxyList('proxylist.txt')->setWorkingProxyList("good-list.txt")->run(1000); ?>
OSCON Final Impressions
by jeff on Jul.24, 2009, under Uncategorized, symfony
I happened upon another PHP guy today and much of what I said was echoed. PHP was not really well-represented in sessions, especially in terms of practical applications that average developers might be interested in. But, Java was almost non-existent.
However, technically, there were PHP sessions all day even though some of them were only tangentially about PHP as they were more about memcached, cloud computing, etc. I attended all of them and, strangely enough, found the least-attended one to be the most interesting and the most relevant to my needs. It was about using the PEAR installer to manage website releases. I’m currently using rsync and it’s not the most elegant solution. I have a bunch of custom CLI scripts to rsync stuff from different directories. They work, but it’s a bit difficult to manage and there are often dependencies that span directories…this causes havoc every now and then when rsync’ing templates and forgetting to rsync the associated actions.
There were two other talks I thought were really cool and they both took place at OSCAMP. One was about phpBB and the other was about YQL. It’s not that I didn’t know about phpBB already, but you could tell the speaker was at least extremely interested in the topic and it left me wanting to get involved, so I’d say that’s a good measure of success.
For what it’s worth, it seems that OSCON is really geared towards decision-makers and not so much towards developers. I guess I understand the logic there, after all I suppose that I could have gone to ZendCon for an extreme exploration of PHP topics.
I did get a chance to tell peeps from Webmin that I love their product, and I told Luke Welling and Laura Thomson that I loved their book
One last thing - with so many authors in attendance, I thought apress and wrox would make an appearance, but I guess it being an O’Reilly thing they may have not been welcome. That kinda sucks.