PHP knows how to group classes under a common namespace, and to create redistributable packages using PEAR. Node.js is also very good at organizing code into modular and reusable sets, but there are key differences. I'll explain them shortly, but before I'd like to talk about dolls.
My 4-year old daughter was recently offered a second-hand doll set, including 4 dolls, various clothes, furniture, a pony, and a strawberry house. She loves to dress and undress the dolls, and I'm glad to see her learning how to do that perfectly - much better than she knows how to dress herself.
The problem is that she doesn't know how to put the clothes away when she finishes playing, just like she leaves her scarf, sweater and cap on the floor when she comes back home. So it's my sad duty to put the doll clothes away in the strawberry house, and this is a pain. You see, each piece of clothing must be neatly folded and put away in a very specific place, otherwise "it's not good". So the pink socks must go into the bedroom closet in the strawberry house, but not the pink dress - no, this one must go into the entrance chest.
If you want my opinion, folding and putting doll clothes away is so boring that this task should be given to a computer. My daughter would probably never understand it, but I'm sure you will.
PHP knows how to categorize classes using namespaces. Here is an example:
PHP namespaces are a very simple feature. At compile time, the PHP parser prefixes all the class names in the file using the declared namespace. So
Sock (the class name) becomes
House\Bedroom\Closet\Sock (the fully qualified class name) - it's just string manipulation. It's enough to prevent name collision, but it often makes the code harder to read than it really should:
The Node.js equivalent of the
The last line is more specific to Node.js. Node provides a global object called
exports. The script adds the newly defined
Sock class as a property of this
exports object. In practice, this last line "exports" the class to the outside, and defines the public API of the script.
To import this
exports object into another variable, Node provides the
Although the code looks similar to the PHP version, what happens here is a little different. Node's
require() function does not just load and execute the
socks.js code directly. Instead, it returns a copy of the
exports object defined in the
Tip: Node doesn't need the
.js ending in the file path passed as parameter to
require(). Node finds the script even if you just call
require('./house/bedroom/closet/sock'). The suffix will be omitted in further examples.
requireIs The Gate To The Module System
To be fair,
exports is not really a global object. It's local to a Node module. And what's a module? In short, it's a closure. Here is what the call to
require actually does behind the curtain:
require encapsulates the
sock.js code into a closure, and executes it using the
exports object as a parameter. That's why
exports is said to be a pseudoglobal: it is seen as a global variable from within a module, but in fact it is local to this module. So all the variables defined in a module (like
Sock) are isolated and don't pollute the global namespace. Only the properties added to the
exports parameter make it to the outside:
accepted_colors variable is therefore private and cannot be accessed by any outside script that
requires the sock module. You can imagine how this can be used to create private classes, a concept nowhere to be found in PHP.
Node.js isn't at the origin of the module concept. It's part of an initiative called CommonJS, and the CommonJS "module" feature has several implementations. Node.js offers one of them, adds some syntactic sugar and a few bonus features.
sock is just the name of the variable storing the
require() return value. This "namespace" is nowhere to be found in the original
That makes aliasing trivial in Node:
This is the equivalent of the
use ... as statement in PHP:
By the way, if a module exports just a single class, you may want to use an alternative exporting method called
Where does that
module object come from? Just like
exports, it is passed to the module as a pseudoglobal. To be completely fair, the result of a call to
require('sock') looks more like the following:
Yes, that's an anonymous function calling itself. For an average PHP developer, this may be looking like voodoo, but the truth is to be found farther East.
You know these Russian nesting dolls called Matryoshka dolls? Well, Node modules are a bit like that. Small modules can nest inside larger modules:
You can require a module and reexport it. And since the
require() function is in fact a pseudoglobal, it executes in the context (and using the file path) of the current module. That makes nesting of modules extremely easy, without necessarily using long fully qualified class names as in PHP. Besides, it keeps the code readable, while PHP code following the PSR-0 standard is harder to read due to namespaces matching the directory structure.
There is much more to tell about Node's module system. But doll clothes need to be put away. Node.js module documentation is a must read if you really need to know more.
Once every cloth is neatly folded at the right place, the strawberry doll house can be closed. It has a nice handle, so I could easily give the strawberry house to another kid to freak their dad out. The same goes for libraries: you can package several scripts together, and distribute the result across your company, or the Internet.
PHP's doll house market is called PEAR. It should be the default distribution method used by library developers, but it's more and more replaced by
git clone or composer. That's probably because PEAR has many drawbacks. First, it installs libraries user- (or system-) wide, which means that all projects must share the same version of a given library. Second, creating PEAR packages is a pain due to a very verbose XML package description format. Third, publishing packages is not an easy task (having your own package registered on the main PEAR channel is heavy, and hosting your own channel is heavy, too). Last, many PEAR packages have not been updated for a while - that's not the preferred package registry anymore.
Node, on the other hand, comes with a very powerful Node Package Manager.
npm is both a distribution utility and the central repository for Node public packages. Check the list of available packages (about 7000 packages available as of writing this post) in the node registry.
If you want to publish your own package for other developers using npm, you don't need anybody's permission, you don't even need to describe all the files in your package. You just need a simple
package.json at the root of the package looking like the following:
The npm JSON package description format allows for much more metadata, but these four lines are enough to publish the package to the node public registry. To do so, just call:
npm publish .
Of course, you can also choose to publish a package only to your own private registry.
To install a public node package, just call
npm install my-little-pony
This downloads and installs the
my-little-pony package, together with all its dependencies, under the
node_modules/ subdirectory, under the current path.
Even better, if you define your own list of dependencies in the
package.json file, npm will download and install all of them for you when you call:
The greatest thing about both the module and package system of Node.js is that it solves version conflicts in the most elegant way: by allowing several versions of a module or package in the same application.
Let's say you use the
strawberry-house package AND the
girl-toys packages in a project.
girl-toys itselfs depends on the
strawberry-house package, and there are chances that it doesn't require the same version than the one you're using directly.
It doesn't matter: npm will install the
girl-toys dependencies in its own directory, and let you use your own version:
app.js // your own app node_modules/ strawberry-house/ // the one you use directly girl-toys/ node_modules/ strawberry-house/ // the one used by girl-toys my-little-pony/
With Node's encapsulated module system, you'll never have to worry about version conflicts anymore.
I learned how to fold doll clothes, I'm sure you'll learn how to use Node's powerful module system. Both are easy, and programmers tend to have more fun with the latter. As compared to the equivalent features in PHP, Node modules and packages feel very refreshing. That's probably why the Node community is so active, and the number of available packages is growing so rapidly.
Published on 07 Feb 2012