Anders Tornblad

All about the code

JavaScript countdown using localStorage

New StackOverflow user Pankaj Badera asked how to create a simple JavaScript timer that would survive reboots of the user's computer.

For the specific question, this is not a good idea, because it seems as if the problem needs to deal with some sort of timing rules (school assignment deadlines, maybe?), in which case a server-side solution is a must. However, the technical aspects is pretty intriguing. As it turns out, I have already faced this problem before, when building Tajmkiper, which handles all of its timing client-side.

In principal, you can start any type of countdown, or "countup", allowing the counter to continue between browser and computer restarts, by simply recording the current time for when an event starts, and then continuously checking the difference between that saved value and the current time of a later event.

In Tajmkiper, this is done for many projects at the same time, but the principle is the same. In the case of this StackOverflow question, there is only one timer. Also, Tajmkiper is meant to be a tool for the user, and not for keeping track of (from the browser's perspective) externally checked rules like deadlines, so a solution involving local storage is fine.

My answer looks like this:

(function() { var started = localStorage['started']; if (started) { // This is not the first time the user opens this file // How long has it been? var diff = Date.now() - started; if (diff >= 1000 * 60 * 60 * 24 * 7) { // At least one week has passed. Do something here. } else { // Less than a week has passed. Do something else here. } } else { // This is the first time the user opens this file localStorage['started'] = Date.now(); // Do something else here to welcome the user... } })();

For my project time clock, this happens every time you select a different project. I combine this with keeping track of how much time has been clocked total.

Formatting a truncated float in JavaScript

StackOverflow user nips asked how to format a floating point number in JavaScript, truncating the value instead of rounding it.

According to the question, this was the desired result:

  • The value 12.999 must be displayed as 12.99
  • The value 14 must be displayed as 14.00

The developer.mozilla.org page gives a thorough example of how to perform decimal rounding correctly, avoiding any rounding errors in the process. For this question, however, a simple multiplication-division pair is surely good enough:

// Step by step var multiplied = value * 100; var truncated = Math.floor(multiplied); var divided = truncated * 0.01; var output = divided.toFixed(2); // One-liner version var output = (Math.floor(value * 100) * 0.01).toFixed(2);

The above code performs the following steps, in order:

  • Value is multiplied by 100
    12.999 => 1299.9
    14 => 1400
  • Value is truncated using the Math.floor function
    1299.9 => 1299
    1400 => 1400
  • Value is multiplied by 0.01
    1299 => 12.99
    1400 => 14
  • Value is formatted for printing using two decimal places using the Number.prototype.toFixed function
    12.99 => "12.99"
    14 => "14.00"

Most languages do have functions called round, ceil, floor or similar ones, but almost all of them round to the nearest integer, so the multiply-round-divide chain (or divide-round-multiply for rounding to tens, hundreds, thousands...) is a good pattern to know.

In JavaScript there is no float datatype like in a lot of other languages, but the Number object is used for both integers and floating point numbers. Internally, numbers are just 64 bit floating point numbers (source: ecma262-6.com), conforming to the IEEE 754 standard. So when dealing with numbers in JavaScript, you always need to take floating point precision into consideration!

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

Keep NumLock on permanently

One of the remnants of early PC computers is the Num Lock key. For most people, like me, it is just a nuisance. I mostly use my laptop with a dock connected to a standard 102 key keyboard, so when I use the numpad, I want numbers to appear – I don't want the cursor to move semi-randomly across my document. When I sometimes hit the Num Lock key by accident, I'm always in for a few seconds of feeling annoyed.

I'm not the only one thinking this. A quick search for keep num lock key on permanently windows 10 gives hundreds of results, but most answers focus on making it turn on at reboot. I could only find one good answer to the actual question.

If you have the same problem, try importing this in Regedit (run as administrator):

Windows Registry Editor Version 5.00 [HKEY_USERS\.DEFAULT\Control Panel\Keyboard] "InitialKeyboardIndicators"="2147483650" [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] "Scancode Map"=hex:00,00,00,00,00,00,00,00,02,00,00,00,00,00,45,00,00,00,00,00

This will fix two things:

  • The first setting makes Num Lock turned on by default at reboot
  • The second setting disables the Num Lock key (key code 45) completely so that its signals don't reach the operating system correctly

Disclaimer

Use this tip on your own risk. Don't forget to backup your registry before doing this. Changing things in RegEdit is always risky and in no event will I be liable for any loss or damage arising from the use of this information.

Revisiting CssFlip

When I first started experimenting with modern Web APIs, 3D transforms was still an experimental feature. Browser support was limited and shaky, and required vendor prefixes. These days support is much better, but there are still a lot of quirks when it comes to browser implementation.

Back in 2011, I wrote a demo called CSS Page Flip, using a combination of CSS and some JavaScript to let a user flip through pages in a book. The transitions are declared in CSS and are triggered by JavaScript. Pages on the left have their transform-origin set to their right edge, which is aligned to the horizontal center of the screen, while pages on the right have their transform-origin set to their left edge. By applying backface-visibility: hidden, I can then rotate pages along the Y axis for a pretty simple effect.

#book { perspective: 2000px; transform-style: preserve-3d; } .page { width: 50%; height: 100%; position: absolute; top: 0px; left: 0px; margin-left: 50%; overflow: hidden; transform-style: flat; backface-visibility: hidden; transition: none; transform: none; } .page:first-child { margin-left: 0px; transform-origin: right center; } .page:last-child { transform-origin: left center; } .currentFold.forward > .page:last-child { transform: rotateY(0deg); } .nextFold.forward > .page:first-child { transform: rotateY(179.9deg); } .currentFold.backward > .page:first-child { transform: rotateY(0deg); } .nextFold.backward > .page:last-child { transform: rotateY(-179.9deg); } .folding .page { transition: all 1s ease-in-out; } .folding > .currentFold.forward > .page:last-child { transform: rotateY(-179.9deg); } .folding > .nextFold.forward > .page:first-child { transform: translateZ(1px) rotateY(0deg); } .folding > .currentFold.backward > .page:first-child { transform: rotateY(179.9deg); } .folding > .nextFold.backward > .page:last-child { transform: translateZ(1px) rotateY(0deg); }

It took a lot of fiddling to find good values for rotateY(). Almost every new version of Webkit broke my experiment, but I eventually settled on a combination of 0deg, -180deg and 180deg.

CSS Flip in EdgeEdge behaving badly A couple of years later, the major browsers started supporting 3D transforms, even without vendor prefixes. Unfortunately all of them have different ideas about how to transition from 180deg or -180deg to 0deg. Finally I thought of forcing the browsers to do what I want by having -179.9deg and 179.9deg. This works fine now, unless for (of course) IE and Edge. IE doesn't even support 3D transforms correctly without prefix, and for some reason Edge treats the transformation matrix differently than the other browsers, completely breaking part of the animation.

Apart from the page flipping, the demo also has an automatic page layout mechanism that reflows the chapters and the text blocks when needed. In the original demo, the contents of the book was actually a detailed description of how the demo was made, but unfortunately the original content was lost at some point. Now it is my personal story instead. Enjoy!

During the next week or two, I will make an effort to bring the solution forward into the 2016 state of mainstream browsers, including the following:

  • Fix scroll wheel flipping in Firefox
  • Handle chapter navigation using history.pushState()
  • Add some interactive stuff on some of the pages
  • Some effort of fixing the weird behavior in Microsoft Edge
  • Minimal effort of making it work in Microsoft IE

When that is done, I will open-source the whole thing on GitHub.

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.

Trying out Visual Studio Code

Yesterday Microsoft held Build 2015 and presented lots of nice things to the developer community. I have always loved Microsoft's developer tools, and started using Visual Studio back in 1997, hacking away with Visual Basic and Visual C++.

Now I still use Visual Studio at work, but for my personal projects, I mostly do web development in PHP and sometimes ASP.NET MVC. For the PHP projects, I have been using Komodo Edit for a while now, and am happy with it.

Visual Studio Code

installing-ms-code
After a quick download and a smooth installation, Visual Studio Code booted. I opened a folder full of PHP and JavaScript files. Syntax highlighting, bracket matching, syntax errors and warnings work really well for PHP, JavaScript, HTML and CSS, but PHP IntelliSense isn't included in this Preview version, which unfortunately means I won't be switching. Yet. However, JavaScript IntelliSense is amazing! It found a rookie mistake for me...
Don't do bitwise operations on bool

IntelliSense in Visual Studio Code is really good in every language it supports. Code completion and suggestions for JavaScript, HTML, CSS, SASS and C# are instant, but some features are missing from HTML IntelliSense.

A couple of HTML IntelliSense suggestions

  • Allowed attribute values should be suggested, like when typing <link rel=", I would like a popdown list to suggest things like stylesheet and so on.
  • Element suggestion should only include elements that make sense in the context. Directly inside an <ul> element, there is no point in suggesting a <blockquote>. Only <li>, <script> and <template> elements make any sense.

Bad elements in UL

What will make me switch

The editor is really nice to work with, it feels snappy and does things well. Changing personal settings is done in JSON, which is cool, because JSON... Until PHP IntelliSense is added, and some improvements are made in HTML editing, I will stick to Komodo Edit, but I will probably switch to Visual Studio Code eventually.

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.