PHPカンファレンス2015で英語を勉強する

先週末、東京でPHPカンファレンスが行われたわけですが、今回のPHPカンファレンスは英語でのセッションが結構ありました。

これらを聴くだけでも英語の勉強になるんじゃないかと思います。

Performance Testing for Modern Apps by Dustin Whittle

最初は、パフォーマンステストのツールに関するセッションです。結構早口です。最初の方は音声が記録されてないようです。

From PHP to Machine Code by Juozas Kaziukėnas

PHPカンファレンスの中で最も面白かったセッションです。

Speeding up the Web with PHP 7 by Rasmus Lerdorf

RasmusさんによるPHP7に関するセッションです。ゆっくりとしたスピーチです。逐次通訳付きです。

なお、PHP7のテスト環境を用意してみようと思った方は、「PHP7のテスト環境を構築するための少しだけ長い道のり」が参考になるかも知れません。

Database Theory Models and Abstractions by Eugene Cook

データベースに関する基本的な内容のセッションでした。しかし、最も早口で休みなく話し続けます。

おまけ

PHPとは全く関係ないですが、英語の勉強したいという人には以下をお薦めしておきます。

Tags: php

PHP7のテスト環境を構築するための少しだけ長い道のり

昨日、東京でPHPカンファレンスが行われ、Rasmus Lerdorf氏による基調講演で「PHP7のテストに協力を」という話がありました。

「It is really easy!」ということなのでやってみたのですが、少しつまづきましたのでその記録です。

まずは、記述通りにgit cloneしてvagrant upします。

$ git clone https://github.com/rlerdorf/php7dev.git
$ cd php7dev
$ vagrant up

しかし、Boxのダウンロードに失敗します。

実は昨日の夜中にもやってみて、今日もやってみたのですが、同じく失敗したのでRasmusさんに確認してみました。

こちらのネットワークの問題ということで、別のネットワークから試したら、すんなりダウンロードできました。

$ wget https://vagrantcloud.com/rasmus/boxes/php7dev/versions/0.0.9/providers/virtualbox.box

(2015-10-30 追記) 新しいバージョンの0.1.0がリリースされています。

$ wget https://atlas.hashicorp.com/rasmus/boxes/php7dev/versions/0.1.0/providers/virtualbox.box

そして、ダウンロードしたBoxを手許のマシンに転送し、Vagrantに登録します。

$ vagrant box add virtualbox.box --name rasmus/php7dev

これで準備OK。あとはvagrant upするだけです。無事に仮想マシンが起動しました。

(2015-10-30 追記) 新しいバージョンの0.1.0がリリースされており、以下で記述されている不具合は解消されています。

仮想マシンにSSHします。

$ vagrant ssh
php7dev image version 0.0.9
  'makephp 7' - to update your PHP 7 build
  'newphp 56' - to switch to PHP-5.6
  Please read the documentation at https://github.com/rlerdorf/php7dev

You have mail.

phpのバージョンを確認します。

vagrant@php7dev:~$ php -v
PHP 7.0.0-dev (cli) (built: May 25 2015 16:34:33) (DEBUG)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0-dev, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies

May 25 2015、ということで少し古いですね。最新版をビルドします。

vagrant@php7dev:~$ makephp 7
Build log in /tmp/build.log
Building PHP 7.0
Already on 'master'
configuring...
compiling...
installing...
done
Building PHP 7.0-debug
Already on 'master'
configuring...
compiling...
installing...
done

無事に終わりました。phpのバージョンを確認します。

vagrant@php7dev:~$ php -v
PHP 7.1.0-dev (cli) (built: Oct  4 2015 03:14:20) ( NTS DEBUG )
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0-dev, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies

なんとPHP 7.1.0-dev!今度は最新過ぎます。

makephpを調べると7のビルド時にブランチがmasterになってました。それをPHP-7.0に修正します。

--- /usr/local/bin/makephp.orig 2015-05-24 19:01:03.897226770 +0000
+++ /usr/local/bin/makephp  2015-10-04 03:26:10.252803467 +0000
@@ -276,18 +276,18 @@
             if(!$zts || $world) {
                 echo "Building PHP 7.0\n";    
                 $conf_flags = implode(' ', $conf[$v]) . ' --disable-debug --prefix=/usr/local/php70';
-                build_it('master', $conf_flags);
+                build_it('PHP-7.0', $conf_flags);
                 echo "Building PHP 7.0-debug\n";    
                 $conf_flags = implode(' ', $conf[$v]) . ' --enable-debug --prefix=/usr/local/php70-debug';
-                build_it('master', $conf_flags);
+                build_it('PHP-7.0', $conf_flags);
             } 
             if($zts || $world) {
                 echo "Building PHP 7.0-zts\n";    
                 $conf_flags = implode(' ', $conf[$v]) . ' --disable-debug --enable-maintainer-zts --prefix=/usr/local/php70-zts';
-                build_it('master', $conf_flags);
+                build_it('PHP-7.0', $conf_flags);
                 echo "Building PHP 7.0-debug-zts\n";    
                 $conf_flags = implode(' ', $conf[$v]) . ' --enable-debug --enable-maintainer-zts --prefix=/usr/local/php70-debug-zts';
-                build_it('master', $conf_flags);
+                build_it('PHP-7.0', $conf_flags);
             }
             break;

これでいいはず。

vagrant@php7dev:~$ makephp 7

phpのバージョンを確認します。

vagrant@php7dev:~$ php -v
PHP 7.0.0-dev (cli) (built: Oct  4 2015 03:41:15) ( NTS DEBUG )
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0-dev, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies

最新のPHP 7.0になりました。

これで、PHP 7.0のテストができるようになりました!

関連

Tags: php, php7, vagrant

ci-phpunit-testのモンキーパッチ機能でexitを例外に変換したときのデバッグ方法

例えば、テスト中にちょっとデバッグしたいときに、以下のように変数をvar_dump()して確認したいとします。

--- a/application/controllers/api/Example.php
+++ b/application/controllers/api/Example.php
@@ -40,7 +40,7 @@ class Example extends REST_Controller {
         ];

         $id = $this->get('id');
-
+var_dump($id); exit;
         // If the id parameter doesn't exist return all the users

         if ($id === NULL)

通常はexitでphpunitが終了し、以下のようになり楽に確認できます。

$ ../../vendor/bin/phpunit controllers/Uri_test.php 
PHPUnit 4.7.7 by Sebastian Bergmann and contributors.

......string(1) "1"

ところが、モンキーパッチでexitを例外に変換してしまっていると、

$ ../../vendor/bin/phpunit controllers/Uri_test.php 
PHPUnit 4.7.7 by Sebastian Bergmann and contributors.

......EE

Time: 7.93 seconds, Memory: 17.75Mb

There were 2 errors:

1) Uri_test::test_api_example_users_uri
CIPHPUnitTestExitException: exit() called in Example::users_get()

/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/controllers/api/Example.php:43
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/libraries/REST_Controller.php:654
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:312
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:256
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:133
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestCase.php:106
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/tests/controllers/Uri_test.php:43
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/phpunit:36

2) Uri_test::test_api_example_users_ruri
CIPHPUnitTestExitException: exit() called in Example::users_get()

/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/controllers/api/Example.php:43
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/libraries/REST_Controller.php:654
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:312
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:256
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:133
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestCase.php:106
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/tests/controllers/Uri_test.php:49
/Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/phpunit:36

FAILURES!
Tests: 8, Assertions: 6, Errors: 2.

Generating code coverage report in Clover XML format ... done

Generating code coverage report in HTML format ... done

とこんな感じでexitで終了してくれず、変数の値をうまく確認できません。

これは、ちょっと不便です。

そこで、今日は、PsySHを導入してデバッグを楽にします。

PsySHのインストール

require-devpsy/psyshを追加してcomposer updateします。

--- a/composer.json
+++ b/composer.json
@@ -9,6 +9,7 @@
         "mikey179/vfsStream": "1.1.*",
         "kenjis/ci-phpunit-test": "1.0.x@dev",
         "satooshi/php-coveralls": "0.6.*",
-        "phpunit/phpunit": "4.7.*"
+        "phpunit/phpunit": "4.7.*",
+        "psy/psysh": "0.5.*"
     }
 }

PsySHの動作には、最新のci-phpunit-test(2015/09/05以降の1.0.x@devまたはv0.7.0以降)が必要です。

デバッグの実際

デバッグのためにexitを記載していた箇所(ブレークポイント)に、代わりにeval(\Psy\sh());を記載します。

--- a/application/controllers/api/Example.php
+++ b/application/controllers/api/Example.php
@@ -40,7 +40,7 @@ class Example extends REST_Controller {
         ];

         $id = $this->get('id');
-
+eval(\Psy\sh());
         // If the id parameter doesn't exist return all the users

         if ($id === NULL)

そして、phpunitを実行します。

$ ../../vendor/bin/phpunit controllers/Uri_test.php 
PHPUnit 4.7.7 by Sebastian Bergmann and contributors.

......Psy Shell v0.5.2 (PHP 5.5.26 — cli) by Justin Hileman

ブレークポイントで止まり、PsySHが起動されました。

ここで、調べたい変数を入力すれば、値が表示されます。

$id
=> "1"
$users
=> [
     [
       "id" => 1,
       "name" => "John",
       "email" => "john@example.com",
       "fact" => "Loves coding",
     ],
     [
       "id" => 2,
       "name" => "Jim",
       "email" => "jim@example.com",
       "fact" => "Developed on CodeIgniter",
     ],
     [
       "id" => 3,
       "name" => "Jane",
       "email" => "jane@example.com",
       "fact" => "Lives in the USA",
       0 => [
         "hobbies" => [
           "guitar",
           "cycling",
         ],
       ],
     ],
   ]

変数の一覧も取得できます。

>>> ls
Variables: $__ret__, $id, $this, $users

$__ret__はモンキーパッチ機能が使っている特別な変数です。

関数を実行することもできます。

>>> get_class($this)
=> "Example"

オブジェクトのパブリックなプロパティの一覧。

>>> ls -p $this
Class Properties: $auth_override, $benchmark, $config, $form_validation, $format, $hooks, $input, $lang, $load, $loader, $output, $router, $security, $session, $uri

オブジェクトのすべてのプロパティの一覧。

>>> ls -p -a $this
Class Properties: $_allow, $_apiuser, $_args, $_delete_args, $_enable_xss, $_end_rtime, $_get_args, $_head_args, $_insert_id, $_options_args, $_patch_args, $_post_args, $_put_args, $_query_args, $_start_rtime, $_supported_formats, $_user_ldap_dn, $allowed_http_methods, $auth_override, $benchmark, $config, $form_validation, $format, $hooks, $http_status_codes, $input, $lang, $load, $loader, $methods, $output, $request, $response, $rest, $rest_format, $router, $security, $session, $uri

オブジェクトのパブリックなメソッドの一覧。

>>> ls -m $this
Class Methods: __construct, __destruct, _remap, delete, get, get_instance, head, options, patch, post, put, query, response, set_response, users_delete, users_get, users_post, validation_errors

バックトレース。

>>> trace
 0:  eval() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/controllers/api/Example.php:43
 1:  Example->users_get() at n/a:n/a
 2:  call_user_func_array() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/libraries/REST_Controller.php:654
 3:  REST_Controller->_remap() at n/a:n/a
 4:  call_user_func_array() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:312
 5:  CIPHPUnitTestRequest->createAndCallController() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:256
 6:  CIPHPUnitTestRequest->requestUri() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php:133
 7:  CIPHPUnitTestRequest->request() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestCase.php:106
 8:  CIPHPUnitTestCase->request() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/application/tests/controllers/Uri_test.php:43
 9:  Uri_test->test_api_example_users_uri() at n/a:n/a
10:  ReflectionMethod->invokeArgs() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestCase.php:881
11:  PHPUnit_Framework_TestCase->runTest() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestCase.php:746
12:  PHPUnit_Framework_TestCase->runBare() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestResult.php:601
13:  PHPUnit_Framework_TestResult->run() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestCase.php:702
14:  PHPUnit_Framework_TestCase->run() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/Framework/TestSuite.php:738
15:  PHPUnit_Framework_TestSuite->run() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:428
16:  PHPUnit_TextUI_TestRunner->doRun() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/TextUI/Command.php:147
17:  PHPUnit_TextUI_Command->run() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/src/TextUI/Command.php:99
18:  PHPUnit_TextUI_Command::main() at /Users/kenji/work/codeigniter/ci-app-for-ci-phpunit-test/vendor/phpunit/phpunit/phpunit:36

オブジェクトのPHPDoc。

>>> doc $this
class Example extends REST_Controller

Description:
  This is an example of a few basic user interaction methods you could use
  all done with a hardcoded array

Package: CodeIgniter

Subpackage: Rest Server

Category: Controller

Author: Phil Sturgeon, Chris Kacerguis

License: MIT

Link: https://github.com/chriskacerguis/codeigniter-restserver

オブジェクトなどのソースコード。

>>> show $this::users_get
  >  33|     public function users_get()
     34|     {
     35|         // Users from a data store e.g. database
     36|         $users = [
     37|             ['id' => 1, 'name' => 'John', 'email' => 'john@example.com', 'fact' => 'Loves coding'],
     38|             ['id' => 2, 'name' => 'Jim', 'email' => 'jim@example.com', 'fact' => 'Developed on CodeIgniter'],
     39|             ['id' => 3, 'name' => 'Jane', 'email' => 'jane@example.com', 'fact' => 'Lives in the USA', ['hobbies' => ['guitar', 'cycling']]],
     40|         ];
     41| 
     42|         $id = $this->get('id');
     43| eval(\Psy\sh());
     44|         // If the id parameter doesn't exist return all the users
     45| 
     46|         if ($id === NULL)
     47|         {
     48|             // Check if the users data store contains users (in case the database result returns NULL)
     49|             if ($users)
     50|             {
     51|                 // Set the response and exit
     52|                 $this->response($users, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
     53|             }
     54|             else
     55|             {
     56|                 // Set the response and exit
     57|                 $this->response([
     58|                     'status' => FALSE,
     59|                     'error' => 'No users were found'
     60|                 ], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
     61|             }
     62|         }
     63| 
     64|         // Find and return a single record for a particular user.
     65| 
     66|         $id = (int) $id;
     67| 
     68|         // Validate the id.
     69|         if ($id <= 0)
     70|         {
     71|             // Invalid id, set the response and exit.
     72|             $this->response(NULL, REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
     73|         }
     74| 
     75|         // Get the user from the array, using the id as key for retreival.
     76|         // Usually a model is to be used for this.
     77| 
     78|         $user = NULL;
     79| 
     80|         if (!empty($users))
     81|         {
     82|             foreach ($users as $key => $value)
     83|             {
     84|                 if (isset($value['id']) && $value['id'] === $id)
     85|                 {
     86|                     $user = $value;
     87|                 }
     88|             }
     89|         }
     90| 
     91|         if (!empty($user))
     92|         {
     93|             $this->set_response($user, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
     94|         }
     95|         else
     96|         {
     97|             $this->set_response([
     98|                 'status' => FALSE,
     99|                 'error' => 'User could not be found'
    100|             ], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code
    101|         }
    102|     }

コマンドがわからないときはヘルプを表示しましょう。

>>> help
  help       Show a list of commands. Type `help [foo]` for information about [foo].      Aliases: ?                     
  ls         List local, instance or class variables, methods and constants.              Aliases: list, dir             
  dump       Dump an object or primitive.                                                                                
  doc        Read the documentation for an object, class, constant, method or property.   Aliases: rtfm, man             
  show       Show the code for an object, class, constant, method or property.                                           
  wtf        Show the backtrace of the most recent exception.                             Aliases: last-exception, wtf?  
  whereami   Show where you are in the code.                                                                             
  throw-up   Throw an exception out of the Psy Shell.                                                                    
  trace      Show the current call stack.                                                                                
  buffer     Show (or clear) the contents of the code input buffer.                       Aliases: buf                   
  clear      Clear the Psy Shell screen.                                                                                 
  history    Show the Psy Shell history.                                                  Aliases: hist                  
  exit       End the current session and return to caller.                                Aliases: quit, q      

確認が済んだら、[control]+[c]キーでphpunitを終了させましょう。

^C

という感じで、コマンドラインが好きな人にはPsySHをお薦めします。

関連

Tags: phpunit, codeigniter, testing