Pages

Wednesday, February 17, 2016

What’s new on Drupal.org? - January 2016

Following the Conversation

One of the most requested features from a wide swath of the community has been a better way to follow content on Drupal.org and receive email notifications. The issue queues have had this follow functionality for some time, but the implementation was quite specific to issues, and not easily extensible to the rest of the site.

Because of the volume of content on Drupal.org we have to be careful that our implementation will scale well. We now use a notification system based on the Message stack which functions much more generically and therefore can be applied to many content types on Drupal.org.
Follow functionality is now available for comments on Forum topics, Posts (like this one), Case Studies, and documentation Book Pages.
In the future we intend to extend this follow functionality to include notification of new revisions (for relevant content types, particularly documentation).

Community Elections for the Board


Nominations for the position of At-Large Director from the community are now open. There are two of these positions on the board, each elected on alternating years. For this year's elections process we've made several small refinements:
  • Candidates are now no longer required to display their real names on their candidate profile. We will now default to the Drupal.org username.
  • Candidates do not have to provide a photo, we will default to a generic avatar.
  • There is now an elections landing page with complete details about the elections process.
We encourage members of the community to nominate themselves!

Drupal.org Enhancements


A number of smaller enhancements made it into the January sprints as well. One of the key ones was the ability to configure an arbitrary one-off test in the issue queues against a custom branch. This is a small step towards ensuring that the DrupalCI testing framework will support the wider testing matrix required for feature branching, so that Drupal can always be shippable.
We also spent some time in January reviewing the results of the documentation survey that was placed on all existing documentation pages on the site. This information is helping to inform the next big item on the roadmap - improved Documentation section on Drupal.org.
Finally, we've continued our battle against spam with the help of Technology Supporter, Distil Networks. We've seen some very promising results in initial trials to prevent spam account registrations from happening in the first place, and will continue to work on refining our integration.

Sustaining support and maintenance


DrupalCon New Orleans Full -Site Launched!

In January we also launched the full -site for DrupalCon New Orleans with registration and the call for papers. As part of this launch, Events.drupal.org now supports multiple, simultaneous event registrations with multiple currencies, payment processors, and invoice formats. This was a significant engineering lift, but has made Events.drupal.org even more robust.
DrupalCon New Orleans is happening from May 9-13th, and will be the first North American DrupalCon after the release of Drupal 8!

DrupalCon Dublin


The next European DrupalCon will also be here before you know it, and we've been working with the local community and our designer to update the DrupalCon Dublin splash page with a new logo that we will carry through into the design for the full-site once that is ready to launch.

Permissions for Elevated Users

In January we also focused on auditing the users with elevated privileges on Drupal.org, both to ensure that they had the permissions they needed, and to enforce our principle of least-access. Users at various levels of elevated privileges were contacted to see if they were still needed, and if not those privileged roles were removed.
The following privileges were also fixed or updated: webmasters can now view a user's' public ssh keys; content moderators can administer comments and block spam users without user profile editing privileges. We also fixed taxonomy vocabulary access and now both content moderators and webmasters have access to edit tags in various vocabularies such as Issue tags, giving more community members access to clean those up and fight duplicates or unused tags.

Updates traffic now redirects to HTTPS

SSL is now the default for FTP traffic from Drupal.org and for Updates.drupal.org itself. This helps to enforce a best practice of using SSL wherever possible, and helps to address an oblique attack surface where a man-in-the-middle could potentially hijack an update for someone running their Drupal installation on an unprotected network (i.e. development environments on a personal laptop in a coffee shop).

Devwww2 Recovery

Drupal.org pre-production environments were affected by some instability in January, particulary the devwww2 server. A combination of a hard restart due to losing a NIC on the machine and some file-system level optimizations in the database containers lead to corruption on the dev site databases. Drupal.org infrastructure engineers restored the system and recovered the critical dev sites, and while some instability continues the system has been recovering more cleanly as they work to resolve the issue permanently.

Friday, November 6, 2015

Building Custom cTools Plugins in Drupal 7

cTools is one of those critical Drupal 7 modules many others depend on. It provides a lot of APIs and functionality that makes life easier when developing modules. Views and Panels are just two examples of such powerhouses that depend on it.
cTools makes available different kinds of functionality. Object caching, configuration exportability, form wizards, dialogs and plugins are but a few. A lot of the credit you would normally attribute to Views or Panels is actually owed to cTools.
Drupal logo
In this article, we are going to take a look at cTools plugins, especially how we can create our very own. After a brief introduction, we will immediately go hands on with a custom module that will use the cTools plugins to make defining Drupal blocks nicer (more in tune to how we define them in Drupal 8).

Introduction

cTools plugins in Drupal 7 (conceptually not so dissimilar to the plugin system in Drupal 8) are meant for easily defining reusable bits of functionality. That is to say, for the ability to define isolated business logic that is used in some context. The goal is to set up that context and plugin type once, and allow other modules to then define plugins that can be used in that context automatically.
If you’ve been developing Drupal sites for more than a year you’ve probably encountered cTools plugins in one shape or form. I think the first plugin type we usually deal with is the content_type plugin which allows us to create our own custom panel panes that display dynamic content. And that is awesome. Some of the others you may have encountered in the same realm of Panels are probably context and access (visibility rules). Maybe even relationships and arguments. These are all provided by cTools. Panels adds to this list by introducing layouts and styles that we normally use for creating Panels layouts and individual pane styles. These are I think the more common ones.
However, all of the above are to a certain extent a black box to many. All we know is that we need to define a hook to specify a directory and then provide an include file with some definition and logic code and the rest happens by magic. Going forward, I would like us to look into how a plugin type is defined so that if the case arises, we can create our own plugins to represent some reusable bits of functionality. To demonstrate this, we will create a module that turns the pesky hook system of defining custom Drupal blocks into a plugin based approach similar to what Drupal 8 is using.
The final code (+ a bit more) can be found in this repository if you want to follow along. And I do expect you are familiar with the steps necessary for defining custom Drupal blocks.

The block_plugin module

As I mentioned, I would like to illustrate the power of cTools plugins with a custom plugin type that makes defining Drupal 7 blocks saner. Instead of implementing the 2 main hooks (hook_block_info() and hook_block_view()) necessary to define a block, we’ll be able to have separate plugin files each responsible for all the logic related to their own block. No more switch cases and changing the hook implementation every time we need a new block. So how do we do this?
First, let’s create our block_plugin.info file to get started with our module:
name = Block Plugin
description = Using cTools plugins to define Drupal core blocks
core = 7.x
dependencies[] = ctools
Simple enough.

The plugin type

In order to define our news plugin type, inside the block_plugin.module file we need to implement hook_ctools_plugin_type() which is responsible for defining new plugin types cTools will recognize:
function block_plugin_ctools_plugin_type() {
  return array(
    'block' => array(
      'label' => 'Block',
      'use hooks' => FALSE,
      'process' => 'block_plugin_process_plugin'
    )
  );
}
In this hook we need to return an associative array of all the plugin type definitions we need keyed by the machine name of the plugin type name. Today we are only creating one called block. For more information on all the options available here, feel free to consult the plugins-creating.html help file within the cTools module. No use repeating all that information here.
The process key defines a function name that gets triggered every time cTools loads for us a plugin and is responsible for shaping or massaging the plugin data before we use it. It’s sort of a helper function that prepares the plugin for us each time so we don’t have to bother. So let’s see what we can do inside that function:
function block_plugin_process_plugin(&$plugin, $info) {
  // Add a block admin title
  if (!isset($plugin['admin title'])) {
    $exploded = explode('_', $plugin['name']);
    $name = '';
    foreach ($exploded as $part) {
      $name .= ucfirst($part) . ' ';
    }
    $plugin['admin title'] = $name;
  }

  // By default we also show a block title but this can be overwritten
  if (!isset($plugin['show title'])) {
    $plugin['show title'] = TRUE;
  }

  // Add a block view function
  if (!isset($plugin['view'])) {
    $plugin['view'] = $plugin['module'] . '_' . $plugin['name'] . '_view';
  }

  // Add a block form function
  if (!isset($plugin['configure'])) {
    $plugin['configure'] = $plugin['module'] . '_' . $plugin['name'] . '_configure';
  }

  // Add a block save function
  if (!isset($plugin['save'])) {
    $plugin['save'] = $plugin['module'] . '_' . $plugin['name'] . '_save';
  }
}
This callback receives the plugin array as a reference and some information about the plugin type. The task at hand is to either change or add data to the plugin dynamically. So what do we achieve above?
First, if the developer hasn’t defined an admin title for the block plugin, we generate one automatically based on the machine name of the plugin. This is so that we always have an admin title in the Drupal block interface.
Second, we choose to always display the title of the block so we mark the show title key of the plugin array as TRUE. When defining the block plugin, the developer has the option of setting this to FALSE in which case we won’t show a block title (subject).
Third, fourth and fifth, we generate a callback function for the block view, save and configure actions (if they haven’t already been set by the developer for a given plugin). These callbacks will be used when implementing hook_block_view(), hook_block_configure() and hook_block_save(), respectively. We won’t be covering the latter two in this article but feel free to check out the repository to see what these can look like.
And that’s pretty much all we need for defining our custom plugin type. We should, however, also implement hook_ctools_plugin_directory() which, as you may know, is responsible for telling cTools where a plugin of a certain type can be found in the current module:
function block_plugin_ctools_plugin_directory($module, $plugin) {
  if ($module == 'block_plugin' && in_array($plugin, array_keys(block_plugin_ctools_plugin_type())) ) {
    return 'plugins/' . $plugin;
  }
}
This will need to be implemented also by any other module that wants to define block plugins.

Drupal blocks

Now that we have the plugin type, let’s write the code which turns any defined block plugin into a Drupal block. Will start with the hook_block_info() implementation:
function block_plugin_block_info() {
  $blocks = array();

  $plugins = block_plugin_get_all_plugins();
  foreach ($plugins as $plugin) {
    $blocks[DELTA_PREFIX . $plugin['name']] = array(
      'info' => $plugin['admin title'],
    );
  }

  return $blocks;
}
Here we load all of the plugins using a helper function and define the minimum required information for the block. Here you can add also more information but we are keeping it simple for brevity.
We know each plugin will have a machine name (the name of the include file basically) and an admin title because we generate one in the processing phase if one doesn’t exist. The DELTA_PREFIX is a simple constant in which we define the prefix we want for the block machine name because we need to reuse it and should be able to easily change it if we want to:
define('DELTA_PREFIX', 'block_plugin_');
Our helper function we saw earlier looks like this:
function block_plugin_get_all_plugins() {
  return ctools_get_plugins('block_plugin', 'block');
}
It’s a simple wrapper around the respective cTools function. And for that matter, we also have the following function responsible for loading a single plugin by its machine name:
function block_plugin_get_plugin($name) {
  return ctools_get_plugins('block_plugin', 'block', $name);
}
This is very similar to the one before.
In order to make our Drupal block definitions complete, we need to implement hook_block_view():
function block_plugin_block_view($delta = '') {
  $plugin = block_plugin_plugin_from_delta($delta);
  if (!$plugin) {
    return;
  }

  $block = array();

  // Optional title
  if (isset($plugin['title']) && $plugin['show title'] !== FALSE) {
    $block['subject'] = $plugin['title'];
  }

  // Block content
  $block['content'] = $plugin['view']($delta);

  return $block;
}
So what’s happening here?
First, we use another helper function to try to load a plugin based on the delta of the current block and do nothing if we are not dealing with a plugin block.
Second, we build the block. If the user specified a title key on the plugin and the show title key is not false, we set the subject of the block (its title basically) as the former’s value. As for the actual block content, we simply call the view callback defined in the plugin. And that’s it.
Let us quickly see also the helper function responsible for loading a plugin based on a block delta:
function block_plugin_plugin_from_delta($delta) {
  $prefix_length = strlen(DELTA_PREFIX);
  $name = substr($delta, $prefix_length);
  $plugin = block_plugin_get_plugin($name);
  return $plugin ? $plugin : FALSE;
}
Nothing complicated going on here.

Defining block plugins

Since we told cTools that it can find block plugins inside the plugins/block folder of our module, let’s go ahead and create that folder. In it, we can add our first block inside a file with the .inc extension, for example my_block.inc:
<?php

$plugin = array(
  'title' => t('This is my block'),
);

/**
 * Returns a renderable array that represents the block content
 */
function block_plugin_my_block_view($delta) {
  return array(
    '#type' => 'markup',
    '#markup' => 'Yo block!'
  );
}
Like we do with all other plugins (content_type, context, etc), the plugin definition is in the form of an array inside a variable called $plugin. And for our case all we need at this point is a title (and not even that since without it the block simply won’t show a title).
Below it, we defined our callback function to display the block. The naming of this function is important. It matches the pattern we used for it during the processing phase (module_name_plugin_name_view). If we want to name it differently, all we have to do is reference the function name in the view key of the $plugin and it will use that one instead.
And that is basically it. We can now clear our caches and go to the Block administration screen where we can find our block and add it to a region. Showing that block on the page should trigger the view callback for that block plugin and render the contents.

Custom Display Suite Fields in Drupal 8

Without question, Display Suite is one of the most popular modules in Drupal’s contributed modules history. It allows the creation of layouts, fields and exposes all sorts of other powerful tools we use to build the presentation layer of our Drupal sites.
Drupal 8 logo
One of the more powerful features of Display Suite (DS) is the ability to create custom fields that can be displayed inside DS layouts alongside the actual core field values. In Drupal 7, this has been a very popular way of building layouts and showing dynamic data that is not strictly related to the output of any Field API field on the node (or other) entity.
Display Suite has been ported and is being maintained for Drupal 8. Depending on another contributed module called Layout Plugin, the D8 version offers much of what we have available in Drupal 7 and probably even more.
In this article, we are going to look at how we can create our own Display Suite field in Drupal 8 using the new OOP architecture and plugin system. To demonstrate this, we are going to create a DS field available only on the Article nodes that can be used to display a list of taxonomy terms from a certain vocabulary. And we’re going to make it so that the latter can be configured from the UI, namely admins will be able to specify which vocabulary’s terms should be listed. Not much usefulness in this example, I know, but it will allow you to understand how things work.
If you are following along, the code we write is available in this repository inside the Demo module. So feel free to check that out.

Drupal 8 plugins

Much of the functionality that used to be declared using an _info hook in Drupal 7 is now declared using plugins in Drupal 8. For more information on using plugins and creating your own plugin types, make sure you check out a previous Sitepoint article that talks about just that.
Display Suite also uses the new plugin system to allow other modules to define DS fields. It exposes a DsField plugin type which allows us to write and maintain all the necessary logic for such a field inside a single plugin class (+ any services we might inject into it). So we no longer implement hook_ds_field_info() and return an array of field information per entity type, but create a plugin class with data straight in its annotation and the relevant logic inside its methods.

VocabularyTerms class

Let us start by creating our plugin class called VocabularyTerms inside the src/plugins/DsField folder of our custom module and annotating it for our purposes:
namespace Drupal\demo\Plugin\DsField;

use Drupal\ds\Plugin\DsField\DsFieldBase;

/**
 * Plugin that renders the terms from a chosen taxonomy vocabulary.
 *
 * @DsField(
 *   id = "vocabulary_terms",
 *   title = @Translation("Vocabulary Terms"),
 *   entity_type = "node",
 *   provider = "demo",
 *   ui_limit = {"article|*"}
 * )
 */
class VocabularyTerms extends DsFieldBase {
}
This class alone will hold all of our logic for our very simple DsField plugin. But here are a couple of remarks about what we have so far:
  • The annotation is quite self explanatory: it provides meta information about the plugin.
  • The class extends DsFieldBase which provides base functionality for all the plugins of this type.
  • At the time of writing, the ui_limit annotation has just been committed to HEAD so it might not be available in the release you are using. Limiting the availability of the field on content types and view modes can be done by overriding the isAllowed() method of the base class and performing the logic there.

Default configuration

We want our field to be configurable: the ability to select from a list of existing vocabularies. So let’s start off by providing some defaults to this configuration so that if the user selects nothing, the Tags vocabulary which comes with core will be used. For this, we have to implement the defaultConfiguration() method:
/**
 * {@inheritdoc}
 */
public function defaultConfiguration() {

  $configuration = array(
    'vocabulary' => 'tags',
  );

  return $configuration;
}
And since we only have one configuration option, we return an array with one element keyed by the configuration name. That’s about it.

Formatters

We also want to have the ability to specify from the UI if the list of taxonomy terms is a series of links to their term pages or formatter as plain text. We could implement this within the configuration realm but let’s do so using formatters instead. And it’s very simple: we implement the formatters() method and return an array of available formatters:
/**
 * {@inheritdoc}
 */
public function formatters() {
  return array('linked' => 'Linked', 'unlinked' => 'Unlinked');
}
DS Field Formatter Options
These will be available for selection in the UI under the Field heading of the Manage Display page of the content type. And we’ll be able to see the choice when we are building the actual field for display. But more on that in a second.

Configuration summary

It’s also recommended that if we are using UI defined settings, we have a summary of what has been selected as a simple string that describes it. This gets printed under the Widget heading of the Manage Display page of the content type.
DS Configuration Summary
To do this, we need to implement the settingsSummary() method and return said text:
/**
 * {@inheritdoc}
 */
public function settingsSummary($settings) {
  $config = $this->getConfiguration();
  $no_selection = array('No vocabulary selected.');

  if (isset($config['vocabulary']) && $config['vocabulary']) {
    $vocabulary = Vocabulary::load($config['vocabulary']);
    return $vocabulary ? array('Vocabulary: ' . $vocabulary->label()) : $no_selection;
  }

  return $no_selection;
}
Here we start getting more intimate with the actual configuration that was stored with the field, available by calling the getConfiguration() method on our plugin class. What we do above, then, is check if the vocabulary setting has been set, we load it based on its machine name using the Vocabulary class and return an array of strings that need to be printed.
Since we are referencing the Vocabulary class, we also need to use it at the top:
use Drupal\taxonomy\Entity\Vocabulary;
Important to note: I am using Vocabulary statically here to load an entity for the sake of brevity. It is highly recommended you inject the relevant storage using dependency injection and use that to load entities. The same goes for most classes you’ll see me referencing statically below.

Settings form

Now that we display which configuration has been chosen from the UI, it’s time to provide the actual form which will allow the user to do so. This will be made available by clicking the cogwheel under the Operations heading of the Manage Display page of the content type.
DS Field Settings Form
/**
 * {@inheritdoc}
 */
public function settingsForm($form, FormStateInterface $form_state) {
  $config = $this->getConfiguration();

  $names = taxonomy_vocabulary_get_names();
  $vocabularies = Vocabulary::loadMultiple($names); 
  $options = array();
  foreach ($vocabularies as $vocabulary) {
    $options[$vocabulary->id()] = $vocabulary->label();
  }
  $settings['vocabulary'] = array(
    '#type' => 'select',
    '#title' => t('Vocabulary'),
    '#default_value' => $config['vocabulary'],
    '#options' => $options,
  );

  return $settings;
}
Like before, we need to implement a method for this. And what we do inside is load all the taxonomy vocabulary names and prepare an array of options to be used with a Form API select list. The latter is the only element we need for this form.

Rendering the field

The last thing left to do is implement the build() method responsible for rendering the contents of our field:
/**
 * {@inheritdoc}
 */
public function build() {
  $config = $this->getConfiguration();
  if (!isset($config['vocabulary']) || !$config['vocabulary']) {
    return;
  }

  $query = \Drupal::entityQuery('taxonomy_term')
    ->condition('vid', $config['vocabulary']);

  $tids = $query->execute();
  if (!$tids) {
    return;
  }

  $terms = Term::loadMultiple($tids);
  if (!$terms) {
    return;
   }

  return array(
    '#theme' => 'item_list',
    '#items' => $this->buildTermList($terms),
  );
}
So what do we do here? First, we access the chosen vocabulary from the configuration. Then we run an EntityQuery to find all the terms in this vocabulary. Next, we load all these terms and finally we return a render array that uses the item_list theme to print our terms.
Although we don’t need it here, in most cases you’ll need to access the node entity that is currently being rendered. That is available inside the configuration array under the entity key. Moreover, under the build key you have the actual render array of the node being built. So keep this in mind and do inspect the other elements of the configuration array on your own for more information.
I would like to mention a few more things before we take a look at the actual buildTermList() method. First, for brevity, we used the EntityQuery service statically. In your project, you should inject it. Second, we used the Term class statically to load the taxonomy term entities. Again, you should inject its storage and use that for this purpose. And lastly, we should import the Term class at the top with use:
use Drupal\taxonomy\Entity\Term;
Now that this is clear, let’s take a look at our own buildTermList() method:
private function buildTermList(array $terms) {
  $config = $this->getConfiguration();
  $formatter = isset($config['field']['formatter']) && $config['field']['formatter'] ? $config['field']['formatter'] : 'unlinked';
  $items = array();
  foreach ($terms as $term) {
    $items[] = $this->buildTermListItem($term, $formatter);
  }

  return $items;
}
This method is responsible for getting the field formatter, looping through the term entities and building an array of term information that can be printed using the item_list theme. As you can see, though, the individual term entity and formatter are passed to yet another helper method to keep things nice and tidy:
private function buildTermListItem(Term $term, $formatter) {
  if ($formatter === 'linked') {
    $link_url = Url::fromRoute('entity.taxonomy_term.canonical', array('taxonomy_term' => $term->id()));
    return \Drupal::l($term->label(), $link_url);
  }

  return SafeMarkup::checkPlain($term->label());
}
Finally, in the buildTermListItem() method we either return the sanitized title of the term or a link to it depending on the formatter.
Again we see classes which should be injected but were used statically to save some space. With the risk of sounding like a broken record, keep in mind that you should inject these. For now, we must use them at the top:
use Drupal\Core\Url;
use Drupal\Component\Utility\SafeMarkup;

PHP vs Node.js

The web is ever-changing technology landscape. Server-side developers have a bewildering choice of long-standing heavy-weights such as Java, C, and Perl to newer, web-focused languages such as Ruby, Clojure and Go. It rarely matters what you choose, presuming your application works.
But how do those new to web development make an informed choice?
I hope not to start a holy war, but I’m pitting two development disciplines against each other:
  • PHP
    PHP was created by Rasmus Lerdorf in 1994. It is processed by an interpreter normally installed as a module in a web server such as Apache or Nginx. PHP code can be intermingled with HTML. That’s not necessarily best-practice, but those new to the language can produce useful code very quickly. It contributed to the language’s popularity, and PHP is now used on more than 80% of the world’s web servers. It has been helped in no small part by WordPress — a PHP Content Management System which powers a quarter of all sites.
  • Node.js
    Node.js was created by Ryan Dahl in 2009. It uses Google’s V8 JavaScript engine, which also powers client-side code in the Chrome web browser. Unusually, the platform has built-in libraries to handle web requests and responses — you don’t need a separate web server or other dependencies. Node.js is relatively new but has been rapidly gaining traction. It’s used by companies including Microsoft, Yahoo, LinkedIn and PayPal.

Where’s C#, Java, Ruby, Python, Perl, Erlang, C++, Go, Dart, Scala, Haskell, etc?

An article which compared every option would be long. Would you read it? Do you expect a single developer to know them all? I’ve restricted this smackdown to PHP and Node.js because:
  1. It’s a good comparison. They’re both open source, primarily aimed at web development and applicable to similar projects.
  2. PHP is a long-established language but Node.js is a young upstart receiving increased attention. Should PHP developers believe the Node.js hype? Should they consider switching?
  3. I know and love the languages. I’ve been developing with PHP and JavaScript since the late 1990s, with a few years of Node.js experience. I’ve dabbled in other technologies, but couldn’t do them justice in this review.
Besides, it wouldn’t matter how many languages I compared. Someone, somewhere, would complain that I hadn’t included their favorite!

About Smackdowns

Developers spend many years honing their craft. Some have languages thrust upon them, but those who reach Ninja level usually make their own choice based on a host of factors. It’s subjective; you’ll promote and defend your technology decision.
That said, Smackdowns are not “use whatever suits you, buddy” reviews. I will make recommendations based on my own experience, requirements and biases. You’ll agree with some points and disagree with others; that’s great — your comments will help others make an informed choice.

Evaluation Methodology

PHP and Node.js are compared in the following ten rounds. Each bout considers a general development challenge which could be applied to any web technology. We won’t go too deep; few people will care about the relative merits of random number generators or array sorting algorithms.
The overall winner will be the technology which wins the most rounds. Ready? Let the battle commence …

Round 1: Getting Started

How quickly can you build a “Hello World” web page? In PHP:
<?php
 echo 'Hello World!';
?>
The code can be placed in any file which is interpreted by the PHP engine — typically, one with a .php extension. Enter the URL which maps to that file in your browser and you’re done.
Admittedly, this isn’t the whole story. The code will only run via a web server with PHP installed. (PHP has a built-in server, although it’s best to use something more robust). Most OSs provide server software such as IIS on Windows or Apache on Mac and Linux, although they need to be enabled and configured. It’s often simpler to use a pre-built set-up such as XAMPP or a virtual OS image (such as Vagrant). Even easier: upload your file to almost any web host.
By comparison, installing Node.js is a breeze. You can either download the installer or use a package manager. So let’s create our web page in hello.js:
var http = require('http');
http.createServer(function (req, res) {
 res.writeHead(200, {'Content-Type': 'text/plain'});
 res.end('Hello World!');
}).listen(3000, '127.0.0.1');
You need to start the app from the terminal with node hello.js before you can visit http://127.0.0.1:3000/ in your browser. We’ve created a small web server in five lines of code and, amazing though that is, even those with strong client-side JavaScript experience would struggle to understand it.
PHP is conceptually simpler and wins this round. Those who know a few PHP statements can write something useful. It has more software dependencies, but PHP concepts are less daunting to new developers.
There’s a greater intellectual leap between knowing some JavaScript and coding Node.js apps. The development approach is different from most server-side technologies, and you need to understand fairly complex concepts such as closures and callback functions.

Round 2: Help and Support

You won’t get far without some development assistance from the official documentation and resources such as courses, forums and StackOverflow. PHP wins this round easily; it has a great manual and twenty years’ worth of Q&As. Whatever you’re doing, someone will have encountered a similar issue before.
Node.js has good documentation but is younger and there is less help available. JavaScript has been around as long as PHP, but the majority of assistance relates to in-browser development. That rarely helps.

Round 3: Language Syntax

Are statements and structures logical and easy to use?
Unlike some languages and frameworks, PHP doesn’t force you to work in a specific way and grows with you. You can start with a few multi-line programs, add functions, progress to simple PHP4-like objects and eventually code beautiful object-oriented MVC PHP5+ applications. Your code may be chaotic to start with, but it’ll work and evolve with your understanding.
PHP syntax can change between versions, but backward compatibility is generally good. Unfortunately, this has led to a problem: PHP is a mess. For example, how do you count the number of characters in a string? Is it count? str_len? strlen? mb_strlen? There are hundreds of functions and they can be inconsistently named. Try writing a few lines of code without consulting the manual.
JavaScript is comparatively concise, with a few dozen core statements. That said, the syntax attracts venom from developers because its prototypal object model seems familiar but isn’t. You’ll also find complaints about mathematical errors (0.1 + 0.2 != 0.3) and type conversion confusion ('4' + 2 == '42' and '4' - 2 == 2) — but these situations rarely cause problems, and all languages have quirks.
PHP has benefits, but I’m awarding round three to Node.js. The reasons include:
  1. JavaScript remains the world’s most misunderstood language — but, once the concepts click, it makes other languages seem cumbersome.
  2. JavaScript code is terse compared to PHP. For example, you’ll no longer need to translate to/from JSON and — thankfully — UTF-8.
  3. Full-stack developers can use JavaScript on the client and server. Your brain doesn’t need to switch modes.
  4. Understanding JavaScript makes you want to use it more. I couldn’t say the same for PHP.

Round 4: Development Tools

Both technologies have a good range of editors, IDEs, debuggers, validators and other tools. I considered calling a draw but there’s one tool which gives Node.js an edge: npm — the Node Package Manager. npm allows you to install and manage dependencies, set configuration variables, define scripts and more.
PHP’s Composer project was influenced by npm and is better in some respects. However, it’s not provided with PHP by default, has a smaller active repository and has made less of an impact within the community.
npm is partially responsible for the growth of build tools such as Grunt and Gulp which have revolutionized development. PHP developers will probably want/need to install Node.js at some point. The reverse isn’t true.

Round 5: Environments

Where can the technologies be used and deployed? Which platforms and ecosystems are supported? Web developers often need to create applications which aren’t strictly for the web, e.g. build tools, migration tools, database conversion scripts, etc.
There are ways to use PHP for desktop and command-line app development. You won’t use them. At heart, PHP is a server-side development technology. It’s good at that job but is rarely stretched beyond those boundaries.
A few years ago, JavaScript would have been considered more restrictive. There were a few fringe technologies but its main place was in the browser. Node.js has changed that perception and there has been an explosion of JavaScript projects. You can use JavaScript everywhere — in the browser, on the server, terminal, desktop and even embedded systems. Node.js has made JavaScript ubiquitous.

Round 6: Integration

Development technologies are restricted unless they can integrate with databases and drivers. PHP is strong in this area. It’s been around for many years and its extensions system allow direct communication with a host of popular and obscure APIs.
Node.js is catching up fast, but you may struggle to find mature integration components for older, less-popular technologies.

Round 7: Hosting and Deployment

How easy is deploying your shiny new app to a live web server? It’s another clear win for PHP. Contact a random selection of web hosting companies and you’ll discover the majority offer PHP support. You’ll probably get MySQL thrown in for a bargain price. PHP is considerably easier to sandbox and more risky extensions can be disabled.
Node.js is a different beast and server-side apps run permanently. You’ll need a real/virtual/cloud or specialist server environment, ideally with root SSH access. That’s a step too far for some hosts, especially on shared hosting where you could bring down the whole system.
Node.js hosting will become simpler, but I doubt it’ll ever match the ease of FTP’ing a few PHP files.

Round 8: Performance

PHP is no slouch and there are projects and options which make it faster. Even the most demanding PHP developer rarely worries about speed but Node.js performance is generally better. Of course, performance is largely a consequence of the experience and care taken by the development team but Node.js has several advantages…

Fewer Dependencies

All requests to a PHP application must be routed via a web server which starts the PHP interpreter which runs the code. Node.js doesn’t need so many dependencies and, while you’ll almost certainly use a server framework such as Express, it’s lightweight and forms part of your application.

A Smaller, Faster Interpreter

Node.js is smaller and nimbler than the PHP interpreter. It’s less encumbered by legacy language support and Google has made a huge investment in V8 performance.

Applications are Permanently On

PHP follows the typical client-server model. Every page request initiates your application; you load configuration parameters, connect to a database, fetch information and render HTML. A Node.js app runs permanently and it need only initialize once. For example, you could create a single database connection object which is reused by everyone during every request. Admittedly, there are ways to implement this type of behavior in PHP using systems such as Memcached but it’s not a standard feature of the language.

An Event-driven, Non-Blocking I/O

PHP and most other server-side languages use an obvious blocking execution model. When you issue a command such as fetching information from a database, that command will complete execution before progressing to the next statement. Node.js doesn’t (normally) wait. Instead, you provide a callback function which is executed once the action is complete, e.g.
// fetch records from a NoSQL database
DB.collection('test').find({}).toArray(process);
console.log('finished');

// process database information
function process(err, recs) {
 if (!err) {
  console.log(recs.length + ' records returned');
 }
}
In this example, the console will output ‘finished’ before ‘N records returned’ because the process function is called when all the data has been retrieved. In other words, the interpreter is freed to do other work while other processes are busy.
Note that situations are complex and there are caveats:
  • Node.js/JavaScript runs on a single thread while most web servers are multi-threaded and handle requests concurrently.
  • Long-running JavaScript processes for one user prevent code running for all other users unless you split tasks or use Web Workers.
  • Benchmarking is subjective and flawed; you’ll find examples where Node.js beats PHP and counter examples where PHP beats Node.js. Developers are adept at proving whatever they believe!
  • Writing asynchronous event-driven code is complex and incurs its own challenges.
I can only go from experience: my Node.js applications are noticeably faster than PHP equivalents. Yours may not be but you’ll never know until you try.

Round 9: Programmer Passion

This may be stretching the “general web development challenge” objective but it’s important. It doesn’t matter whether a technology is good or bad if you dread writing code every day.
It’s a little difficult to make comparisons but relatively few PHP developers are passionate about the language. When was the last time you read a PHP article or saw a presentation which captivated the audience? Perhaps everything has been said? Perhaps there’s less exposure? Perhaps I’m not looking in the right places? There are some nice features arriving in PHP7 but the technology has been treading water for a few years. That said, few PHP developers berate the language.
JavaScript splits the community. There are those who love it and those who hate it; few developers sit on the fence. However, response to Node.js has been largely positive and the technology is riding the crest of a wave. This is partly because it’s new and the praise may not last but, for now, Node.js wins this round.

Round 10: The Future

It doesn’t particularly matter which server-side language you use; it will continue to work even if the project is abandoned (yay ColdFusion!) Usage has possibly plateaued but many continue to use PHP. It’s a safe bet and support looks assured for another twenty years.
The ascent of Node.js has been rapid. It offers a modern development approach, uses the same syntax as client-side development and supports revolutionary HTML5 features such as web sockets and server-sent events. There has been some confusion regarding forks of the language but usage continues to grow at an exponential rate.
Node.js will inevitably eat into PHP’s market share but I doubt it will overtake. Both technologies have a bright future. I declare this round a draw.

The Overall Winner

The final score: five rounds to Node.js, four to PHP and one draw. The result was closer than I expected and could have gone either way.
Node.js has a steep learning curve and isn’t ideal for novice developers but it wins this smackdown. Just. If you’re a competent JavaScript programmer who loves the language, Node.js doesn’t disappoint. It feels fresher and offers a liberating web development experience — you won’t miss PHP.
But don’t discount it. PHP is alive and there’s little reason to jump on the Node.js bandwagon because it looks faster, newer or trendier. PHP is easier to learn yet supports proficient professional programming techniques. Assistance is everywhere and deployment is simple. Even die-hard Node.js developers should consider PHP for simpler websites and apps.
My advice: assess the options and and pick a language based on your requirements. That’s far more practical than relying on ‘vs’ articles like this!