社内で行った初心者向けPHP勉強会のスライドを改変したものです。

社内のiOSエンジニアを想定して資料を作成しました。

プログラム自体の初心者ではないが、PHPでのウェブサービスの作り方についてよく分からないという方向けの入門です。


この文書に記述されていること

PHPでウェブアプリを作成するための重要な知識とツールをまとめました。


この文書に記述されていないこと

文法については説明しません。 HTML,CSS,Javascriptなどのフロントエンド関連の情報もありません。


PHPのコード例

<?php
require 'vendor/autoload.php';
echo 'hoge';

PHPのコード例です。PHPは現時点(PHP5.6)ではとても平凡な文法の言語です。 他の普通の言語(Ruby,Java,C++)を一つでも知っている人であれば文法の習得は容易です。


PHP開発ツール


PHPStorm一択で

phpstorm


PHPに関するドキュメント


PHP: The Right Way

php-the-right-way

PHPによる現時点でのベストプラクティスをまとめ上げたウェブサイト。

文法に関する説明はありません。


PHP逆引きレシピ第2版

php-recipe

PHPを使ったコードの実装方法について詳細に記述されています。 文法についても学べます。

初心者に一冊与えるとしたらこれです。


Modern PHP

modern-php

PHPを使ったウェブサービスを提供する上での基本的な知識が網羅されています。 他の言語を使える人にとってのとても良いPHP入門書ですが英語です。

PHPの文法に関する説明は一部を除いてありません。

 


PHPの動かし方

<?php
echo 'hoge';

上記のソースコードはコマンドラインとしてもウェブページとしても表示させられます。


コマンドライン(php)

$ php hoge.php
hoge

スクリプト言語なので普通にコマンドラインから実行できます。 $という文字はコマンドプロンプトを意味します。実際には入力しません。


ビルトインウェブサーバー(php -s)

$ php -S localhost:8000

hoge.php

5.4以降のPHPではビルトインウェブサーバを使うことができます。Apacheやnginxを使用せずともPHPの動作確認が可能です。

一度に一つのリクエストしか受け付けてくれないので本番サーバには使用できません。


Read-eval-print loop(psysh)

PHPでは標準で 対話シェル(php-a) が用意されていますがが、あまり便利ではありません。


$ psysh
Psy Shell v0.5.2 (PHP 5.6.12 — cli) by Justin Hileman
>>>  echo 'hoge';
hoge
>>> $a = new DateTime('10 day ago');
=> DateTime {#199
     +"date": "2015-10-10 18:25:54.000000",
     +"timezone_type": 3,
     +"timezone": "Asia/Tokyo",
   }
>>> 

psyshはPHPのコードを対話型に実行できる、俗に言うREPLです。 Rubyで言うところのpryです。 入力した内容が即座に反映されるので言語やライブラリの習得に便利です。


laravelでartisan tinkerを実行する時に内部で呼ばれているのもこのpsyshです。


ライブラリのインストールとバージョン固定について(Composer,Packagist)


composerについて

サードパーティのライブラリのプロジェクト単位でのインストールやバージョン固定、依存性管理を行います。 node.jsのnpm,RubyのBundler,Pythonのpipのようなものです。 2011年に開発が開始されました。


packagistについて

composer対応PHPライブラリをまとめた公式リポジトリが Packagist です。


packagist

ダウンロード数やユーザーが付けたスターの数も確認できます。


基本的な使い方

carbonというライブラリをインストールするには以下のコマンドで行います。

$ composer require nesbot/carbon

インストールするライブラリ名やバージョンはcomposer.jsonに記述することもできます。 依存情報をテキストに記述することでgitでの版数管理が可能になります。 コマンドでインストールしたライブラリの情報もcomposer.jsonに自動的に追記されます。

{
    "require": {
        "nesbot/carbon": "^1.20"
    }
}

インストールしたライブラリはvendorディレクトリに配置されます。 インストールしたライブラリは以下のrequire文を追加するだけで利用できるようになります。 依存するライブラリが複数あってもこの一つのrequire文だけで使用できるようになります。

<?php
require 'vendor/autoload.php';
...

その他の使い方


global require

composerは基本的にプロジェクト単位のライブラリインストールを行うツールですが、 「npm install -g」のようにグローバルにインストールすることもできます。


composer global require phpunit/phpunit

global requireを実行すると、~/.composer/vendor以下にphpunitがインストールされます。

実行可能なプログラムがある場合~/.composer/vendor/bin/にインストールされます。


create-project

composer create-project laravel/laravel hoge

フレームワークの新規プロジェクトを作成するときにはcomposer create-projectが使えることがあります。上記はlaravelの例です。


PSRについて

PSRはPHP Standard Recommendationの略です。


PHPにはRoRのような極端に強いWAFが存在しないことや、 近代的な言語機能が追加される時期が遅かった(名前空間のサポートは2009年の5.3.0までありませんでした)こともあり、microなものやフルスタックなものなど非常にたくさんのWAFが乱立しています。


WAFは様々なコンポーネントから成り立っています。 かつてそれらは独自に実装されていることが多く 他のWAFのコンポーネントを相互に使うことが難しい状態でした。


2009年のphp|tekというイベントで行われたコンポーネント間相互利用に関する議論をきっかけに PHP-FIG(PHP Framework Interop Group)が発足しました。


PHP-FIGのメンバー達がコンポーネント間の相互利用を目的にコーディング規約やロガーのインターフェイスなどを整備したものがPSRです。 現在のステータスはPSR indexで見られます。


コンポーネント作成者向けですがPSR-2はコーディング規約であり普通のプログラマにも有用です。 PHPStormなどのIDEを使用していれば自然にPSR-2のスタイルに整形されます。


最近ではHTTPのメッセージングI/Fを規定するPSR-7が制定されました。 DIコンテナの規約であるPSR-11なども提案されています。


PSRとComposerの恩恵

ComposerとPSRによりフレームワーク間のコンポーネントの相互利用が容易になってきました。


Slimframework

  • ルータ、DIコンテナ、HTTPメッセージングに外部コンポーネントを利用しています。
  • DIコンテナはPSR-11を満たすどんなコンポーネントも使用できます。
  • HTTPメッセージングはPSR-7を満たすどんなコンポーネントも使用できます。

Laravel

  • Laravelは多くのSymfony2のコンポーネントを利用しています。
  • またLaravelのEloquent ORMは他のフレームワークでも利用できます。

フラットなPHPからWAFへ


フラットなPHP

フラットなPHP=1ファイルで書かれたPHP

以下はビューもロジックも混在したフラットなPHPファイルの例です。

<html>
<head></head>
<body>
<?php
$a = 'Hello'; $b = 'World';
$c = $a . ' ' . $b;
echo $c;
?>
</body>
</html>

WAFの目的

  • ビューとロジックの分離
  • ルーティング
  • HTTPのリクエストやレスポンスの処理

index.php

...
$app->get('/blog/:id', function ($id) use ($app) {
    $entry = getEntry($id);
    if (empty($entry)){
      $app->response->setStatus(404);
      $app->render('notfound.tpl');
    }else{
      $app->render('entry.tpl',array($entry));
    }
});
$app->run();
function getEntry($id){
  ... fetch data from db ...
};

entry.tpl

<html><head>
<title>{$title}</title>
</head>
<body>
{$body}
</body></html>

ビューとロジックの分離

index.php

$app->get('/blog/:id', function ($id) use ($app) {
...
    $entry = getEntry($id);
    $app->render('entry.tpl',array($entry));
...
}

entry.tpl

<html><head>
<title>{$title}</title>
</head>
<body>
{$body}
</body></html>

ルーティング

index.php

...
$app->get('/blog/:id', function ($id) use ($app) {
  // get https://hoge.com/blog/20
  ...
});
$app->get('/blog', function () use ($app) {
  // get https://hoge.com/blog
  ...
});
$app->post('/blog', function () use ($app) {
  // post https://hoge.com/blog
  ...
});

httpのリクエストを一つのファイルが一手に引き受けてURLごとに 処理を切り分けることをルーティングと言います。


HTTPのリクエストやレスポンスの処理

  //パラメータの読み込み
  $paramValue = $app->request->params('paramName');

  //HTTPステータスの変更
  $app->response->setStatus(404);

HTTPのリクエストやレスポンスを簡単に扱うための仕組みです。


WAF紹介

PSRのセクションで紹介した通りPHPにはたくさんのWAFが存在します。

以下に主だったWAFの年表があります。

A History Of PHP Frameworks/Library Collections

今回はmicroなものとmacroな物をひとつづつ紹介します。


slim

slim160

composer create-project slim/slim-skeleton hoge

PHP: The Right Way のJosh Lockhartが作成したWAFです。 Rubyで言うところのSinatraです。


laravel

laravel160

composer create-project laravel/laravel hoge

2014年にブレイクしたWAFです。 Eloquentという独自のORMを持っています。 Rubyで言うところのRailsです。


データの永続化

ウェブサイトではユーザーから受け取ったデータを保存したり取り出したりする処理が必要になることがあります。 データの保存にはデータベースが利用されます。

  • Data Access Object(PDO)
  • Active Record(Eloquent ORM)
  • Data Mapper(Doctrine2)

Data Access Object(PDO)

データベースを利用する現時点で一番原始的な方法がPDOによるものです。 テーブルとSQLを直接意識して操作する必要があります。

$dsn = 'mysql:dbname=hoge;host=localhost';
$user = 'user';
$password = 'password';

try{
    $dbh = new PDO($dsn, $user, $password);
    $sql = 'SELECT id, name FROM users';
    $stmt = $dbh->query($sql);
    while($result = $stmt->fetch(PDO::FETCH_ASSOC)){
        print($result['id']);
        print($result['name']);
    }
}catch (PDOException $e){
    print('Error:'.$e->getMessage());
    die();
}

Active Record(Eloquent ORM)

laravelでデータを永続化するにはEloquent ORMを利用します。


Eloquent ORMはRuby on Railsで有名になったActive Recordパターンを適用した ORM(Object-relational mapping)ライブラリです。


activer(出典:PofEAA)


Active Recordパターンはわかりやすい反面ベースとなる大きいクラスを継承したクラスを利用するため後述するDataMapperパターンを適用したORMに比べてパフォーマンスが劣ります。


mysql> desc users;
+------------+------------------+------+-----+---------------------+----------------+
| Field      | Type             | Null | Key | Default             | Extra          |
+------------+------------------+------+-----+---------------------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL                | auto_increment |
| name       | varchar(255)     | NO   | UNI | NULL                |                |
+------------+------------------+------+-----+---------------------+----------------+

usersというテーブルが既にあれば、Eloquentを継承したクラスを作るだけでデータベースを直感的に操作できます。

class User extends Eloquent {}

$users = User::all();   // select * from users;
$user  = User::find(1); // select * from users where id = 1;

$user = new User;
$user->name = 'John';
$user->save();          // insert into users (name) values ('John');

Data Mapper(Doctrine2)

Doctrine2はData MapperパターンをベースとしたORMです。

SymphonyフレームワークにはDoctrine2というORMコンポーネントが含まれています。


2015年9月にlaravelとdoctrine2を統合するLaravel Doctrine の1.0がリリースされました。


datamapper(出典:PofEAA)


$scientist = new Scientist(
    'Albert',
    'Einstein'
);
$scientist->addTheory(
    new Theory('Theory of relativity')
);
EntityManager::persist($scientist);
EntityManager::flush();

モデルを表現するクラス(上記例ではScientistやTheory)には複雑なロジックを入れず、 データベース操作部分はエンティティマネージャーに、複雑なビジネルロジック部分は リポジトリマネージャーに分離することができます。


自動テスト

  • unit testing
  • mock

phpunit

<?php
class StackTest extends PHPUnit_Framework_TestCase
{
    public function testPushAndPop()
    {
        $stack = array();
        $this->assertEquals(0, count($stack));

        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertEquals(1, count($stack));

        $this->assertEquals('foo', array_pop($stack));
        $this->assertEquals(0, count($stack));
    }
}

phpunitはunit testingを行うためのフレームワークです。 assertメソッドを用いてコードが期待する動作をすることをテストします。


mockery

Psy Shell v0.5.2 (PHP 5.6.12 — cli) by Justin Hileman
>>> use \Mockery as M;
=> false
>>> $calcMock = M::mock('Calc');
=> Mockery_0__Calc {#198}
>>> $calcMock->shouldReceive('sum')->with('foo','bar')->andReturn('baz');
=> Mockery\CompositeExpectation {#227}
>>> $calcMock->sum('foo','bar');
=> "baz"
>>> $calcMock->sum('foo2','bar2');
Mockery\Exception\NoMatchingExpectationException with message 'No matching handler found for Mockery_0__Calc::sum("foo2", "bar2"). Either the method was unexpected or its arguments matched no expected argument list for this method
>>> 

クラスBに依存するクラスAのユニットテストを行いたい時、 クラスBのテストをしたいのではないのでクラスBをクラスBのふりをする別のものに置き換えて テストを行います。 このようなクラスBのふりをするオブジェクトをモックと呼びます。


phpunitにもmock作成ツールはありますが使いやすいmockeryが広く使われています。 laravelのコンポーネントもmockeryによりテスト時の入れ替えが容易になっています。


終わりに

PHPでウェブアプリを作成するための重要な知識とツールをまとめました。

この文書がカバーするキーワードをすべて理解できるなら、 モダンなウェブアプリケーションを実装するためのスタートラインに立てたと言えます。