Hi, my name is Terry Smith and I'm a developer and aspiring entrepreneur.


Archive for the ‘Miscellaenous’ Category


6
Aug

Programming your own PHP framework, Part 2

Posted by Terry on 08/06/2009 at around 7:47 PM

Welcome back (or just plain welcome)! In the last part of our PHP framework exploration, we looked at URL rewriting.  Today we’re going to look at creating an MVC framework to wrap around your application.  I’ll be making reference to parts of the first tutorial to help us accomplish this.

To get things started, you should be familiar with the Model-View-Controller paradigm.  The basics components are models (classes/objects/etc.) that represent items in your application (users, database records, etc.), controllers which do the processing for a page or module and views, which contain the HTML/CSS for your output.  You can read more about it here

Step 1: Directory Structure

As noted in the last part, I have chosen to structure my URLs in the format domain.com/[controller]/[module]?vars.  In this case, the controller indeed represents the controller in the MVC paradigm.  I will take a moment to explain the directory structure I’ve used; again, note that you can use almost any structure for your own applications.

/config - Basic configuration files (database settings, etc.)
/controllers – Controllers are all in this directory.
/lib – Default library files included with every new site I deploy (database class, URL rewriting, templating and other base classes)
/models – Custom models for each application (users, sessions, etc.)
/views - View files (PHP files), that contain the HTML
/web – The actual web directory we point our web server to.

There are two things of note here.  First, our images, CSS, etc. go into our /web directory, since the web server can’t read anything above the /web folder.  Second, and most important, all of these files should be outside/above the directory you actually point your web server to serve files from.

Step 2: The only real file

So let’s start at the beginning, since that seems the most reasonable place.  Inside my /web directory, I have just one PHP file, index.php:

<?php
require(dirname(getcwd()) . "/lib/init.php");
RoutingController::getInstance()->route();
?>

As you can see, there isn’t too much to it.  You may note that we’re using a singletoninstance of the RoutingController; more on that later.  We’re including the initialization file that comes standard with each deployment.  This file will include all of the others and set up the rest of our system.  Note: make sure you have mod_rewrite enabled and the .htaccess file in place from the last part.

Step 3: Initializing the system

So, let’s take a look at that initialization file:

<?php
// Are we currectly in production?
define('PRODSERV', false);
// Directory definitions
define('ROOT_DIR', dirname(getcwd()) . '/');
define('LIB_DIR', ROOT_DIR . 'lib/');
define('VIEW_DIR', ROOT_DIR . 'views/');
define('MODEL_DIR', ROOT_DIR . 'models/');
define('CONTROLLER_DIR', ROOT_DIR . 'controllers/');
define('CONFIG_DIR', ROOT_DIR . 'config/');
define('TEMPLATE_DIR', ROOT_DIR . 'templates/');
// Set up the library includes
set_include_path(CONTROLLER_DIR . PATH_SEPARATOR . LIB_DIR . PATH_SEPARATOR . MODEL_DIR . PATH_SEPARATOR . get_include_path());
// Include database settings
$dbini = parse_ini_file(CONFIG_DIR . 'database.ini');
// Get our connection to MySQL
$mysql = MySQL::getInstance();
$mysql->initialize($dbini['host'], $dbini['username'], $dbini['password'], $dbini['database']);
// Make sure we can autoload files
function __autoload($class)
{
        require("$class.php");
}

Let’s go through this part by part.  The very first thing we do is create a define called PRODSERV which represents whether we’re running in a production environment or not.  If we’re not, we can output error information, but we certainly wouldn’t want that in production.  Next, we define all of the directories where our various files reside and make sure to let PHP know where they are by temporarily changing the include path.  We can then load the database settings (I’m using INI files for ease of use, you could also use XML, etc.) and tell MySQL to connect (or whatever database you’re using).  Finally, we’re setting PHP’s __autoload variable to autoload classes with the simple [ClassName].php naming system from anywhere in our include path.

Step 4: Routing to a controller

Alright, on to the meat.  This is going to be the longest step by far so hang on!  Next up, our RoutingController class (located in my install at /lib/RoutingController.php).  The route() function in our index.php class was actually covered in our last section:

$path = parse_url(
     (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' .         // Scheme     
     $_SERVER['PHP_AUTH_USER'] . ':' .                               // User     
     $_SERVER['PHP_AUTH_PW'] . '@' .                                 // Password     
     $_SERVER['HTTP_HOST'] .                                         // Hostname     
     $_SERVER['REQUEST_URI']                                         // Path and query string
);
$temp = explode("/", substr($path['path'], 1));
$controller = strtolower((@$temp[0]) ? $temp[0] : "welcome");
$module = strtolower((@$temp[1]) ? $temp[1] : "index");
if(!file_exists(CONTROLLER_DIR . "{$controller}Controller.php")) {     
     $controller = "Error";
     $module = "index";
}if(!method_exists("{$controller}Controller", "{$module}Handler")) {
     $controller = "error";
     $module = "index";
}
$class = $controller . "Controller";
$controller = new $class($controller, $module);
$method = "{$module}Handler";
$controller->$method();
$controller->render();

Basically, we’re tearing apart the URL and redirecting control to a Controller.  So let’s take a look at the Controller base class, from which all of our other controllers will inherit:

<?php

class Controller {
        private $Template;
        protected $vars = array();
        private $Controller;
        private $Module;

        function Controller($controller, $module) {
                $this->Template = new Template;

                $this->Controller = $controller;
                $this->Module = $module;
        }

        function get($var) {
                if(isset($_GET[$var]))
                        return($_GET[$var]);
                return(false);

        }

        function post($var) {
                if(isset($_POST[$var]))
                        return($_POST[$var]);
                return(false);

        }

        function cookie($var) {
                if(isset($_COOKIE[$var]))
                        return($_COOKIE[$var]);

                return(false);
        }

        function setLayout($layout) {
                $this->Template->setLayout($layout);
        }

        function redirect($controller, $module) {
                if(strcmp($module, "index"))
                        header("Location: /$controller/$module");
                else
                        header("Location: /$controller");
        }

        function render() {
                // Set our template variables first
                foreach($this->vars as $key => $value)
                        $this->Template->set($key, $value);

                // Render
                $this->Controller = strtolower(substr($this->Controller, 0, 1)) . substr($this->Controller, 1);
                $this->Template->render(VIEW_DIR . "/{$this->Controller}/{$this->Module}.php");
        }
}

Again, we’ll walk through it bit by bit (function by function in this case).  We start off with our constructor, which saves the controller and module that are being called and initializes our template class (which we’ll look at in the another part).  Next we’ve defined wrapper classes around our get, post and cookie variables.  The reason for this is that while I’m not doing it here, you can preform various sanitizations on the values getting returned.  We define a wrapper function to make things a little easier for our users for the template in setLayout.  The only real use for this is if we want to disable any page layout (again, more on this in our section on templating).  We also define a redirection function to redirect internally (adding support for external redirects will be left as an activity for you).  Finally, we have created a render function which was called from our RoutingController which passes set variables into the template and then tells it which file will generate the content for the layout.

Whew!  It’s a lot, I know, and I’ve tried to compress is as much as possible.  But keep on trekking, we’re almost there.

Now we’ve got URL re-writing and our basic routing system and controller base class set up.  So let’s look at an actual controller.  We will define a basic “welcome” controller I will include with every default deployment of this system.  This file will therefore be located at /controllers/welcomeController.php:

class welcomeController extends Controller {
        function indexHandler() {
                $model = new SomeModel;
                $this->vars['title'] = 'Welcome';
        }
}

This controller illustrates several things:

  1. The class name and file name must be the same.  This is because in our __autoload call in our initialization file, we told it to look for [controller]Controller in the file with the same name. 
  2. Our module name must be in a function called [module]Handler.  As I’ve said throughout this entire series, this is simply a design decision on my part and is located in the RoutingController::route function. 
  3. The default module, as defined in the same route function is “index” so if there is no module (ie. domain.com/welcome) it will be sent to the function indexHandler().
  4. Variables passed in to our template will be located in $this->vars.  These variables will be passed into our layout and our controller/module template.  In this case, we’re passing the page title in as a variable called “title”, therefore we set $this->vars['title'].

Step 5: Models and Views

This will be a short section, mostly because Models will be covered in more detail when we look at database abstraction and we’re going to cover views when we look at our template system.  But let’s take a quick peek nonetheless:

Models are our custom classes.  So users, sessions, database records, etc.  All of our models reside in our /models directory.  Let’s take a look at our default model (for fun, defined at /models/Default.php):

class Default extends MySQLObject {
}

As you can see, we’re creating a Default class that extends our MySQLObject base class to be discussed later.  Simple as that!

Next let’s take a quick look at a view.  The corresponding view for our welcomeController::indexHandler function can be found at /views/welcome/index.php.  This can include an entire HTML file, but as you’ll see later, our template class wraps whatever is in this file in a layout that is standard to all views.  So let’s take a look:

<h2><?php echo $tpl_vars['title']; ?></h2>

This is the default welcome view file for each deployment.  Edit away!

Like I said, super simple.  However, you can see our template variable being used!

Step 6: Keep going!

This entire process has been trial and error for me, and as I said in the last section, I am by no means an expert in these subject.  But I haven’t seen a good guide on doing this sort of stuff yourself and I hope that you can take what I’ve presented here and keep going and create new and even more wonderful things.

In our next sections, we’ll cover database abstraction and templating!

8
May

Wordcamp Toronto 2009 Presentation

Posted by Terry on 05/08/2009 at around 4:03 PM

I’ve posted my presentation from Wordcamp Toronto 2009 on buddyPress/bbPress integration below.  I will be making more posts in the next little bit about some of the content and going over my talk, but the presentation itself is in high demand, so voila!

24
Apr

Be a Part of the Conversation

Posted by Terry on 04/24/2009 at around 10:52 PM

Social media has brought about a lot of things; some good, some not so good.  One of the things it has done is opened up the conversation.  Just the other day we (b5media) encountered a rendering issue with Google AdSense on Webkit based browsers (specifically Safari and Chrome).  So I tweeted about it to mattcutts who is quite well known on Twitter for helping people solve Google related issues.

On the flip side of things, it also allows companies to monitor what people are saying about them.  There are a lot of companies (especially startups, but some well known larger companies) that actively watch social media services for feedback, complaints, etc. and many of them even respond in turn.

This is a tremendous advantage of utilizing social media tools.  So why can’t everyone be a part of the conversation?

Specifically, I am working on a software project that would allow you to store all your movies in the cloud.  Now, to avoid legal complications, I would love to talk to someone from the MPAA or the movie industry in general to find out what the guidelines are (for example, how to recognize rented DVDs, DVDs with copyright protection, etc.).  But these major media organizations aren’t a part of the conversation.  The only contact information they give is their address and telephone number (not even an e-mail address!).

Now anyone I know can tell you I’m not afraid to pick up a phone, but I wouldn’t even know where to start.  E-mail gives me time to formulate my thoughts and answers to questions.  And most importantly, my e-mails can be forwarded if necessary and can be responded to in due time, because nobody wants to play telephone tag with a major corporation.

These organizations need to be encouraging legal innovation in order to stop people from using illegal methods.  I’ve said it before, you just need to make it easier, faster, more reliable, etc. to get your movies legally and people will do that.  Not everyone, but a good portion I think.

Am I missing something?  This my start of a conversation; is anyone listening?

16
Mar

Am I missing something?

Posted by Terry on 03/16/2009 at around 8:44 AM

So this morning I am being assaulted on Digg and other sites with news that AIG is paying $160 million+ in bonuses.  Congress is clearly pissed, and are all saying “Oh my god, this will never happen again.”  Yeah… right.

These bailouts are turning into on of the greatest catastrophes in North America’s history.  On the part of the auto-makers and their ridiculous unions and executives fighting to protect high costs that are (and I believe will) drive them out of business.  Any so it should be; that’s capitalism, and no business is going to last forever.  But where each of the big 3 fail, 100 others will rise to take their place. 

Reid Hoffman (CEO, LinkedIN) recently wrote in a large number of places that start-ups can bail us out.  Armed with innovation and creativity and the lack of executive red tape that makes implementation possible, most of the suppliers and people in the pipeline would remain in business and in the face of bankruptcy, would almost certainly offer better deals to fresh faces.  And these startups could finally tell the unions to go to hell and renegotiate contracts that will help keep the innovation alive.

But back to the point of this article: on the part of the banks (and in part the automakers), where is the person who will tell the banks that THEY ARE GOING OUT OF BUSINESS?  There must be enough people out there with the experience and determination who can take over as CEOs and make the changes necessary to restore the trust and turn around what has surely become the worst PR disaster in these banks’ history.

The government needs oversight in key sectors like finance in order to keep control over the nation and to prevent capitalism from destroying the entire sector.  What is happening in the banks now should’ve happened long ago.  But the government needs to take it one step further: they need to tell the current executives to fundamentally change and restructure the way things get done.  Cap salaries, no more bonuses, and the list goes on.  And if they can’t perform, then the government needs to go to the shareholders and tell them that they need to fire their disastrous management and bring in people who will make the necessary changes or they will let them sink into insolvency and will pick up the pieces after the fact.

I heard this morning that these bonuses were contractually obligated and that there was no out clause.  First, that was a dumb move.  But more importantly, why are these executives still taking their bonuses??  What we have here is a fundamental difference between start-ups executives and executives brought in specifically to make money for themselves and their shareholders who care nothing for their business.  Any reasonable executive would’ve turned their bonus away, knowing it’s yet another PR disaster that they simply cannot afford and that their business is that much more likely to have to jump through even more hoops if they need more funds in the future.

Where are the people willing to make the necessary changes?  Am I missing something?

15
Mar

New Blog Design

Posted by Terry on 03/15/2009 at around 1:02 PM

Well, it’s been one hell of a last month.  I’ve been working hard on some bbPress and buddyPress stuff, learning their code bases, and learning to write plug-ins to modify and build on their cores.  I’ve developed what we’re thinking of releasing as bbPress Plus version which will come prepackaged with most of the functionality found in other forum software.

I’ve also been working hard to smoothly integrate all of the best features of the two.  Instead of using bbPress’s built in profiles and plug-ins for private messaging, we’re taking that functionality from buddyPress.  It’s challenging because both of them are pre-1.0 packages and neither have seemingly taken the time to play nice with each other (though they both integrate quite nicely with Wordpress MU).

At the same time, I’ve stopped working a “regular” job and have gotten into contracting full-time.  This means instead of working for “the” man, I’m now working for a lot of different ones (and women too!).  Hence, I decided to invest some time and money into a new blog design that would allow me to better sell myself and my services.

So by referral from a friend, I hired James MacDonald from Flockey Web Development and I am quite impressed with the new design.  I didn’t have a lot of requirements, but I really wanted to create an equal focus on the blog and my services.  The blog is a representation of me personally, an outlet for my thoughts and ideas, and since my personality is reflected in all of my work, it’s important for my to find an appropriate balance.

So if you’ve got a bbPress, buddyPress or any other job and are looking for a fun and creative programmer with a (very) tiny flair for design, feel free to contact me!

More to come on my most recent projects in the next couple weeks!