Don’t Repeat Yourself v.s. Write it Every Time.
It’s not common that acronyms work out that well. I’m pretty happy that I just thought of that.
Author: giblfiz
Tottling around CakePHP
My research into CakePHP has, up to this point has been pretty positive. It seems to address most of the big issues I have had with other frameworks, and the code and tutorials all look good.
Because it was looking so good, I decided that I would go ahead and give it a whirl on an actual live project. I chose one that was small and urgent. (The urgent part may not have been wise, since for me personally cake is still unproven, but it seemed like a good fit) and I have been pounding out code for it since then.
So far I’m pretty happy with it, the code virtually never makes me cry, though I run into a lot of road-bumps almost all of them seem to be learning curve issues, and I haven’t actually gotten stuck on anything yet, just briefly derailed occasionally.
The Bake application that comes with cake, which is a small php script that lets you generate starting PHP code is really great for me, because it does cover a lot of gruntwork in a snap, though sometimes it crashes when I’m trying to generate specific models, but it’s probably good, for learning purposes, that I’m forced to write the occasional one by hand.
At the moment I still strongly prefer the smarty template engine to the way views are handled in cake, but the two are not incompatible, and it also may be just a case of me being set in my ways. Perhaps when I really get to know the helper functions a bit better I will be happier with this aspect of cake.
Oh, and the one thing that has caused me the most trouble so far is singular vs plural names. For some reason bake messes up the pluralizations when it is generating the rules for many to many models, and I don’t seem to have a feel for what should be singular and what should be plural.
Still, even with all the bitching I’m doing, I’m really happy with cake. I would say that development using it is going about on schedule, which is pretty good for the first time with a framework, and I feel like it is going to save me a lot of pain at the end with debugging and maintainability.
strike one against cakePHP
So I’m looking into the cakePHP framework, because, well why not. (for those not in the know cake is to php as rails is to ruby) Install is quite painless, but I’m still in the documentation, and I have already bumped into something that makes me cringe. The naming conventions require some classes to be singular, and some to be plural, and there is automatic translation between the the two. (I.E. you don’t declare where the singular class should look for the plural one, it just appends an s and goes to look for it) I’m sure some of you know that English is just not that regular a language.
This of course means there has to be a way to add exceptions
Cake’s naming conventions can be really nice – you can name your database table big_boxes, your model BigBox, your controller BigBoxesController, and everything just works together automatically. The way CakePHP knows how to tie things together is by inflecting the words between their singular and plural forms.
There are occasions (especially for our non-English speaking friends) where you may run into situations where CakePHP’s inflector (the class that pluralizes, singularizes, camelCases, and under_scores) might not work as you’d like. If CakePHP won’t recognize your Foci or Fish, editing the custom inflections configuration file is where you can tell CakePHP about your special cases. This file is found in /app/config/inflections.php.
In this file, you will find six variables. Each allows you to fine-tune CakePHP inflection behavior.
Ugg!! I can just imagine getting caught by this one as part of a learning curve and walking away from the whole damn framework because of it. But I guess this is one open manhole cover that I managed not to fall into.
Built a Tiny Module for phpShop
So I built a tiny new module for phpShop, for one of my clients. Honestly It would have been easier to just slap the code into an already present module, but I wanted to see what it was like to add one from scratch. It was pretty painless, though the permission structure is a little weird in some ways. (I’ll go into it in more detail if anyone is curious)
Really the only thing that caught me off guard with adding a new module, was that you need to register it, and any new functions that it supports in the database. (shockingly in the module and function table respectively) Other than that you basically just write the damn things. Pretty clean and easy.
So Far phpShop is pretty sweet
Well, I have gotten shin deep into phpShop now, and at this point I’m pretty darned happy with what I have seen. Since I have been so dramatically UNHAPPY with most of the other shopping cart implementations that I have run into in PHP, this is something of a godsend. At the moment my only significant complaint is that it uses PHP directly as its template engine, and at this point I’m a HUGE fan of smarty. However I think that this can be dismissed for the moment as a small bump in the road, and if I end up using this heavily, then perhaps I will end up patching smarty onto it.
Other than that, some of the file names could be slightly more descriptive (what is the diff between p_header.ihtml s_header.ihtml and c_header.ihtml) but that would just make an already fairly gentle learning curve easier, its not really a problem.
So far, I give php shop a big thumbs up
Looking into phpShop
Well, I just grabbed a small contract to tweak some stuff in an online shopping cart called phpShop. I have had quite a few run-ins with a huge variety of carts written in PHP, and by and large they have been horrible. OScart is a train-wreck, zen cart is not pretty, but is survivable. So far though, phpShop looks sharp, clean and well written. It does seem to be a bit lightweight, but most of the time thats a good thing not a bad one. As I keep going on this, I will review phpShop in more detail.
Building your own libraries
About 4 months ago I finally got the hang of building my own libraries, and having them consistently come out as useful bits of software that I found myself reusing over and over. More than anything else I stumbled across the secret to this simply as the result of some random flailing, but it’s a very good principle, and I would like to share it because I think that it’s easily adaptable.
The secret is: Write sample code first
This is not the same thing as writing test cases first, its about figuring out how you want to USE your lib, before you sit down and write the lib. you should write sample code that makes use of your library, and if it’s ugly, then you should re-write it. Try not to worry about how you are going to actually make the library do what it will have to at all, just think about writing code exactly the way you want to, in your dream scenario, when you apply the library. Go through it, are there Param’s that can be left out most of the time because 90% of the cases they will always be the same? (set rational defaults)
Now write some more sample code, this time come up with the most absurd, obtuse, edge case applications of your library. Things that it was just not meant to do properly. Write some sample code that is able to do them. Unlike the previous sample code, this does not need to be pretty, you just need to make sure that its possible. I strongly suggest making use of optional closures in order to keep yourself out of boxes.
Now go back over your ideal sample code, polish it even more. You have probably come up with some things that could be made simpler, easier and more obvious after writing the really evil edge cases.
And then finally, start writing the actual library. Figure out how to make it leap through whatever hoops you need to in order to make it fit the sample code that you have written. Try hard not to compromise your original sample code vision. If you must, make sure that you do it in a way that leaves the sample code still graceful. (sometimes it makes sense to change several things if you have to change just one)
I have found that this leaves me with two advantages for actually re-using my libraries later:
1) When I want to use them, they tend to work just how I want them to. This is a result of writing them to an ideal use case
2) I have two big lumps of sample code so I can remember how the hell I was supposed to use them without re-reading the whole body of code. Often I actually paste the simple sample into what I’m doing and then just transform it into what I need.
User Interface Issues
I just recently finished up some pretty cool feature adds to a lightweight CMS that I have built for a client. (they are happy and impressed with it, and It looks like I’m going to pick up another two jobs, where all I need to do is install the code I have written. Yey! I have been trying to get to that point for quite some time now)
One of the major reasons behind the feature upgrade was to fix some huge User Interface problems with “list management”, and they are fixed, but of course now that I’m playing with it, I have discovered that the new UI has some big issues as well. In particular, administrators now see the list exactly as it normally would be, but there are a few links next to each item that allow you to edit it, delete it, or move it up or down. It’s neat but the extra text very much breaks up the visual flow of the list in a lot of cases, and in one case is actually pretty darned hard to access.
schizophrenic code (It talks to itself)
The third part in the Introspective debugger code breakdown, Lets look at what happens when the code actually stops at a breakpoint…
The first chunk of code uses some globals and the backtrace to figure out if it should actually stop or just keep going I will break this down in detail later, when I go over the breakpoint code
function idbg_tick_function(){
global $idbg_stop_flag, $idbg_id, $idbg_var_spy, $idbg_last_line, $idbg_backtrace; $idbg_backtrace = debug_backtrace();
// echo ($idbg_last_line. ” ?= ” .$idbg_backtrace[0][‘line’] .” in “.$idbg_backtrace[0][‘file’].”<Br/>” );
if($idbg_stop_flag and $idbg_last_line != $idbg_backtrace[0][‘line’] and !strpos($idbg_backtrace[0][‘file’], ‘STOP.php’))
{ //echo(“</hr> idbg_tick_function START <br/>”);Next Up We open up a tcp server on the socket of our ID, which will always be a high port. $socket = stream_socket_server(“tcp://”.$_SERVER[“REMOTE_ADDR”].”:$idbg_id”, $errno, $errstr); if (!$socket) { echo “<h1>Debugger couldn’t open socket: $errstr ($errno)<h1/>\n”;} else { because, its what they did in the example code for opening sockets, we read using a while string.I’m pretty sure that when you open the socket your thread sleeps until there is a connection to it anyway, so this is fine.When it does get a connection, we read the first 1024 bites that got sent to it, and store them in msgIn, This is going to tell us what sort of information is being looked for while ($conn = stream_socket_accept($socket)) { $msgIn = fread($conn, 1024); echo($msgIn);
The flush call is pretty important for this, it makes sure that the version of the page that the users are looking atis up to date (and not being stashed away in the buffer)
flush();For command and control we just send back a serialized version of the backtrace, and our spied variables. Much more onvariable spying in a later post (Code in RED is from a different chunk of code, its basically the other end of the conversation)
if ($msgIn == ‘cnc’){ fwrite($conn, serialize(array(“trace”=>$idbg_backtrace,”vars” => $idbg_var_spy))); $responce = unserialize(idbg_socket_client(“cnc”));<font color="#990000"> foreach($responce["trace"] as $key => $val){ echo("<a href='".$callBackName."file&idbg_open_line=".$val['line']. "&idbg_open=".$val['file']."#anchor' target='infoframe'>".strrchr($val['file'],"/")."</a>"); echo(" Line:". $val['line'] . " Function:" . $responce["trace"][$key+1]["function"]. " <a href='#' onClick=\"document.getElementById('v_$key').style.display='block'\">+</a> <div id='v_$key' style='display:none'><pre>"); print_r($responce["vars"][$responce["trace"][$key+1]["function"]]); echo("</pre></div><hr/>"); </font> }
If the message is step, then we return, so the program keeps running, but we don't turn off the "STOP" flags,we also update the "last line" so that we don't end up hitting the same line over and over. } else if ($msgIn == 'step'){ fclose($conn); fclose($socket); $idbg_last_line = $idbg_backtrace[0]['line']; return(TRUE);If the message is cont, then we turn off the "STOP" flags, and then we go on further } else if ($msgIn == 'cont'){ // fwrite($conn, "ACK"); fclose($conn); fclose($socket); $idbg_last_line = 0; $idbg_stop_flag = FALSE; return(TRUE);If we didn't get a command that we know about, send back a generic message, just showing that we heard them.Regardless after this we are going to close down the connection, and then the socket. We also susspended error messaging while we were in the debug code, so we will re-enable it before exiting. } else { fwrite($conn, " This is a SERVER Responce \n"); } fclose($conn); } fclose($socket); } }}//else just keep goingerror_reporting($old_error_level);
Introspective Debugger — the basic structure
So the first thing that this script does is decide which instance of the script it is. What I mean by this that this page now loads completely diffrent code and behaves differently depending on what the situation is.So by default when you load the page, its going to generate an ID number (which will be used as a high port), load a very simple frameset, and then die
if(!array_key_exists("idbg_mode",$_GET)){ /* Display Frame Mode */ echo("&lt;html&gt; &lt;frameset rows='65%,35%'&gt; &lt;frame src='$callBackName"."cnc' name='infoframe'&gt; &lt;frame src='$callBackWvars"."run' name='scriptframe'&gt;&lt;/frameset&gt;&lt;/html&gt;"); exit();
each of the two frames within the frame set then calls the same script, but passes variables to it that drastically change its behaviorOne of them calls with the cnc option (command and control) and one of them calls with the run option, which tells the program to go ahead and run, with the degbugging code active.In each case, the instances are passed (as a get variable) the instance id that was generated with the frameset, which is how they know what port to find eachother on.
I’ll go into how CNC works, and how the processes talk to eachother a good bit more in later posts, but for now I want to look at how the “run” instance works. Run depends on a very obscure php language feature called “register_tick_function” which allows you to set a function that will be run every X statements that are viewed.in addition to registering the tick function it is necessary to start the ticks firing by using the line declare(ticks=1);Honestly I’m a little curious why this languge feature is in PHP at all, but its there, So I think I might as well use it. This is also the feature that was the inspiration for the whole damn project.So the very Astute among you might notice that I do not ever actually call declare(ticks=1), but instead require the user to put it in there code right after they include my file, which is horribly inelegant. Why you might ask?As it turns out the declare statement (possibly only with refrence to ticks, but ticks are the ONLY thing that you can use the declare statement for currently, I guess we will find out if this is universal when we move on to php6) seems to only apply to includes at the same depth and lower in the file inclusion tree as it.This means that since you are including my file in your file, my file is at depth 2 and your file is at depth 1, if I put the declare statement in my file, it will not apply to your file, though it will start applying to any other files that you include. This problem is not documented, and honestly drove me batty for a good long time, because it generates such strange behavior.
else if (array_key_exists("idbg_mode",$_GET) && $_GET["idbg_mode"] == "run"){
I need to cover a lot more of what happens at each tick, but I want to keep each post pretty short, so I’m going to save that for tomorrow.In the meantime I should try to get some paid work done.
echo ("&lt;h1&gt; I am running DIV $idbg_id &lt;/h1&gt;"); register_tick_function("idbg_tick_function");}