Upload
andrew-eddie
View
1.281
Download
6
Embed Size (px)
DESCRIPTION
A talk by Andrew Eddie to possibly help developers experienced with PHP and similar languages to make a more gentle splash in the deep-end of Node.js.
Citation preview
Node.js for PHP developers
for CampJS IV
by Andrew Eddie
@AndrewEddie
• Civil Engineer (1990) turned Software Engineer (2000)
• Discovered Basic early 80's - nerd ever since
• Cut teeth on C and C++ (late 90's vintage)
• PHP Open Source contributor
• Node acolyte
JavaScript
• Was born in 1995 (same as PHP)
• Is ECMAScript
• Shares a mostly familiar C-style syntax
• Is not Java
• Is not CoffeeScript et al
Node.js
• It's just server-side JavaScript (it is the server)
• V8 - very light core
• Single thread event loop
• Package management via NPM
• No real equivalent to things like SPL or PHP-FIG
Documentation
• https://developer.mozilla.org/en-
US/docs/Web/JavaScript
• http://nodejs.org/documentation/
• Individual modules/packages
• http://kapeli.com/dash
Interactive command line / REPL
$ php -a
php > echo "Hello World!\n";;
Hello World!
php >
$ node
> console.log('Hello World');
Hello World
undefined
>
Primitive value differences
// PHP
// * booleans
// * integers
// * floating point numbers
// * strings
// * arrays
// * objects
// * resources
// * null
// * callbacks
// JavaScript
// * boolean
// (number)
// * number
// * string
// (object)
// * object
//
// * null
// (function, which is an object)
Variable differences
<?php
// PHP
$minecraft = '1.8.1-pre3';
$spawnX = -77;
$spawnY = 63;
$spawnZ = 241;
// JavaScript
seed = -6623756275123996544;
var spawnX = -77;
var spawnY = 63,
spawnZ = 241;
var $biome = 'Savanah';
Operator differences
// PHP
$number = 1 + 2;
$string = 'mega' . 'taiga';
$string .= ' biome';
1 && 2 && 3; // true
1 && 3 && 0; // false
$perPage || 10; // true
// JavaScript
var number = 1 + 2;
var string = 'extreme' + ' hills';
string += ' biome';
1 && 3 && 2; // 2
1 && 3 && 0; // 0
perPage || 10; // 10
Falsy differences
// PHP
false;
0;
"";
"0"
null;
$x is undefined
// NAN is truthy (double), need is_nan()
array(); // Empty array
// JavaScript
false;
0;
"";
// "0" is truthy!
null;
undefined;
NaN;
// [] is truthy!
Array differences
// PHP 5.4+
$recipe = ['water', 'nether wart'];
$recipe[] = 'glass bottle';
// array_push($recipe, 'another one');
$array = new ArrayObject($recipe);
$array.count(); // 3
array_keys($array);
foreach ($array as $index => $value) {
echo "\narray[$index] = $value";
}
// Note PHP < 5.4
$recipe = array('water', 'nether wart');
// JavaScript
var recipe = ['x', 'y', 'z'];
recipe.push('4th dimension');
recipe.length; // 3 (are you sure?)
recipe.keys();
recipe.forEach(function (value, index) {
console.log(index + '=' + value);
});
Associative arrays
// PHP
$inventory = [
'pick' => 'stone',
'sword' => 'diamond',
'axe' => 'iron'
];
// JavaScript
var inventory = {
pick: 'stone',
sword: 'diamond',
axe: 'iron'
};
Object differences
// PHP
$inventory = new stdClass;
$inventory->pick = 'stone';
$inventory->sword = 'diamond';
$inventory->axe = 'iron';
// Alternatively
$inventory = (object)[
'pick' => 'stone',
'sword' => 'diamond',
'axe' => 'iron'
];
echo $inventory->pick;
$axe = 'axe';
echo $inventory->$axe;
// JavaScript
var inventory = {
pick: 'stone',
sword: 'diamond',
axe: 'iron'
};
console.log(inventory.pick);
console.log(inventory['axe']);
The same
• if
• while
• do-while
• for
• function (or closure) declaration
Switch
// PHP
switch ("") {
case false:
echo 'It is false';
break;
case "":
echo 'Empty string';
break;
default:
echo 'Did not match';
}
// It is false
// Loose == comparison!
// JavaScript
switch ("") {
case false:
console.log('It is false');
break;
case "":
console.log('Empty string');
break;
default:
console.log('Did not match');
}
// Empty string
// Strict === comparison
Synchronous try-catch
// PHP
try {
throw new Exception('Bad req', 400);
}
catch (Exception $e) {
echo $e->getMessage();
echo $e->getCode();
echo $e->getTraceAsString();
}
// JavaScript
try {
throw new Error('Bad req');
}
catch (err) {
console.log(err.message);
// No native 'code' support.
console.log(err.stack);
}
foreach / for-in
// PHP
foreach ($objOrArr as $k => $v) {
echo "Key is $k\n";
echo "- value is $v\n";
}
// JavaScript
for (k in object) {
console.log("Key is %s", k);
console.log("- value is %s",
object[k]);
}
Function defaults
// PHP
function fetch($page, $perPage = 10) {
// ...
return $page * perPage;
}
php > fetch();
Warning: Missing argument 1 for fetch(), called in php
shell code on line 1 and defined in php shell code on
line 1
// JavaScript
function fetch(page, perPage) {
perPage = perPage || 10;
return page * perPage;
}
> fetch();
NaN
>
// Workaround?
function fetch(page, perPage) {
if (page === undefined) {
throw Error('Page missing');
}
perPage = perPage || 10;
return page * perPage;
}
Invoking functions
// PHP
function getDamage($weapon, $level) {
// ...
return $damage;
}
$damage = getDamage('axe', 1);
call_user_func('getDamage', 'axe', 1);
call_user_func_array(
'getDamage',
['axe', 1]
);
// JavaScript
function getDamage(weapon, level) {
// ...
return damage;
}
var damage = getDamage('axe', 1);
getDamage.call(null, 'axe', 1);
getDamage.apply(null, ['axe', 1]);
Closures
// PHP
$mineBlock = function ($block) {
// ...
return $item;
};
$item = $mineBlock('diamond ore');
// JavaScript
var mineBlock = function (block) {
// ...
return item;
};
var item = mineBlock('diamond ore');
Function Scope
// PHP
// Global if in the main file.
$sky = 'blue';
function night() {
$sky = 'black';
}
night();
echo $sky; // blue
// JavaScript
// Always global.
sky = 'blue';
function night() {
sky = 'black';
}
night();
console.log(sky); // black
Function Scope - PHP equivalent to JavaScript
// PHP
// Global if in the main file.
$sky = 'blue';
function night() {
global $sky;
$sky = 'black';
}
night();
echo $sky; // black
// JavaScript
// Always global.
sky = 'blue';
function night() {
sky = 'black';
}
night();
console.log(sky); // black
Function Scope - JavaScript equivalent to PHP
// PHP
// Global if in the main file.
$sky = 'blue';
function night() {
$sky = 'black';
}
night();
echo $sky; // blue
// JavaScript
// Always local.
var sky = 'blue';
function night() {
var sky = 'black';
}
night();
console.log(sky); // blue
Classes?
// PHP
class Block {
public $type;
public function __construct($type) {
$this->type = $type;
}
public function getType() {
return $this->type;
}
}
$sand = new Block('sand');
// JavaScript
function Block(type) {
this.type = type;
}
Block.prototype.getType = function () {
return this.type;
}
var dirt = new Block('dirt');
"Classes" in JavaScript
• Constructors are just named functions
• Functions called with `new` return `this`
• `new` allows prototyping to work
• Upper CamelCase function names by convention
• No native equivalent to `protected`
• True `private` is possible but awkward
Inheritance - JavaScript
// PHP
class DiamondOre extends Block {
function __construct() {
this.type = 'DiamondOre';
}
}
// JavaScript
function DiamondOre() {
// Remember the original constructor
// back a slide or two took a type.
Block.call(this, 'DiamondOre');
}
DiamondOre.prototype =
Object.create(Block.prototype);
DiamondOre.prototype.constructor =
Block;
Inheritance - Node
// PHP
class IronOre extends Block {
function __construct() {
parent::__construct('IronOre');
}
function getType() {
return 'Unsmelted'
. parent::getType();
}
}
// JavaScript
var util = require('util');
function IronOre() {
Block.call(this, 'IronOre');
}
util.inherits(IronOre, Block);
IronOre.prototype.getType = function (){
return 'Unsmelted'
+ super_.getType();
}
Class closure scope - public example
// PHP
class Tool {
public $max = 5;
function register($container) {
// Pre PHP 5.4
$self = $this;
$f = function($bag) use ($self){
return count($bag)
< $self->max);
};
$container->register($f);
}
}
// JavaScript
function Tool() {
this.max = 5;
}
Tool.prototype.register = function($c){
var self = this;
var f = function (bag) {
return bag.length < self.max;
}
c.register(f);
}
Class closure scope - private example
// PHP
class Tool {
private $max = 5;
function register($container) {
// Pre PHP 5.4
$self = $this;
$f = function($bag) use ($self){
return count($bag)
< $self->max);
};
$container->register($f);
}
}
// Node module
// "max" is now private to Tool.
var max = 5;
Tool.prototype.register = function($c){
var self = this;
var f = function (bag) {
return bag.length < max;
}
c.register(f);
}
Modules
• Modules sort of equate to "class"
• Sand-boxed scope
• Returns module.exports
Modules - conceptual equivalence
// PHP
root
|- Game
| `- Block.php
|- vendor
| |- composer
| |- monolog
| `- autoload.php
|- index.php
`- package.json
----------
<?php
require './vendor/autoload.php';
use Monolog\Logger;
use Game\Block;
$log = new Logger('name');
// JavaScript
root
|- lib
| `- game
| `- index.js
|- node_modules
| `- winston
|- app.js
`- package.json
----------
// Conceptual equivalent
var winston = require('winston');
var Game = require('./lib/game');
var Block = Game.Block;
var block = new Block('DiamondOre');
Some popular modules
• Logging - Winston
• Promises - Bluebird, Q
• Utility classes - Lodash
• Web serving - Express
• ORM - Waterline, JugglingDB
• MVC/ADR - Build your own
Documentation, Testing and Build Tools
• npm (vs Composer or <cough>PEAR</cough>)
• JSDoc (vs phpDocumentor)
• Mocha (vs PHPUnit) { ui: 'exports' }
• Sinon (object mocking)
• Nock (request mocking)
• Frisby/Supertest (request functional testing)
• Istanbul (code coverage)
• Grunt (vs Phing)
What do to with non-public scope?
• True private - hard to test
• Could use the old PEAR underscore prefix
• Could reference them via a faux value
• Could use @private in the DocBlock
Thank you - Questions?
eddify.me
twitter.com/AndrewEddie
slideshare.net/AndrewEddie
delicious.com/eddieajau/node.js