Anders Tornblad

All about the code

Label archive for lbrtw

Complete blog remake, part 2

This is the second part of a series of articles about my complete blog remake. If you haven't read the first part, here it is: Complete blog remake, part 1

Last week I wrote about completely remaking my blog, leaving WordPress, PHP, MySQL and Loopia behind. One of my main concerns was to keep all urls intact, since I know that some of my old articles have a lot of incoming links. The whole url scheme reverse-engineering was the focus of the first part of this article series.

The ghost of WordPress unpatched

After taking a leap of faith and actually publishing the first version of my reverse-engineered blog engine (along with all of the articles) to Azure, I kept a vigil eye on the access logs. I wanted to make sure that I hadn't missed any incoming links. I discovered two cases of misspelled addresses and the non-existing robots.txt and favicon.ico, that I could fix quickly, but most of all there were hundreds of of 404's for the WordPress administration panel, WordPress-specific AJAX url's, some requests for /xmlrpc.php, and a lot of requests for (which I found out after some searching) known security flaws in older WordPress versions.

Virtually every evil bot net out there is trying to exploit our blogs, hoping to find an unpatched WordPress installation. This is one of the reasons I wanted to leave WordPress behind. It is also the reason I have chosen to not have an administration web interface for my blog. Instead I am actually using Visual Studio, not just for coding, running unit tests, debugging, testing and publishing to Azure, but also for writing my articles and publishing them.

Putting T4 text templates to work

My article data files are really simple text files, each containing a title, a category, some labels and the markup of the article itself. I wrote a simple T4 template for converting this to an XML file. When I have written an article, I simply run the TextTemplatingFileGenerator tool and then click Publish to send the new article to Azure. Then I just wait for the scheduled task (runs once per hour) to pick up the new article and make it visible.

My favorite IDE, by far, is Visual Studio, and my favorite language is without doubt C#. I have blogged a lot about JavaScript and PHP too, but I have to admit that C# is my number one. Being able to actually use Visual Studio as my main tool for blogging (both when writing the Blog engine code, and when writing articles) feels really great.

So far, everything that I have done fits well within the Free tier of my Azure subscription. So not only have I a blogging tool that suits me better, I have also reduced my web hosting cost with 100 %. There is still more to write about, like having ASP.NET MVC Areas that map to my subdomains, like demo.atornblad.se, and I leave that for the next part of this series.

Complete blog remake, part 1
Complete blog remake, part 2 (this part)
403s for the Naughty List

Complete blog remake, part 1

For a while now, I have been thinking of migrating this blog to a completely different environment. I have grown really tired of the two most important technical aspects of this blog: the hosting provider, and the blog platform.

Leaving WordPress

For some bloggers, WordPress is the way to go. The platform is pretty easy to use, even if there is a bit of a learning curve, and it's still capable enough for more advanced blogging solutions, especially if you are ready to go down the messy plugin path. For lots of blogs, WordPress does the job.

However, for my needs, WordPress is too big, too small, and too messy. Too big, because what I really only need is to serve some text to readers. This can be done with a static filesystem serving articles. But I also need archives, search and some structure in the form of categories and labels. WordPress does that but so could I. Too small, because I want to be able to show nicely formatted source code in different languages, display some graphics based on data, link to my spare-time projects and GitHub repositories in a good way. There are plugins for all of those, but WordPress plugins tend to be really messy. Too messy, in fact, because the WordPress core and all those plugins tend to make it difficult to properly keep blogs upgraded and secure. You should always apply security patches, but that might break some plugin that hasn't got around to patching or upgrading yet.

There are more arguments for leaving WordPress behind, and I think Daniel Thornbury said it best.

Writing my own blog engine couldn't be that difficult. After all, it's just a matter of mapping urls to information, right? That's what web servers do. When looking at my WordPress configuration, I realized that I should be able to create a very simple ASP.NET MVC site to do all this for me. This is a run-down of what addresses are handled:

Addresses for routing

All of this could be handles with a pretty simple chain of calls to MapRoute, like this:

public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute( name : "MonthlyArchivePage", url : "{year}/{month}/page/{page}", constraints : new { year = @"^\d{4}$", month = @"^\d{2}$", page = @"^\d+$" }, defaults : new { controller = "Archive", action = "MonthPage" } ); routes.MapRoute( name : "MonthlyArchiveFirstPage", url : "{year}/{month}", constraints : new { year = @"^\d{4}$", month = @"^\d{2}$" }, defaults : new { controller = "Archive", action = "MonthPage", page = 1 } ); routes.MapRoute( name : "LabelPage", url : "label/{name}/page/{page}", constraints : new { page = @"^\d+$" }, defaults : new { controller = "Archive", action = "LabelPage" } ); routes.MapRoute( name : "LabelPageFirstPage", url : "label/{name}", defaults : new { controller = "Archive", action = "LabelPage", page = 1 } ); routes.MapRoute( name : "CategoryPage", url : "category/{name}/page/{page}", constraints : new { page = @"^\d+$" }, defaults : new { controller = "Archive", action = "CategoryPage" } ); routes.MapRoute( name : "CategoryPageFirstPage", url : "category/{name}", defaults : new { controller = "Archive", action = "CategoryPage", page = 1 } ); routes.MapRoute( name : "Search", url : "search", defaults : new { controller = "Search", action = "Index" } ); routes.MapRoute( name : "Feed", url : "feed", defaults : new { controller = "Feed", action = "Index" } ); routes.MapRoute( name : "StartPage", url : "page/{page}", constraints : new { page = @"^\d+$" }, defaults : new { controller = "Start", action = "Page" } ); routes.MapRoute( name : "SingleArticle", url : "{postname}", defaults : new { controller = "Article", action = "Single" } ); routes.MapRoute( name : "StartFirstPage", url : "", defaults : new { controller = "Start", action = "Page", page = 1 } ); }

With a total of five controller classes and just seven action methods, the MVC part is really simple. Each method has a non-complex data query and a corresponding view, all sharing a common set of "master page" layout views. The paginated views (start page and archive pages) all share their pagination technique, so I only have to write it once. These are some examples of how simple the code can be:

public class ArchiveController : Controller { public ActionResult MonthPage(int year, int month, int page) { DateTime startDate = new DateTime(year, month, 1); DateTime endDate = startDate.AddMonths(1); var query = from article in ApplicationData.Current.Articles where article.Published >= startDate && article.Published < endDate select article; var model = new ArchivePageModel { AllArticles = query.ToArray(), Page = page, PageSize = 10, Title = "Monthly archive for " + startDate.ToString("MMMM yyyy") }; return View("Page", model); } public ActionResult LabelPage(string name, int page) { var query = from article in ApplicationData.Current.Articles where article.Labels.Contains(name, StringComparer.InvariantCultureIgnoreCase) select article; var model = new ArchivePageModel { AllArticles = query.ToArray(), Page = page, PageSize = 10, Title = $"Label archive for {name}" }; return View("Page", model); } } public class ArticleController : Controller { public ActionResult Single(string postname) { var article = ApplicationData.Current.SingleOrDefault(a => a.PostName.Equals(postname, StringComparison.InvariantCultureIgnoreCase)); if (article == null) { return HttpNotFound(); } return View(article); } }

I also want to keep the data backend super-simple, so I actually decided to go with putting the articles on files in the App_Data folder, and storing the entire dataset in the HttpApplicationState object, effectively caching everything in-memory between app restarts. But since I don't want to actually restart the application every time I add a new post, I also write a "data refresh" action method that clears and rebuilds the cache. That method is set up with some added security so that it can only be called from localhost.

All in all, writing all the C# code needed for migrating from WordPress to ASP.NET MVC takes about a day. Adding some nice CSS stuff and JavaScript takes another day.

Leaving Loopia

For several years now, I have been a customer of Swedish web hosting provider Loopia. They have always been almost good enough, not quite living up to 100 % of my expectations. When ASP.NET 4 came out, it took Loopia almost two years to provide it to customers, and then ASP.NET 4.5 was already in the works. Their current version of PHP is 5.5.11, which was release almost two years ago. This is really bad for several reasons.

I'm not talking about web developers wanting the latest shiny toys to play with (even if that is also the case). No, I'm talking about security and quality. Failing to upgrade means failing to install security patches. I'm not demanding an upgrade to PHP 7, and not even PHP 5.6, but at the very least they should upgrade to 5.5.30, which has had a lot of bugfixes and security added since 5.5.11.

Loopia has worked for me in the past, but they have also always been a little too expensive given what they provide. Too little, too late, too expensive.

So I decided to move to Microsoft Azure. With a really nice set of tools available for developers, and an exquisitely well-made integration with Visual Studio, the choice was simple. I can design and create my blog, run all my tests, publish it and even debug it remotely from within the same tool.

Brave new world

So I did a full move. From PHP to ASP.NET and C#. From Loopia to Microsoft Azure. From MySQL to filesystem. From WordPress to my own creation. And it's all in the cloud, monitored, secure, load-balanced, and almost completely free!

Next time, I will talk a little about the Azure part of the move. Deployment, scheduled jobs, monitoring and so on.

Complete blog remake, part 1 (this part)
Complete blog remake, part 2
403s for the Naughty List

Reinventing a PHP MVC framework, part 4

Putting the parts together

This is the fourth part of a series of articles about the mt-mvc PHP MVC framework. If you haven't read the first parts, here is the first one: Reinventing a PHP MVC framework, part 1

The ASP.NET MVC framework started out pretty simple, but now contains loads of features that I don't really feel belong in something called an MVC framework. There is stuff like script and stylesheet bundling and minification, helper methods for HTML "controls", the Action Filters from MVC 3, and so on.

These things are nice to have, but they bloat the framework and should be added using some smart Dependency Injection framework instead.

That is why, in my PHP MVC implementation, I will stick (for now) with the most basic things needed to get some real use out of an MVC framework:

  • Routing URLs matching a pre-set pattern to Controller class names and Views
  • Getting user input from the URL or POST data without hassle
  • Rendering an HTML view
  • Robust, unit-tested code

Url rewriting

To be able to use urls like this one: http://example.com/ninjas/item/52 in PHP, you need to do some url rewriting. This section in .htaccess should work fine for you:

<IfModule mod_rewrite.c> RewriteEngine on RewriteRule ^(.*) mvc.php?path=$1 [L] </IfModule>

Or this setting in IIS UrlRewrite web.config:

<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="MVC routing" stopProcessing="true"> <match url="^(.*)" ignoreCase="false" /> <action type="Rewrite" url="mvc.php?path={R:1}" /> </rule> </rules> </rewrite> </system.webServer> </configuration>

Then getting the requested path is as simple as calling:

$requestedPath = $_GET['path'];

Autoloading magic

Taking a convention over configuration approach, I simply decide that controller classes should belong to the Controllers namespace, reside in the ~/Controllers/ directory, and have a name ending with Controller. This is in line with how ASP.NET MVC expects things, in the default setting.

By registering an autoload function, I get a certain amount of control over what .php files to include when some part of the solution wants to create an instance of a controller class. The spl_autoload_register function takes a callback function that gets called every time an unknown class is referenced. The callback function can either create the class (probably by including some php file containing the class) and return true, or decide that it is not the right callback for the job, and return false.

The autoloader for MVC controller classes looks like this:

spl_autoload_register(function ($fullClassName) { // Must be Namespace\Classname $parts = explode('\\', $fullClassName); $isTwoParts = (count($parts) == 2); if (!isTwoParts) return false; // Namespace must be 'Controllers' $namespaceName = $parts[0]; $isControllersNamespace = ($namespaceName == 'Controllers'); if (!isControllersNamespace) return false; // Class name must end with 'Controller' $className = $parts[1]; $isControllerSuffix = (substr($className, -10) == 'Controller'); if (!isControllerSuffix) return false; // Look for file here: DOCUMENT_ROOT/Controllers/classname.php $filename = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'Controllers' . DIRECTORY_SEPARATOR . $className . '.php'; // Does the file exist? if (!file_exists($filename)) return false; // Include the file. Done! require_once $filename; return true; });

Now that the routing mechanism is (almost) in place, and there is a way of locating the controller classes, putting things together will look something like this:

$routing = new Routing(); $route = $routing->handle($requestedPath); $controllerClassName = 'Controllers\\' . $route->controllerClassName; if (class_exists($controllerClassName)) { $controllerClass = new ReflectionClass($controllerClassName); if ($controllerClass->hasMethod($route->methodName)) { $controllerInstance = new $controllerClassName; $method = $controllerClass->getMethod($route->methodName); $inputModelBuilder = new InputModelBuilder; // TODO: Create $request instance first $inputModel = $inputModelBuilder->buildInputModel($method, $request, $route); $result = $method->invokeArgs($controllerInstance, $inputModel); // TODO: Create $response and $viewRootDir instances first $result->executeResult($response, $viewRootDir); } else { // Non-existing method! // Respond with 404 Not Found } } else { // Non-existing controller! // Respond with 404 Not Found }

Still to do

There is still some code to write before this framework is useful. As you have noticed, some code was mocked in the unit tests of the earlier articles of this series:

  • Some Request class that contains POST data (and possibly other things in the future)
  • Some Response class that knows how to set HTTP headers (and possibly other things in the future)
  • Some FileSystem class that wraps files, directories and that can include files into the output stream
  • Some AutoLoader that loads the different parts of the framework when needed
  • Some ActionResult base class that ViewResult and other result classes can inherit from
  • Some NotFoundResult class that sets the HTTP response to 404
  • Consider putting the framework classes in a namespace of their own

These action points will be the topic of the next article(s). For now, take care!

Reinventing a PHP MVC framework, part 1
Reinventing a PHP MVC framework, part 2
Reinventing a PHP MVC framework, part 3
Reinventing a PHP MVC framework, part 4 (this part)

You'll find the code from this article in the related release on GitHub. The latest version is always available in the GitHub repository.

Reinventing a PHP MVC framework, part 3

Let's add some spokes to the wheel

This is the third part of a series of articles about the mt-mvc PHP MVC framework. If you haven't read the first parts, here is the first one: Reinventing a PHP MVC framework, part 1

When an HTTP request is handled by ASP.NET, a factory called DefaultControllerFactory goes to work. Its CreateController method looks through the web app's controller classes using reflection, finds the right one and creates an instance. Then the infrastructure takes a look at which method to call, again using reflection, and the method gets called. The result, some ActionResult subclass, then produces the correct view.

The mechanism relies heavily on reflection which is really difficult to unit-test, because you would have to mock the entire class system and file system. So this part is not developed using TDD, but write-and-debug. I'm sorry!

PHP doesn't have precompiled assemblies that the infrastructure can search through to find the correct class. Instead we need to take a convention over configuration approach, and pick some standards to enforce. Using PHP's autoload capabilities, we can write an autoloader specifically for controllers. The "controller factory" then simply turns into a call to class_exists. The autoloader is detailed in a future article.

Target in sight

The next piece of the puzzle is what I would like to call the MVC framework itself – a class that binds request, routing and response together. The request and response are mocked, and the workflow looks a little like this:

  1. Get path of url from request handler
  2. Translate path into route information using the routing system
  3. Check the method signature of the method requested:
    • If the method requires a single piece of trivial input, and the parameter is called $id, first try the parameter value from the route information
    • If the method requires additional or non-trivial input, get post data from request handler, using prefixed or non-prefixed keys
    • Build the required input model, if any
    • Because the input model will get passed to a method, the input model is an array of arguments in correct order for passing to the method
  4. Run the method, passing the input model as arguments
  5. Execute the result using the response handler

The routing bit is already in place, albeit not complete for non-trivial production purposes. The next step is creating an input model builder, and here are the first set of tests for it:

class InputModelBuilderTests { public function ReturnsNullForMethodWithoutParameters() { $class = new \ReflectionClass(ImbtControllerDummy::class); $method = $class->getMethod('NoParametersMethod'); // No expected calls to request or route! Expect($route); Expect($request); $builder = new InputModelBuilder(); $result = $builder->buildInputModel($method, $request, $route); The($result)->ShouldEqual([]); } public function ReturnsRouteParameterForSimpleParameterMethod() { $class = new \ReflectionClass(ImbtControllerDummy::class); $method = $class->getMethod('SimpleParameterMethod'); // No expected calls to request! Expect($route)->toGet('parameter', 'abc.123'); Expect($request); $builder = new InputModelBuilder(); $result = $builder->buildInputModel($method, $request, $route); $route->checkAll(); $request->checkAll(); The($result)->ShouldEqual(['abc.123']); } public function ReturnsPostIdForSimpleParameterMethod() { $class = new \ReflectionClass(ImbtControllerDummy::class); $method = $class->getMethod('SimpleParameterMethod'); Expect($route)->toGet('parameter', null); Expect($request)->toGet('post', ['id' => 'abc.123']); $builder = new InputModelBuilder(); $result = $builder->buildInputModel($method, $request, $route); $route->checkAll(); $request->checkAll(); The($result)->ShouldEqual(['abc.123']); } public function ReturnsPostDataForTwoSimpleParametersMethod() { $class = new \ReflectionClass(ImbtControllerDummy::class); $method = $class->getMethod('TwoSimpleParametersMethod'); // No expected calls to route! Expect($route); Expect($request)->toGet('post', ['bar' => 'QWER', 'foo' => 'ASDF']); $builder = new InputModelBuilder(); $result = $builder->buildInputModel($method, $request, $route); $route->checkAll(); $request->checkAll(); The($result)->ShouldEqual(['ASDF', 'QWER']); } public function ReturnsNonprefixedPostDataForComplexParameterMethod() { $class = new \ReflectionClass(ImbtControllerDummy::class); $method = $class->getMethod('SmallInputModelMethod'); // No expected calls to route! Expect($route); Expect($request)->toGet('post', ['bar' => 'QWER', 'foo' => 'ASDF']); $builder = new InputModelBuilder(); $result = $builder->buildInputModel($method, $request, $route); $route->checkAll(); $request->checkAll(); The(count($result))->ShouldBeExactly(1); The($result[0])->ShouldBeInstanceOf(\ImbtSmallInputModel::class); The($result[0]->foo)->ShouldEqual('ASDF'); The($result[0]->bar)->ShouldEqual('QWER'); } public function ReturnsPrefixedPostDataForComplexParameterMethod() { $class = new \ReflectionClass(ImbtControllerDummy::class); $method = $class->getMethod('SmallInputModelMethod'); // No expected calls to route! Expect($route); Expect($request)->toGet('post', ['model-bar' => 'QWER', 'model-foo' => 'ASDF']); $builder = new InputModelBuilder(); $result = $builder->buildInputModel($method, $request, $route); $route->checkAll(); $request->checkAll(); The(count($result))->ShouldBeExactly(1); The($result[0])->ShouldBeInstanceOf(\ImbtSmallInputModel::class); The($result[0]->foo)->ShouldEqual('ASDF'); The($result[0]->bar)->ShouldEqual('QWER'); } } class ImbtControllerDummy { public function NoParametersMethod() { } public function SimpleParameterMethod($id) { } public function TwoSimpleParametersMethod($foo, $bar) { } public function SmallInputModelMethod(ImbtSmallInputModel $model) { } } class ImbtSmallInputModel { public $foo; public $bar; }

Making these tests succeed is not that hard, but I have to admit that we have strayed a little from the ASP.NET path now. This is intentional. I want the ease-of-use of ASP.NET, but also want to add some ideas of my own.

class InputModelBuilder { public function buildInputModel(ReflectionMethod $method, $request, $route) { $parameters = $method->getParameters(); $parameterCount = count($parameters); if ($parameterCount === 0) { return []; } $result = []; foreach ($parameters as $index => $parameter) { $typeHint = $parameter->getClass(); $name = $parameter->getName(); // Trivial single-value input model named $id: if ($name == 'id' && !isset($typeHint) && $parameterCount === 1) { // This is the only time the $route->parameter is used! $value = @$route->parameter; if (isset($value)) { return[$value]; } } if (!isset($postData)) $postData = $request->post; if (!isset($typeHint)) { // Trivial single value from post if (isset($postData[$name])) { $result[] = $postData[$name]; } } else { // Type-hinted value $result[] = $this->buildTypeHintedObject($name, $typeHint, $postData); } } return $result; } private function buildTypeHintedObject($optionalPrefix, ReflectionClass $typeHint, array $postData) { $className = $typeHint->getName(); $result = new $className; $properties = $typeHint->getProperties(); foreach ($properties as $property) { $name = $property->getName(); $prefixedName = "$optionalPrefix-$name"; if (isset($postData[$prefixedName])) { $property->setValue($result, $postData[$prefixedName]); } else if (isset($postData[$name])) { $property->setValue($result, $postData[$name]); } } return $result; } }

The bits and pieces are starting to fall into place, but they all need to be put together. Come back here in a while for more on that.

Reinventing a PHP MVC framework, part 1
Reinventing a PHP MVC framework, part 2
Reinventing a PHP MVC framework, part 3 (this part)
Reinventing a PHP MVC framework, part 4

You'll find the code from this article in the related release on GitHub. The latest version is always available in the GitHub repository.

Reinventing a PHP MVC framework, part 2

Let's make the wheel more round

This is the second part of a series of articles about the mt-mvc PHP MVC framework. If you haven't read the first part, here it is: Reinventing a PHP MVC framework, part 1

Return to sender

In the old days, before fire was invented, responding to a request was done by calling Response.Write and writing directly to the response stream. In an MVC world (in whatever language, but especially in an object-oriented one), doing this from within a controller is a big no-no. Writing to the stream, using Response.Write or echo will only happen in the View!

In ASP.NET MVC, responding to an HTTP request is done by returning an instance of a class derived from the abstract ActionResult class. For a normal page view, you return a ViewResult object. For an AJAX request expecting JSON data, you return a JsonResult object. Some other examples are the RedirectResult, HttpStatusCodeResult, AtomFeedActionResult, and FileContentResult classes.

Most of those classes reference some model object, and will eventually render something view-like using the properties of the model object. The rendering itself, including sending HTTP headers, takes place in an implementation of the abstract ExecuteResult method. For now, I will focus only on serving ordinary views, like ASP.NET MVC does through the ViewResult class.

Some assembly needed

Using the Routing class from the previous part, we can find the names of a controller class and the method to call. We will now expect that method to return an object that has an executeResult method (first letter is lower-case, because PHP). I actually want to make my MVC framework act more in line with the MVC pattern than ASP.NET.

First of all, I don't want the controller to be able to access response artefacts like HTTP response headers, and the response content stream, because those are definitely presentation details. To have a clear separation of duties, those things should only be available to the View. Because of this, the executeResult method needs to be provided with some mechanism for setting HTTP headers and writing content. This "response wrapper" is easily mocked, for now. For testability, we also need to mock the filesystem.

This first iteration of ViewResult should set the Content-Type to text/html and then perform a standard PHP include on a view php file, using a (mocked) filesystem wrapper.

class ViewResultTests { public function ExecuteResultSetsCorrectContentType() { $controllerName = 'Home'; $viewName = 'Index'; $model = null; $viewResult = new ViewResult($controllerName, $viewName, $model); Expect($response)->toCall('setHeader')->withArguments(['Content-Type', 'text/html; charset=utf-8']); Expect($viewRootDir)->toCall('phpInclude')->withArguments(['home/index.php']); $viewResult->executeResult($response, $viewRootDir); $response->checkAll(); $viewRootDir->checkAll(); } } class ViewResult { private $controllerName; private $viewName; public function __construct($controllerName, $viewName, $model) { $this->controllerName = $controllerName; $this->viewName = $viewName; } public function executeResult($response, $viewRootDir) { $response->setHeader('Content-Type', 'text/html; charset=utf-8'); $viewRootDir->phpInclude(mb_strtolower($this->controllerName) . '/' . mb_strtolower($this->viewName) . '.php'); } }

The constructor for the ViewResult class needs the name of the controller, not the controller class. For this, we need to add a few more lines to the RoutingTests and Routing classes. That code is trivial and out of scope for this article, but you can look at it in the GitHub release.

All parts

Reinventing a PHP MVC framework, part 1
Reinventing a PHP MVC framework, part 2 (this part)
Reinventing a PHP MVC framework, part 3
Reinventing a PHP MVC framework, part 4

You'll find the code from this article in the related release on GitHub. The latest version is always available in the GitHub repository.

Reinventing a PHP MVC framework, part 1

Let's reinvent the wheel

This is the first part of a series of articles about the mt-mvc PHP MVC framework.

I wanted to know how ASP.NET MVC does what it does, so I decided to find out... by trying to reinvent it... in PHP. My line of thought was this:

  • I know how to USE the ASP.NET MVC framework
  • I know the effects of using the various features of the ASP.NET MVC framework
  • I know the principles of TDD
  • I should be able to reinvent (or reverse-engineer) a working MVC framework by adding unit tests for increasingly complex use of MVC, and making one or a few tests pass at a time
  • I also want to become a better PHP developer

I am perfectly aware of the fact that there are lots of MVC frameworks for PHP that are really capable of taking care of business, but this is not a website development effort. This is a learning effort. Reinventing the wheel works fine for learning - not for production code.

MVC the ASP.NET way

Let's start with something simple. The most basic use of ASP.NET MVC, in the default setting, appears to work by separating the request path of an incoming request into a Controller class name, a View method name, and an optional parameter value that gets passed into the method. Also, there are default values for all parts of the path.

First set of tests

I imagine a class that's solely responsible for parsing a path, and suggesting the name of a controller class, and a method to call, so I write some tests for that class first. Hooking things up to the PHP HTTP infrastructure gets added later.

class RoutingTests { public function CheckAllDefaults() { $routing = new Routing(); $route = $routing->handle(''); The($route->controllerClassName)->shouldEqual('HomeController'); The($route->methodName)->shouldEqual('Index'); The($route->parameter)->shouldNotBeSet(); } public function CheckDefaultMethodNameAndParameter() { $routing = new Routing(); $route = $routing->handle('Articles'); The($route->controllerClassName)->shouldEqual('ArticlesController'); The($route->methodName)->shouldEqual('Index'); The($route->parameter)->shouldNotBeSet(); } public function CheckDefaultParameter() { $routing = new Routing(); $route = $routing->handle('Categories/List'); The($route->controllerClassName)->shouldEqual('CategoriesController'); The($route->methodName)->shouldEqual('List'); The($route->parameter)->shouldNotBeSet(); } public function CheckNoDefaults() { $routing = new Routing(); $route = $routing->handle('Products/Item/123x'); The($route->controllerClassName)->shouldEqual('ProductsController'); The($route->methodName)->shouldEqual('Item'); The($route->parameter)->shouldEqual('123x'); } }

These tests are about the default out-of-the-box behavior of the routing subsystem. More advanced features, like registering custom url patterns, get added later.

class Routing { public function handle($url) { $parts = explode('/', $url); $controllerName = @$parts[0]; $methodName = @$parts[1]; $parameter = @$parts[2]; if (!$controllerName) $controllerName = 'Home'; if (!$methodName) $methodName = 'Index'; return (object) [ 'controllerClassName' => $controllerName . 'Controller', 'methodName' => $methodName, 'parameter' => $parameter ]; } }

Usefulness right now

This class does the bare minimum, and making some real use of it requires a lot of nuts and bolts in place – some URL redirection, a request/response pipeline system, some use of reflection to dynamically create controller instances and calling methods, a lot of thought about how to connecting views to the controller methods, and so on. Don't worry; all of that will be covered in the following posts.

All parts

Reinventing a PHP MVC framework, part 1 (this part)
Reinventing a PHP MVC framework, part 2
Reinventing a PHP MVC framework, part 3
Reinventing a PHP MVC framework, part 4

You'll find the code from this article in the related release on GitHub. The latest version is always available in the GitHub repository.

About to solve an old THREE.js bug and move on with Artsy

I really need to pay more attention. Almomst a year ago, THREE.js released the r67 version, which removed the concept of centroids. This made part 3 of Artsy break. I used centroids and the Mesh.calculateCentroid function, not because I needed to, but because some tutorial told me I should.

When the concept of centroids was removed, in April 2014, my JavaScript demos was very low on my list of priorities, but soon I will make time for fixing and advancing. Who knows, I might even be able to finish Artsy once and for all. I started working on it in October of 2013, so it's really overdue!

For now, I have removed the calls to calculateCentroid and done some small changes to at least get Part 3 to start. Stay posted!

Artsy part 3, SoundBox

Wow, it's been a really long time since I wrote anything about my demo projects here. I apologize for that, but the good news is that I have finally found some extra time to spend on "pleasure programming" again.

First, there's the unfinished business of Artsy (and slightly insane). About five months ago, I was finished with the first two parts, and I haven't had time to start on part three until now. So far, it looks pretty good. Unfortunately it doesn't yet work on iOS devices, but feel free to check it out:

Artsy part 3

SoundBox

Someone in the CODEF Facebook group linked to the SoundBox tool, and I find it quite amazing. It's a JavaScript-based chip tune tracker, with a really nice and small playback routine. I have been experimenting a bit with it, and here are some results:

Lots of unfinished business in this update, and I'm sorry about that.

Phenomenal & Enigmatic, part 4

TV Cube remakeI remember seeing the "TV Cube" part of Enigma for the first time – and not really being able to figure out how it was made. Heck, I couldn't even do the math for a proper backface culling, so back in the 1990s my occational 3D objects were never any good. So the thought of making 2D and 3D objects appear on the surfaces of another 3D object was way beyond my understanding of math.

Once again, I am aware that the prettier way of doing this is by manipulating a transformation matrix to rotate, translate and project coordinates from different branches of a hierarchical coordinate system. But I ignored that and rolled it all by hand.

Star field

The stars on the front of the cube might look as if there is some depth, but that's just an illusion. Each star has an (X,Y) coordinate, and a third constant (which I called Z) that governs speed along the X axis and also the alpha component of its color. The lower the speed, the dimmer the light. When observed face on, it gives the impression of a 3D space, but it's really just a form of parallax scroller.

Pseudo code

for (var star, i = 0; star = stars[i++];) { // Move the star a bit to the "right" star.x += (star.z * star.z * speedConstant);     // Limit x to (-1 .. 1) if (star.x > 1) star.x -= 2;     // Left out: Project the star's coordinates to screen coordinates var screenCoords = ( /* left out */ );     // Draw the star, using Z to determine alpha and size context.fillStyle = "rgba(255,255,255," + (star.z * star.z).toFixed(3) + ")"; context.fillRect(screenCoords.x, screenCoords.2, star.z * 2, star.z * 2); }

Hidden line vector

Back in the days, I could never do a proper hidden line vector, because I didn't know how to properly cull back-facing polygons. For the Phenomenal & Enigmatic "TV Cube" part, I arranged all polygons in the hidden line pyramid so that when facing the camera, each polygon is to be drawn clockwise. That way I could use a very simple algorithm to determine each polygon's winding order.

I found one really efficient algorithm on StackOverflow, and I learned that since all five polygons are convex (triangles cannot be concave, and the only quadrangle is a true square), it's really enough to only check the first three coordinates, even for the quadrangle.

Rotating the pyramid in 3D space was exactly the same as with the intro part of the demo, and after all coordinates are rotated, I simple use the polygon winding order algorithm to perform backface culling, then drawing all polygons' outlines. Voilá, a hidden line vector.

Pseudo code

// Points var points = [ { x : -40, y : -40, z : 70 }, // Four corners at the bottom { x : 40, y : -40, z : 70 }, { x : 40, y : 40, z : 70 }, { x : -40, y : 40, z : 70 }, { x : 0, y : 0, z : -70 } // And finally the top ];   // Each polygon is just an array of point indices var polygons = [ [0, 4, 3], // Four triangle sides [1, 4, 0], [2, 4, 1], [3, 4, 2], [3, 2, 1, 0] // And a quadrangle bottom ];   // First rotate the points in space and project to screen coordinates var screenCoords = [];   for (var point, i = 0; point = points[i++];) { screenCoords.push(rotateAndProject(point)); // rotateAndProject is left out }   // Then go through each polygon and draw those facing forward for (var polygon, i = 0; polygon = polygons[i++];) { var edgeSum = 0; for (var j = 0; j < 3; ++j) { var pointIndex = polygon[j]; var pointIndex2 = polygon[(j + 1) % 3];   var point = screenCoords[pointIndex]; var point2 = screenCoords[pointIndex2];   edgeSum += (point2.x - point.x) * (point2.y + point.y); }   if (edgeSum < 0) { // This polygon is facing the camera // Left out: Draw the polygon using screenCoords, context.moveTo and context.lineTo } }

Plane vector

The plane vector is super-simple. Just rotating a plane around its center and then using the code already in place to project it to screen coordinates.

Projection

The function responsible for translating coordinates in the 3D space to screen coordinates is not particularly complex, since it's basically the exact same thing as for the intro part of the demo. Also, to determine which faces of the cube that are facing the camera, I just used the same backface culling algorithm as for the hidden line vector. I was really pleased with the end result.

Phenomenal & Enigmatic, part 1
Phenomenal & Enigmatic, part 2
Phenomenal & Enigmatic, part 3
Phenomenal & Enigmatic, part 4 (this part)

The entire demo, including non-minified JavaScript, is available on GitHub: /lbrtw/enigmatic

Artsy (and slightly insane), first part now in beta

In between writing about the Phenomenal & Enigmatic JavaScript demo, I'm also doing a JavaScript remake of the Arte demo by Sanity from 1993. The first effect I made was the "disc tunnel" effect, seen 2min 9sec into the YouTube clip, and the entire first part is now live, but still in beta.

The address is demo.atornblad.se/artsy, but I haven't tested it that many browsers and devices yet. I do know that it crashes on Windows Phone 7.8 after just a few scenes, but it works really nicely on my iPad 3, especially in fullscreen mode. Add a shortcut to your iPad's start screen for fullscreen mode.

I will make some changes to the bitmap tunnel effect, and make sure that the demo runs correctly on most browsers and devices. Also, stay tuned for parts 2 and 3 of Sanity Arte, and of course there will be a blow-by-blow description here on atornblad.se when the whole thing is complete.