はじめに
この記事はリブセンスアドベントカレンダー2015(その2)の4日目です。「Livesense(その2) Advent Calendar」ってまるでリブセンスの二軍みたいですな。
PHPNGと呼ばれPHP6をスキップしたPHP7は12月3日リリース予定、Symfony 3.0.0 は11月30日にリリースされたばかりです。MariaDB 10.1.9は11月23日リリースされた10.1系の最新版です。すぐにでも導入したい!!!!・・・と思ってる人は多くないでしょうが、やはり気になる存在であるのは確か。でも、検証するにしても 環境周りでつまづくとだるい・・いう人には、もしかしたら参考になるかもしれません。
なお、この記事を書いている現在(2015/12/3夕刻)はまだPHP7の公式リリースはされていません。なので、直接 github からリポジトリを持ってきています。そこのところはご容赦ください。
無事リリースされたようです。おめでとう!!
追記>
事前準備
適当に Amazon LinuxからEC2を立ち上げる。
ビルドに必要な基本的なツールをセットアップしておきます。
$ sudo yum groupinstall -y 'Development Tools'
$ sudo yum groupinstall -y "Development Libraries"
$ sudo yum install -y git
MariaDB10.1.9のインストール
maria リポジトリを追加する。sudo vi /etc/yum.repos.d/mariadb.repo
して、
[mariadb]
name=MariaDB
baseurl=http://yum.mariadb.org/10.1.9/centos6-x86/
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1
して、
$ sudo yum -y install MariaDB-devel MariaDB-client MariaDB-server
yum install する。
動作確認。
$ sudo service mysql start
Starting MySQL. SUCCESS!
$ mysql -uroot
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 10.1.9-MariaDB MariaDB Server
Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]>
drwxrwxr-x 2 ec2-user ec2-use
サービス名もコマンド名もmysqlだが、接続先はMariaDB 10.1.9 になってることが確認できる。ここまではよし。
PHP7.0.0をソースからコンパイルしてインストール
先に依存しそうなパッケージを入れておく
$ sudo yum install -y libxml2-devel libcurl-devel openssl-devel libpng-devel freetype-devel libmcrypt-devel libicu-devel libxslt-devel
グループインストールと重複もあるがまあよし。
コード取得と ./configure
php-srcをクローンしてくる。
$ git clone https://github.com/php/php-src
$ cd php-src
$ git checkout refs/tags/php-7.0.0
Note: checking out 'refs/tags/php-7.0.0'.
コレダ。ブランチには php-7.0
ってのもあるがそちらは違うので注意する。リリースタグを見る。
$ ./buildconf --force
これで ./configure が生成されるので、
$ ./configure \
--enable-mbstring \
--with-mysqli \
--with-pdo-mysql \
--with-mysqli \
--enable-pcntl \
--enable-fpm \
--with-fpm-user=nginx \
--with-fpm-group=nginx \
--with-openssl \
--with-pcre-regex \
--with-zlib \
--with-curl \
--with-mhash \
--with-xsl \
--with-mcrypt \
--with-pear \
--enable-exif \
--enable-ftp \
--with-gd \
--enable-gd-native-ttf \
--enable-gd-jis-conv \
--without-unixODBC \
--disable-posix \
--disable-sysvmsg \
--disable-sysvshm \
--disable-sysvsem \
--disable-debug \
--enable-intl \
--with-config-file-path=/etc
してあげよう。
ビルドするぞ
あとは
$ make
して、
$ sudo make install
するだけだ。
2015/12/3時点のpear.php.net ダウン問題<ここから>
・・・と、sudo make install
するだけかと思ったら、
$ sudo make install
/bin/sh /home/ec2-user/php-src/libtool --silent --preserve-dup-deps --mode=install cp ext/opcache/opcache.la /home/ec2-user/php-src/modules
Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-
...
Installing helper programs: /usr/local/bin/
program: phpize
program: php-config
Installing man pages: /usr/local/php/man/man1/
page: phpize.1
page: php-config.1
Installing PEAR environment: /usr/local/lib/php/
--2015-12-03 00:06:19-- https://pear.php.net/install-pear-nozlib.phar
pear.php.net (pear.php.net) をDNSに問いあわせています... 5.35.241.22
pear.php.net (pear.php.net)|5.35.241.22|:443 に接続しています... 接続しました。
エラー: 証明書に記載されている別名とホスト名 `pear.php.net' が一致しません
pear.php.net に安全の確認をしないで接続するには、`--no-check-certificate' を使ってください。
make: *** [install-pear] エラー 5
ほう・・・?
$ curl -I -k https://pear.php.net
HTTP/1.1 200 OK
Date: Thu, 03 Dec 2015 00:08:10 GMT
Server: Apache/2.4.10 (Debian) SVN/1.8.10 mod_fastcgi/mod_fastcgi-SNAP-0910052141 mod_jk/1.2.37 PHP/5.6.14-0+deb8u1 mod_python/3.3.1 Python/2.7.9 OpenSSL/1.0.1k
X-Powered-By: PHP/5.6.14-0+deb8u1
Set-Cookie: roundcube_sessid=xxxxxxxxxxxxxxxxxxxx; path=/; secure; HttpOnly
Expires: Thu, 03 Dec 2015 00:08:10 GMT
Last-Modified: Thu, 03 Dec 2015 00:08:10 GMT
X-DNS-Prefetch-Control: off
Cache-Control: private, no-cache, no-store, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
X-Frame-Options: sameorigin
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
$ curl -I https://pear.php.net
curl: (51) Unable to communicate securely with peer: requested domain name does not match the server's certificate.
確かに。つながらない。
twitterでも阿鼻叫喚である。
しかし「何の成果も得られませんでしたァァ」とやるのも芸がない。
よし・・・Qiitaに記事を書くためだ。仕方がない。https では繋げないが、http だとつながることは確認した。
http にしよう。端から sed -i "s|https://pear.php.net|http://pear.php.net|g"
しちまおう。目標をgrepしてreplace、目標をgrepしてreplace。
こういうことは仕事ではやっちゃだめですよ。
ここまで>
$ make install
/bin/sh /home/ec2-user/php-src/libtool --silent --preserve-dup-deps --mode=install cp ext/opcache/opcache.la /home/ec2-user/php-src/modules
Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20151012/
Installing PHP CLI binary: /usr/local/bin/
Installing PHP CLI man page: /usr/local/php/man/man1/
Installing PHP FPM binary: /usr/local/sbin/
Installing PHP FPM config: /usr/local/etc/
Installing PHP FPM man page: /usr/local/php/man/man8/
Installing PHP FPM status page: /usr/local/php/php/fpm/
Installing phpdbg binary: /usr/local/bin/
Installing phpdbg man page: /usr/local/php/man/man1/
Installing PHP CGI binary: /usr/local/bin/
Installing PHP CGI man page: /usr/local/php/man/man1/
Installing build environment: /usr/local/lib/php/build/
Installing header files: /usr/local/include/php/
Installing helper programs: /usr/local/bin/
program: phpize
program: php-config
Installing man pages: /usr/local/php/man/man1/
page: phpize.1
page: php-config.1
Installing PEAR environment: /usr/local/lib/php/
--2015-12-03 00:20:44-- http://pear.php.net/install-pear-nozlib.phar
pear.php.net (pear.php.net) をDNSに問いあわせています... 5.35.241.22
pear.php.net (pear.php.net)|5.35.241.22|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 3579275 (3.4M) [application/octet-stream]
`pear/install-pear-nozlib.phar' に保存中
install-pear-nozlib.phar 100%[==================================================================>] 3.41M 1.12MB/s 時間 3.1s
2015-12-03 00:20:48 (1.12 MB/s) - `pear/install-pear-nozlib.phar' へ保存完了 [3579275/3579275]
[PEAR] Archive_Tar - installed: 1.4.0
[PEAR] Console_Getopt - installed: 1.4.1
[PEAR] Structures_Graph- installed: 1.1.1
[PEAR] XML_Util - installed: 1.3.0
[PEAR] PEAR - installed: 1.10.1
Wrote PEAR system config file at: /usr/local/etc/pear.conf
You may want to add: /usr/local/lib/php to your php.ini include_path
/home/ec2-user/php-src/build/shtool install -c ext/phar/phar.phar /usr/local/bin
ln -s -f phar.phar /usr/local/bin/phar
Installing PDO headers: /usr/local/include/php/ext/pdo/
やってしまった。何かを失った気がする。すみません。
実行されるPHPのバージョンを確認する
$ php -v
PHP 7.0.0 (cli) (built: Dec 2 2015 23:33:14) ( NTS )
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2015 Zend Technologies
気をとりなおす。よし。devがついてない。俺の勝ちだ。「本当に2倍速くなったの、アナタ?」みたいなやつは今回のコンセプトではないのでやりません。
<追記>セットアップスクリプトの出力の最後で `You may want to add: /usr/local/lib/php to your php.ini include_path`とか言われてるの気がつかなかった。後で確認しよう。追記>
Webアプリを作ってみて動作確認する
WAF(Symfony 3)のインストール
Symfony 3.0.0 をインストールする。
公式サイト通りに Symfony のインストーラを先に。
$ sudo curl -LsS http://symfony.com/installer -o /usr/local/bin/symfony
$ sudo chmod a+x /usr/local/bin/symfony
$ symfony --verion
Symfony Installer (1.4.4)
=========================
...
インストーラバージョンは1.4.4。
プロジェクト作成
symfony new <PROJECT NANE>
する。ここではプロジェクト名 newton とします。ちなみにニュートンはうちの猫の名前です。
$ symfony new newton
Downloading Symfony...
...
Preparing project...
✕ Symfony 3.0.0 was successfully installed but your system doesn't meet its
technical requirements! Fix the following issues before executing
your Symfony application:
* date.timezone setting must be set
> Set the "date.timezone" setting in php.ini* (like Europe/Paris).
After fixing these issues, re-check Symfony requirements executing this command:
php newton/bin/symfony_requirements
Then, you can:
* Change your current directory to /home/ec2-user/php-src/newton
* Configure your application in app/config/parameters.yml file.
* Run your application:
1. Execute the php bin/console server:run command.
2. Browse to the http://localhost:8000 URL.
* Read the documentation at http://symfony.com/doc
チェックスクリプトを走らせるとエラーが!
ついでに表記が symfony 2 になってることを確認するが、まあこれはご愛嬌。
$ php newton/bin/symfony_requirements
Symfony2 Requirements Checker
> PHP is using the following php.ini file:
WARNING: No configuration file (php.ini) used by PHP!
> Checking Symfony requirements:
.....E......................W....WW.....
[ERROR]
Your system is not ready to run Symfony2 projects
Fix the following mandatory requirements
* date.timezone setting must be set
> Set the "date.timezone" setting in php.ini* (like Europe/Paris).
Optional recommendations to improve your setup
* posix_isatty() should be available
> Install and enable the php_posix extension (used to colorize the
> CLI output).
* a PHP accelerator should be installed
> Install and/or enable a PHP accelerator (highly recommended).
* short_open_tag should be disabled in php.ini
> Set short_open_tag to off in php.ini*.
Note The command console could use a different php.ini file
~~~~ than the one used with your web server. To be on the
safe side, please check the requirements from your web
server using the web/config.php script.
おお。php.ini がないのと、timezone の指定が必須だ。
ソースからコンパイルした場合、php.ini は自動では置かれない。ということで、ソースディレクトリにあるテンプレートファイルを適宜 /etc/php.ini にコピーする。ついでに timezone を "Asia/Tokyo" にしておく。
sudo cp php.ini-development /etc/php.ini
vi /etc/php.ini
#=> date.timezone = Asia/Tokyo
もういちどチェックスクリプトを実行すると、、、
[OK]
Your system is ready to run Symfony2 projects
よし。OKが出た。
Composerをダウンロード
ちょっぴりサボってDLしたものをそのまんま使う。
$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar install
テストサーバの実行
プロジェクトディレクトリに移動し、php bin/console server:run 0.0.0.0
する。Symfony 2までのドキュメントだと app/console になってるが、bin/console に変わっている。あと、0.0.0.0 に明示的に指定してバインドしないと外から繋がらない。
$ cd newton/
$ php bin/console server:run 0.0.0.0
[OK] Server running on http://0.0.0.0:8000
// Quit the server with CONTROL-C.
起動した!
画面下側に出ているのはビルトインの開発ツール。すごく高機能で、システムプロファイラも当然のようにビルトインである。開発しやすそう。
こういう環境構成チェッカー的な機能ってわりとPHP文化な気がする。
MariaDBへの接続とデータベースの作成
Symfony 3.0.0 が自動生成したプロジェクトはデフォルトでMySQLドライバを利用する。接続情報としては、localhost の 3306 にパスワードなしの root である。つまり、この状態だとデータベースの接続設定についてはノーコンフィギュレーションでいける。
DBを作成。作られるデータベース名は symfony。
$ php bin/console doctrine:database:create
Created database `symfony` for connection named default
確認する。できてる。
$ mysql -uroot -e "show databases;"
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| symfony |
| test |
+--------------------+
Crud の作成(よくある Scaffold的なやつ)
まあよくある Post テーブルを作ってみる。作者と本文と投稿日があるやつ。以下では対話形式で(楽しかったので)やってるけど、絶対インタラクションモードはトラブルのもとだ。詳しくはドキュメントを参照してください。
$ php bin/console doctrine:generate:entity
Welcome to the Doctrine2 entity generator
This command helps you generate Doctrine2 entities.
First, you need to give the entity name you want to generate.
You must use the shortcut notation like AcmeBlogBundle:Post.
The Entity shortcut name: AppBundle:Post
Determine the format to use for the mapping information.
Configuration format (yml, xml, php, or annotation) [annotation]:
Instead of starting with a blank entity, you can add some fields now.
Note that the primary key will be added automatically (named id).
Available types: array, simple_array, json_array, object,
boolean, integer, smallint, bigint, string, text, datetime, datetimetz,
date, time, decimal, float, binary, blob, guid.
New field name (press <return> to stop adding fields): author
Field type [string]:
Field length [255]:
Is nullable [false]:
Unique [false]:
New field name (press <return> to stop adding fields): body
Field type [string]:
Field length [255]:
Is nullable [false]:
Unique [false]:
New field name (press <return> to stop adding fields): created_at
Field type [datetime]:
Is nullable [false]:
Unique [false]:
New field name (press <return> to stop adding fields):
Entity generation
> Generating entity class src/AppBundle/Entity/Post.php: OK!
> Generating repository class src/AppBundle/Repository/PostRepository.php: OK!
Everything is OK! Now get to work :).
ショートカットネームは必ず指定しなければいけないが、とりあえず初期に作成されている AppBundle の下に Post を作るので AppBundle:Post
なのだとわかれば良い。
地味に面白いのが、created_at ってフィールド名に自動で datetime 型が推奨されていること。賢い。
以下の2ファイルができている。
- src/AppBundle/Entity/Post.php
- src/AppBundle/Repository/PostRepository.php
いわゆるよくある「モデル」クラスは Post.phpの方。PostRepositoryはイメージ的には「Postテーブル」みたいな概念である。RailsのActiveRecordだとPostクラスのスタティックメソッドにするようなのがPostRepositoryに置かれていると思えば良い。最初ピンとこなかったけど、考えてみればエンティティ(レコード)クラスとテーブルは別クラスだろっていうのは一理ある。
さて、crudを生成する。
$ php bin/console doctrine:generate:crud
Welcome to the Doctrine2 CRUD generator
This command helps you generate CRUD controllers and templates.
First, give the name of the existing entity for which you want to generate a CRUD
(use the shortcut notation like AcmeBlogBundle:Post)
The Entity shortcut name: AppBundle:Post
By default, the generator creates two actions: list and show.
You can also ask it to generate "write" actions: new, update, and delete.
Do you want to generate the "write" actions [no]? yes
Determine the format to use for the generated CRUD.
Configuration format (yml, xml, php, or annotation) [annotation]:
Determine the routes prefix (all the routes will be "mounted" under this
prefix: /prefix/, /prefix/new, ...).
Routes prefix [/post]:
Summary before generation
You are going to generate a CRUD controller for "AppBundle:Post"
using the "annotation" format.
Do you confirm generation [yes]?
CRUD generation
Generating the CRUD code: OK
Generating the Form code: OK
Updating the routing: OK
Everything is OK! Now get to work :).
ルートを確認する。これも見やすいなぁ。
$ php bin/console debug:route
-------------------------- ---------- -------- ------ -----------------------------------
Name Method Scheme Host Path
-------------------------- ---------- -------- ------ -----------------------------------
_wdt ANY ANY ANY /_wdt/{token}
_profiler_home ANY ANY ANY /_profiler/
_profiler_search ANY ANY ANY /_profiler/search
_profiler_search_bar ANY ANY ANY /_profiler/search_bar
_profiler_info ANY ANY ANY /_profiler/info/{about}
_profiler_phpinfo ANY ANY ANY /_profiler/phpinfo
_profiler_search_results ANY ANY ANY /_profiler/{token}/search/results
_profiler ANY ANY ANY /_profiler/{token}
_profiler_router ANY ANY ANY /_profiler/{token}/router
_profiler_exception ANY ANY ANY /_profiler/{token}/exception
_profiler_exception_css ANY ANY ANY /_profiler/{token}/exception.css
_twig_error_test ANY ANY ANY /_error/{code}.{_format}
homepage ANY ANY ANY /
post_index GET ANY ANY /post/
post_new GET|POST ANY ANY /post/new
post_show GET ANY ANY /post/{id}
post_edit GET|POST ANY ANY /post/{id}/edit
post_delete DELETE ANY ANY /post/{id}
-------------------------- ---------- -------- ------ -----------------------------------
Post エンティティに関するCRUD操作のルーティングが生成されている。
/post/ のルーティングの詳細情報はこんな感じだ。
$ php bin/console router:match /post/
[OK] Route "post_index" matches
+--------------+---------------------------------------------------------+
| Property | Value |
+--------------+---------------------------------------------------------+
| Route Name | post_index |
| Path | /post/ |
| Path Regex | #^/post/$#s |
| Host | ANY |
| Host Regex | |
| Scheme | ANY |
| Method | GET |
| Requirements | NO CUSTOM |
| Class | Symfony\Component\Routing\Route |
| Defaults | _controller: AppBundle:Post:index |
| Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
+--------------+---------------------------------------------------------+
ルーティングの設定にホストの正規表現だとか含まれてるのが見える。
スキーマもDBに反映しておく
よく忘れる。
$ php bin/console doctrine:schema:create
ATTENTION: This operation should not be executed in a production environment.
Creating database schema...
Database schema created successfully!
接続するぞ!
/post に接続すると、
おおお!! 開発ツールがリッチなわりに自動生成されるフォームがしょぼいとか言わないように。
create a new entity をクリックすると、
あれれ・・・
エラーになる。エラー画面の方が可愛い。
Expected argument of type "string", "AppBundle\Form\PostType" given
500 Internal Server Error - UnexpectedTypeException
これ、実は自動生成されたコントローラのコードがダメなのだ。createFormの第一引数には型の名前を指定しなければならない。"AppBundle\Form\PostType" のオブジェクトを渡してしまっている。
43 {
44 $post = new Post();
45 $form = $this->createForm(new PostType(), $post); // ここは型"名"が必要
46 $form->handleRequest($request);
47
editActionと newAction の該当箇所 2つを以下のように修正しよう。
//$form = $this->createForm(new PostType(), $post);
$form = $this->createForm("AppBundle\Form\PostType", $post);
ついでに、FormBuilderで、createdAt
が datetime と指定されていたのを外す。や、これは本来 datetime でいい。だけど、自動生成されたコードの状態ではうまく型名が解決されなかったため※のワークアラウンドである。動かすの優先なので追ってない。ごめんなさい。
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('author')
->add('body')
->add('createdAt') // <- ('createdAt', 'datetime') と自動生成される
;
さて、ここまでやれば、crudが動く。
やったね! php7!
※ FormBuilderInterface を継承した型があればいいはずで、DateTimeType は vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php にあるのだが、それが解決できない原因を追うのは実際に使う段になってからでいいかなって・・・。
ちなみにテンプレートは twig
twigは http://twig.sensiolabs.org で公開されている、「The flexible, fast, and secure
template engine for PHP」ということなんだけど構文がとても Jinja っぽくて python 勢の自分としては好感が持てる。
と思ったら Wikipediaの記事で「syntax originates from Jinja and Django templates」と書かれてあってさもありなん。
アセットコンパイルには scss なども使える
また、
symfony demo
ってやると作成されるデモアプリケーションでは、bootstrap を埋め込んで SCSS をちゃんとコンパイルする構成のサンプルが楽しめます。
テストも試す
phpunit をサポートする感じでテストのスタブは生成されるが、別途ダウンロードしなければならない。
curl -O https://phar.phpunit.de/phpunit.phar
chmod +x phpunit.phar
デフォルトで生成されている phpunit.xml.dist は src/*Bundle/Tests/
を追加してくれてないので、編集する。
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
<!-- ここ追加 -->
<directory>src/*Bundle/Tests</directory>
</testsuite>
</testsuites>
さて、実行すると、
$ ./phpunit.phar
PHPUnit 5.0.10 by Sebastian Bergmann and contributors.
.F 2 / 2 (100%)
Time: 135 ms, Memory: 16.00Mb
There was 1 failure:
1) Warning
No tests found in class "AppBundle\Tests\Controller\PostControllerTest".
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
テストが書かれてないと。まあこれから書けって話ですな。よしよし。
まとめ
さて、PHP7とSymfony3が出ましたね。PHP7は年末にかけて山ほど記事が出ると思うのでSymfony3について少し雑感。
Symfony3はコードジェネレータ周りは少しバギーですが、本体の挙動についてそんなに違和感があるところはありませんでした。この記事だと印象悪いですよね。実際、2017 リリース予定のSymfony 3.3までは3系の安定板はでないとのことであり、「Symfony 2.8 will be released as planned in November 2015 and will be a LTS release as well;」だそうなので、しばらくは Symfony 2.8 で頑張りましょう。
現在のSymfony 2/3は非常に大きなフレームワークであることは確かです。学習コストは高そうです。ただ、すっげえビシッと作ってあるので大規模サイト構築には十分選択肢に入ると思いますし、公式ドキュメントもしっかりしているからWebシステム開発の入門には案外向いているのかもしれません。もしあなたがゆるふわなPHPに対するイメージを持っているのであれば、サービスコンテナまわりのドキュメントとかバンドルの構造とベストプラクティスあたりを読むと相当大きなシステムを支えるようにアーキテクチャが設計されていることがわかるでしょう。逆に小さなシステムを組むにはかなりオーバーな印象を受けます。日本だとこのあたりの規模感の開発用途にPHP選択するチームはそんなに多くないんだろうなー。
ということで、弊社アドベントカレンダー4日目とPHP7の祭りにかこつけて単なる Symfony 3.0.0の入門編でしたが後は @tchikuba さんよろしくお願いします。