CakePHP 3 read built-in server code


I read what the CakePHP 3 built-in server is, which is start with php bin/cake.php server.

Environment

  • PHP 5.5.9
  • cakephp/cakephp 3.0.9
  • Ubuntu 14.04.2 LTS

The starting command is php bin/cake.php server, now start reading with cake.php.

cake.php

This is the first file whenever we run CakePHP3 command. There are cake , cake.bat , cake.php in bin directory, but both cake and cake.bat only calls cake.php. cake is shell script and cake.bat is bat file for Windows.

cake.php read config/bootstrap.php and load CakePHP 3 core files. And ShellDispatcher execute command. The last line is important.

The return value of CakeConsoleShellDispatcher::run($argv) will be command exit status.

Let’s look into CakeConsoleShellDispatcher::run($argv).

run

Arguments passed at new ShellDispatcher($argv) , and dispatch() executes the procedure.

The constructor of ShellDispatcher handle several procedures, but the point is $argv is set into instance variable.

dispatch

dispatch focuses on dispatching.

dispatch calls _dispatch. The substantial procedure is in _dispatch, and dispatch returns the result, exit status.

_dispatch

This method find shall code and execute it, according to arguments.

The point is findShell and rumCommand.

shiftArgs in _dispatch() take the first argument. On launching server, the first argument is 'server'. If there’s no argument or the first one is help, --help or -h, it shows help message. And only if with no argument it returns false and eventually exit status will be 1 (failed).

findShell

This calls substantial shell code. cake.php is called as php bin/cake.php bake or php bin/cake.php migrations, and $shell will be 'bake' or 'migratoins'. On launching server, it will be 'server'.

_shellExists returns class name and _createShell create instance of the returned class.

_shellExists

According to the code, we can know it gets class name by App::className.

This method returns class name if it exists, otherwise return false.

Now, look into className method in CakeCoreApp.

App

The parameters passed to className are 'server' , 'Shell' and 'Shell'. pluginSplit is defined in cakephp/src/Core/functions.php and returns [null, 'server'] here.

Configure::read read namespase setting in config.php and return it. 'App' is the namespace in default.

The variable $fullname will be 'ShellserverShell'.

Okey, now, let’s see what is returned by _classExistsInBase.

_classExistsInBase

_classExistsInBase is the method to check the class is defined or not. The first parameter is class name and the second one is namespace, both are string.

This method calls class_exists. If the given class is defined, it returns true, otherwise it returns false.

In className method, _classExistsInBase is called twice. The first call is _classExistsInBase('ShellserverShell', 'App') , and the second one is _classExistsInBase('ShellserverShell', 'Cake').

At the second call, unfortunately, class_exists('CakeShellserverShell') is executed and false is returned. The return value is changed according to OS, but it’s not worrying. After that, _classExistsInBase('ShellServerShell', 'Cake') will be called.

_classExistsInBase returns false false, so _shellExists also returns false and the first if clause in findShell will be executed.

Now, let’s look into _handleAlias($shell).

_handleAlias

_handleAlias compares with registered aliases and if the given parameter is not registered, change it from snake case to camel case and return it. If 'abc_def.ghi' is given , Abc_Def.Ghi will be returned (if it’s not registered).

static::alias('server') returns false. I will read the code around alias some day.

CakeUtilityInflector::camelize' also changes snake case to camel case, with caching.

After _handleAlias, $shell changes to 'Server', and fortunately _shellExists('Server') will be executed. Before, _shellExists('server') returns false because of the lower case first letter of 'server'. But this time, _shellExists returns App::className('Server') which returns CakeShellServerShell, and _createShell('CakeShellServerShell', 'server') will be executed.

So php bin/cake.php Server also can launch server.

_createShell

This method creates instance of given class. The most important is new $className().

pluginSplit appears again, and set $plugin null this time. $instance->plugin is the instance variable defined in CakeConsoleShell, which is inherited by CakeShellServerShell, and its default value is null.

Now $Shell contains instance of shell class, that is the return value of findShell. dispatch() remains 2 lines.

$Shell->initialize() calls initialize method in ShellServer.

initialize

In here, set default value into instance variables.

WWW_ROOT is defined in path.php read by bootstrap.php. It is the absolute path of webroot directory.

And next, runCommand will be executed, which is defined in Shell class.

runCommand

This method handles given options first, and execute procedure according to options. On launching server, main method of ServerShell class by return call_user_func_array([$this, 'main'], $this->args); line and the server will launch. getOptionParser(), startup() and call_user_func_array([$this, 'main'], $this->args) are the points.

Take attention to $this->getOptionParser(). The method executed here is not of Shell, but of ServerShell which inherits Shell.

Let’s look into getOptionParser, which handle given options.

getOptionParser

According to the code, we can see what parameters can be used on server launching. The return value is the instance of ConsoleOptionParser class.

Acceptable Option
H, host host name or ip address
p, port port number
d, document_root document root

The options we can set is initialized in initialize method of ServerShell.

And getOptionParser of ServerShell calls one of the parent class, Shell. And it set options by description and addOption, which is unique for ServerShell.

parse

This method set instance variables according to command arguments. Set parameters into $params variable by calling some methods.

The return value is [$params, $arg] and $arg contains values except option. It is blank array in this time, server launching.

After option setting by parse, it prepares for launching server by startup .

startup

This method applies parameter set by parse method.

The code set host, port and document root set by parse method, and calls startup() of parent class, which shows welcome message.

preg_match changes c:docroot to cdocroot. It is for Windows system, I guess.

main

We can reach the code that launch server finally. call_user_func_array calls main method of this class.

The result is this, php -S host:port -t documentroot documentroot/index.php.

The variable $port seems to be not used ….

According to PHP, -S specifies host and port, -t specifies document root, and last argument specifies router script. Router script can define what procedure should be executed according to request url. As default in CakePHP 3, index.php is the router script and it returns false when existing file is requested for preventing handling by php.