読者です 読者をやめる 読者になる 読者になる

ytake Hatena

Web Application Developer

PHP with Apache Kafka

Apache Foundation

ビッグデータ系の処理向けにApache Kafkaを利用し始めました。

これまでもMessage Queueなどにzmq、Redis(PubSub)、ActiveMQ/RabbitMQなどを利用はしていましたが、

スケールのしやすさや、運用面や機能など今後フル活用できそうなためKafkaに、と。
HadoopやHbase、Cassandraといったミドルウェアを扱う機会も増えていているため、
親和性なども当然あります

Kafkaどうなのよ

保存機能

Kafkaでは指定した期間、メッセージを保存する機能が用意されています。
これまでもその機能を持つMessage Queueはありましたが、
配信後にアプリケーション側から再配信させるといったことができます。
デフォルトで2週間ほど保持することができるので、
障害発生時に、再度処理を実行させることも、配信したメッセージの内容を取得することも容易です。

ただ、たしかに最近のPHP、特にLaravelなどにみられる非同期処理を溜め込むためのQueueレベルであれば、
かなりのオーバースペックとなります。
ビッグデータではデファクトスタンダードと言っても良いくらいのラムダアーキテクチャの上で動くアプリケーションであれば、
集計処理やリアルタイム処理など全てが異なるミドルウェアで実行されますので、
速度層などでは重宝します。

Sparkと一緒に利用することでストリーム処理がより強力になるのも魅力的です
(そのために最近scalaをやり始めました)

実績もあるZookeeperを利用したスケールのしやすさなどは現時点ではKafka一択でした。

Kafkaから直接CassandraやHbaseに値を挿入することもできます

www.confluent.io

Partition

Kafkaは簡単にクラスタを構築することができるのはもちろんですが、
このパーティションを利用してアプリケーションレベルで並列や分散処理が設計できます。

ごく一般的なPubSubやQueueではChannel(KafkaではTopicと呼びます)を利用して、
Round-Robinや、同一のQueueを複数のSubscriberが同時に処理するなどがあげられます。

Kafkaでは上記はもちろんですが、アプリケーションで自由に決めることができ、
場合によっては木の枝の様にどんどん細分化して効率的に処理を行うこともできます。

f:id:ytakezawa:20170521001922p:plain

これはSubscriber側だけではなく、PublishするProducer側からも指定することができます。
一つのTopicに対してPartitionで分割し、特定のサーバからはpartition0, あるサーバからはpartition1に対して、
Publishするなどが可能で、
複数のConsumerはこれらをまとめて処理することもProducer同様に、
アプリケーションに合わせてConsumerを異なるサーバで動かすなどができます。

Chatの様なリアルタイム性が重視されるアプリケーションにおいても、
TopicとPartitionがいきてくるのではないかと思います。

Kafka Install

インストール方法はいろんなサイトに記載されていますので省きます。

ただし、アプリケーション側から接続ができない、などがあれば、
confg/server.properties の中身を確認しましょう。

必要に応じて、 advertised.listeners=PLAINTEXT://ipアドレス指定:9092 などデフォルトのままではなく、
正しい情報になっているかどうかを確認します。

Zookeeperの利用が必須になりますので、Kafkaとは別に起動が必要です。
こちらもあわせてZookpeerのアドレスが正しいかどうかを確認してください。

# root directory for all kafka znodes.
zookeeper.connect=ipアドレス指定:2181

# Timeout in ms for connecting to zookeeper
zookeeper.connection.timeout.ms=6000

*Zookeeperのクラスタの構築方法は他のサイトを参照ください

PHPから利用する

Kafkaを細かく利用していくのであれば、rdkafkaがおすすめです。
libkafkaをラップしたphpエクステンションで、
かなり自由に設定を変更できます。

arnaud-lb/php-rdkafka

設定についはアプリケーションの規模や構成によって異なりますので、
公式のドキュイメントかconfluentなどのドキュメントを読み込むのをおすすめします。

Kafka Consumers — Confluent Platform 3.2.1 documentation

Producerは queue.buffering.max.messages などをアプリケーションに合わせて変更してください。
処理速度が早すぎる場合は、指定した件数以上は処理されなくなってしまいます。

Consumerは heartbeat.interval.ms, session.timeout.ms を適切な値に設定する必要があります。
(正しく設定していない場合は切断されるなどが発生します 切断時はsupervisorでプロセス再起動)
heartbeat.interval.msは session.timeoutの1/3くらいに設定すると良いかもしれません。

簡単にProducer/Consumerの動作確認、partition分割の動作確認などを行えるサンプルを用意していますので、
難しければ参考にしてみてください

github.com

RdKafka\Conf クラスを利用して、上記の設定値を反映させることができます。

<?php
declare(strict_types=1);

namespace Ytake\KafkaConsole\Foundation;

use RdKafka\Conf;
use RdKafka\Producer;
use RdKafka\Consumer;

/**
 * Class Configure
 */
final class Configure
{
    // 省略
    /**
     * @return Producer
     */
    public function producer(): Producer
    {
        $conf = new Conf();
        foreach ($this->producerConfigure as $key => $item) {
            $conf->set($key, $item);
        }
        return new Producer($conf);
    }
    /**
     * @return Consumer
     */
    public function consumer(): Consumer
    {
        $conf = new Conf();
        foreach ($this->consumerConfigure as $key => $item) {
            $conf->set($key, $item);
        }
        return new Consumer($conf);
    }
    // 
}

Produce自体は非常にシンプルです

<?php

    /**
     * @param AbstractProduceDefinition $definition
     */
    public function produce(AbstractProduceDefinition $definition)
    {
        $kafkaTopic = $this->producerTopic();
        $kafkaTopic->produce(RD_KAFKA_PARTITION_UA, 0, $definition->payload());
        if ($this->logger instanceof LoggerInterface) {
            $this->logger->info($definition->payload());
        }
        $this->producer->poll(0);
    }
    /**
     * @return ProducerTopic
     */
    protected function producerTopic(): ProducerTopic
    {
        $configure = $this->repository->find($this->topic);
        $this->producer = $configure->producer();
        $this->producer->setLogLevel(LOG_DEBUG);
        $this->producer->addBrokers($configure->brokers());
        return $this->producer->newTopic($this->topic);
    }

RD_KAFKA_PARTITION_UA で、Producer側から自動でpartitionを選択しpublishします。
複数のpartitionを意図した様に操作する場合は
RdKafka\ProducerTopic, RdKafka\Producer クラスのproduceメソッドの引数で意図したものに指定してください。

Consumerは下記の様になります

<?php

    /**
     * @param Consumable $callable
     *
     * @throws \Exception
     */
    public function handle(Consumable $callable)
    {
        $topic = $this->consumerTopic();
        $topic->consumeStart($this->partition, $this->offset);
        while (true) {
            $message = $topic->consume($this->partition, 120 * 10000);
            if ($message instanceof Message) {
                switch ($message->err) {
                    case RD_KAFKA_RESP_ERR_NO_ERROR:
                        call_user_func($callable, $message);
                        break;
                    case RD_KAFKA_RESP_ERR__TIMED_OUT:
                        throw new \Exception("time out.");
                        break;
                    default:
                        break;
                }
            }
        }
    }

サンプルのアプリケーションでは、
producerは $ php kafka-console kafka:produce message-topic hello
consumerは $ php kafka-console kafka:consume message-topic でそれぞれ簡単に動作確認ができます。

consumerを複数立ち上げて、produceを実行すると複数のconsumerが反応します。
またpartitionを指定してconsumerを起動すると、特定のconusmerのみ反応することが確認できると思います。

複雑なアプリケーションや、ビッグデータを扱うアプリケーションなどでは、
一つのアプリケーションだけで解決するには、困難が付きまといます。
マイクロサービスアーキテクチャなどはこうしたミドルウェアを用いたEvent Sourcingなども重要になってきます。
これらのミドルウェアを適切に使い、アプリケーション作りに役立てていきましょう。

Laravel5.4 Data MapperライクなDatabaseアプローチ

Laravelで使われているilluminate/databaseはPDOを利用して実装されています。
つまりPDOでできることは全て利用可能です。

Laravelの標準の機能では、データベースのレコードはCollectionクラスで、
stdClassまたは、配列でカラムと値が共に返却されます。

Data Mapperライクに任意のオブジェクトで返却する様にするには、
Illuminate\Database\Events\StatementPrepared をlistenする必要がありますが、
Database処理にEventが依存してしまうため(eventヘルパーを使ってもクラスに依存していることになります)、
fetchModeを変更できるメソッドが欲しくなります(laravel5.3まではありましたが変更されました)

5.4で利用したい場合は次の様な拡張で簡単に追加することができます。

<?php
declare(strict_types=1);

namespace App\Foundation;

use PDOStatement;

trait QueryPrepared
{
    /** @var string */
    protected $fetchStyleClass = '';

    /**
     * @param string $class
     *
     * @return QueryPrepared
     */
    public function fetchClass(string $class): self
    {
        $this->fetchStyleClass = $class;

        return $this;
    }

    /**
     * @param PDOStatement $statement
     *
     * @return PDOStatement
     */
    protected function prepared(PDOStatement $statement): PDOStatement
    {
        $statement->setFetchMode($this->fetchMode);
        if (!empty($this->fetchStyleClass)) {
            $statement->setFetchMode(\PDO::FETCH_CLASS, $this->fetchStyleClass);
        }

        return $statement;
    }
}

5.4ではquery発行の直前で Illuminate\Database\Connection クラスの preparedメソッドが実行され、
このメソッドで前述のeventが発火されますので、この動作を変更します。

この例ではeventを利用せずに、fetch styleを変更します。

次にConnectionクラスを継承したクラスを作成し、トレイトを使って変更します。
(当然トレイトではなく、通常の継承でも構いません)

<?php
declare(strict_types=1);

namespace App\Foundation;

/**
 * Class SqliteConnection
 */
final class SqliteConnection extends \Illuminate\Database\SQLiteConnection
{
    use QueryPrepared;
}

この例ではsqliteだけを変更します。

次にService Containerを使って、実行クラスを変更します。

<?php
declare(strict_types=1);

namespace App\Providers;

use App\Foundation\SqliteConnection;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Connectors\SQLiteConnector;

/**
 * Class AppServiceProvider
 */
final class AppServiceProvider extends ServiceProvider
{
    /**
     *
     */
    public function register()
    {
        /** @var DatabaseManager $dbManager */
        $dbManager = $this->app['db'];
        $dbManager->extend('sqlite', function (array $config, $name) {
            $connectior = new SQLiteConnector;
            return new SqliteConnection($connectior->connect($config));
        });
    }
}

これでsqlite利用時は拡張したクラスが利用される様になります。
(SqliteConnectionクラスの引数は必要に応じて指定してください)

データベースで返却されるカラムなどに応じてFETCH_CLASSで指定するクラスを作成します。

次の例は、返却されるカラムが二つだけのシンプルなものです。

<?php
declare(strict_types=1);

namespace App\Mapper;

/**
 * Class UserMapper
 */
class UserMapper
{
    /** @var string */
    private $id;

    /** @var string */
    private $name;

    /**
     * @return string
     */
    public function getId(): string
    {
        return $this->id;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }
}

最後に利用したい処理に記述します。

<?php

use App\Mapper\UserMapper;
use App\Foundation\SqliteConnection;
use Illuminate\Database\DatabaseManager;

public function __invoke(DatabaseManager $databaseManager)
{
    /** @var SQLiteConnection $connection */
    $connection = $databaseManager->connection();
    $connection->fetchClass(UserMapper::class);
    $generator = $connection->table('users')->cursor();

    /** @var UserMapper $item */
    foreach ($generator as $item) {
        dump($item);
    }
}

cursorはLaravel5.3から追加されたGeneratorで返却するメソッドです。
返却されるオブジェクトが指定したものになっていることが確認できます。

UserMapper {#107 ▼
  -id: "1"
  -name: "onamae"
}

Laravel-Aspect update MessageDriven, QueryLog (1.7)

リリースノート作ればいいんですが、便利機能をいくつか追加しましたので、 使い所などを踏まえて追加機能の紹介です。

今回からLaravel5.2, 5.1は1.6、5.3以上は1.7となりました。
5.5リリース後、以前のバージョン対応は停止する予定です。

What

そもそもこのライブラリは、ビジネス要件をサポートするシステム要件を分離し、
AOPを用いてアプリケーションをサポートするものです。
Cache削除追加更新, Transactionをはじめ、Log出力やリトライ処理など
どれもビジネス要件を解決する実装コードに含めてしまうとどうしても複雑になりがちです。

それらはビジネス要件以外のシステム要件なども多く入ってしまうと、
大規模アプリケーションでは理解が難しくなっていくため、そんな要件をサポートするためのものです。

ユースケースによるアスペクト指向ソフトウェア開発 (Object Oriented Selectionシリーズ)

ユースケースによるアスペクト指向ソフトウェア開発 (Object Oriented Selectionシリーズ)

そんなサポートの機能によく使うものを追加しました

Message Driven

これは特定のメソッドの処理をQueueにpushする機能です。
illuminate/busを利用してQueueに挿入しますが、dispatchヘルパーと用途的には同じです。

Queueを利用するため、serializeできないClosureが含まれている場合はエラーになります。

Usage

<?php

declare(strict_types=1);

namespace App\Foundation;

use Ytake\LaravelAspect\Annotation\Loggable;
use Ytake\LaravelAspect\Annotation\LazyQueue;
use Ytake\LaravelAspect\Annotation\MessageDriven;

class MessageDispatcher
{
    /**
     * @Loggable
     * @MessageDriven(
     *     @LazyQueue(10)
     * )
     *
     * @param string $message
     */
    public function queued(string $message)
    {
        $this->hello($message);
    }

    /**
     * @Loggable
     * @param string $message
     *
     * @return string
     */
    public function hello(string $message): string
    {   
        return $message;
    }
}

MessageDrivenアノテーションの第一引数でQueueの振る舞いが変更されます。
LazyQueueをアノテーションで指定すると、databaseやredisなどのドライバーを利用するQueueとして作用し、
EagerQueueを指定すると、syncドライバを利用して遅延なしで実行されます。

上記の例の様に他のアノテーションと組み合わせることもできます。
この場合はQueueに queued メソッドの処理が登録されます。
helloメソッドが実行されるときに引数と実行時間をlogに出力します。

Query Log

Laravelを使っている方はdatabaseのクエリログ取得にEventを利用したり、 Debugbarといったものを使って表示させているかと思います。

Eventを毎回ServiceProviderに記述して必要な処理のところだけLogを出力して・・、
となるとなかなか手間がかかります。

Debugbarは便利ではありますが、商用環境にこのライブラリをインストールするわけにもいかず。。

*大幅にパフォーマンス劣化、実行しているものが見えるため場合によっては脆弱性に繋がるかもしれません

ですが商用環境でSlowQueryを解決するためにコードに記述することも多いと思いますが、
どうしてもアプリケーション本来の要件とは違うコードが記述されるため、処理が複雑になっていくこともあると思います。

そんな問題をAOPで解決する機能です。

Usage

EloquentでもDBファサードやConstruct Injectionでも構いませんが下記の様に、
特定の処理を実行するメソッドに対して @QueryLogアノテーションを記述します。

<?php

use Ytake\LaravelAspect\Annotation\QueryLog;
use Illuminate\Database\ConnectionResolverInterface;

/**
 * Class AspectQueryLog
 */
class AspectQueryLog
{
    /** @var ConnectionResolverInterface */
    protected $db;

    /**
     * @param ConnectionResolverInterface $db
     */
    public function __construct(ConnectionResolverInterface $db)
    {
        $this->db = $db;
    }

    /**
     * @QueryLog
     */
    public function multipleDatabaseAppendRecord()
    {
        $this->db->connection()->statement('CREATE TABLE tests (test varchar(255) NOT NULL)');
        $this->db->connection('testing_second')->statement('CREATE TABLE tests (test varchar(255) NOT NULL)');
        $this->db->connection()->table("tests")->insert(['test' => 'testing']);
        $this->db->connection('testing_second')->table("tests")->insert(['test' => 'testing second']);
    }
}

上記の例では multipleDatabaseAppendRecord メソッドが実行されると、
このメソッド内でコールされたSQLがログファイルに出力されます

Transactional拡張

今までTransactionalアノテーションは一つのメソッドに対して一つしか指定できなかったため、
複数のデータベースに対してTransactionを指示したい場合は、下記の様にする必要がありました。

<?php

namespace App\Domain\Service;

use Ytake\LaravelAspect\Annotation\Transactional;

class ProductActivation
{
    /**
     * @Transactional("db1")
     */
    public function activate()
    {
        $this->activateCampaign();
        // 処理
    }

    /**
     * @Transactional("db2")
     */
    public function activateCampaign()
    {

    }
}

これを1メソッドに対して同時に複数指定することができる様になりました。

<?php

namespace App\Domain\Service;

use Ytake\LaravelAspect\Annotation\Transactional;

class ProductActivation
{
    /**
     * @Transactional({"db1", "db2"})
     */
    public function activate()
    {
        // 処理
    }
}

php7 でxhp-extensionをインストール

XHP

HackでおなじみのXHP XSS対策はもちろんのこと、現在のReactの元(ほぼ同じ記述法)になったもの、
とご存知の方も多いと思います。
php5ではfacebookGithubで公開されています。

github.com

が、現php7対応版は公開されておらず、Hackのみで利用可能となっています。

for php7

がしかし、php7で動くように対応したリポジトリがありました

github.com

こちらをビルドして、php7で動かしてみましょう

Linux

$ git clone https://github.com/KMK-ONLINE/xhp-php7-extension.git
$ phpize
$./configure
$ make
$ sudo make install

Mac OS

Linux環境と同じですが、最新のOSなどではgccが古いためmakeに失敗します。 brew などでgccをinstallし、Makefileを変更するか、CC= CXX= などで指定するとmake できると思います

変更する場合は、xhp-php7-extension/xhp/Makefilegcc, g++をbrewなどでinstallしたgccに変更します。

gcc-6 g++-6の場合は以下のようになるでしょう

ifdef DEBUG
CPPFLAGS = -fPIC -ggdb -Wall -DDEBUG -std=c++11
else
CPPFLAGS = -fPIC -g -Wall -O3 -minline-all-stringops -std=c++11
endif

ifdef PROFILE
CPPFLAGS += -pg
endif

FLEX = `which flex35 2>/dev/null || which flex 2>/dev/null`

all: libxhp.a libxhp.so

clean:
    -rm libxhp.a xhpize parser.yacc.cpp scanner.lex.cpp scanner.lex.hpp parser.yacc.output parser.yacc.hpp fastpath.cpp version.h *.o 2>/dev/null

parser.yacc.cpp: parser.y
    bison --debug --verbose -d -o $@ $<

parser.yacc.hpp: parser.yacc.cpp

scanner.lex.cpp: scanner.l
    $(FLEX) \
      -C --header-file=scanner.lex.hpp -o $@ -d $<

scanner.lex.hpp: scanner.lex.cpp

fastpath.cpp: fastpath.re
    re2c -c -o $@ $<

%.o: %.cpp
    g++-6 -c $(CPPFLAGS) -o $@ $<

xhp_preprocess.o: xhp_preprocess.cpp scanner.lex.hpp parser.yacc.hpp
parser.yacc.o: scanner.lex.hpp
scanner.lex.o: parser.yacc.hpp

libxhp.a: code_rope.o rope_entity.o scanner.lex.o parser.yacc.o fastpath.o xhp_preprocess.o
    $(AR) -crs $@ $^

libxhp.so: code_rope.o rope_entity.o scanner.lex.o parser.yacc.o fastpath.o xhp_preprocess.o
    g++-6 -shared -Wl,-soname,libxhp.so -o libxhp.so $^

version.h:
    echo "#define XHPIZE_VERSION \""`git show -s --format="%h - %ci" HEAD`"\"" >> version.h

xhpize: xhpize.cpp libxhp.a version.h
    g++-6 $(CPPFLAGS) -o $@ $^

rope_test: rope_test.cpp code_rope.o rope_entity.o
    g++-6 $(CPPFLAGS) -o $@ $^

.PHONY: all clean tags

*CentOS 7, Ubuntu16.04環境で動作確認

install後php.ini にextensionを追加します

extension=xhp.so

php -mでインストールされたのを確認しましょう

composer

xhpのextensionに加え、ライブラリをインストールします(kmklabs/xhp)

  "require": {
    "kmklabs/xhp": "^1.6"
  }

インストール後は通常のphpコードに記述できます。

はじめてのxhp

Reactと同様です。
このextensionを利用する場合は、
<?hh にするか、 <?php // xhpと指定する必要があります

<?php // xhp

require __DIR__ . '/vendor/autoload.php';

/**
 * Class
 */
class :ytake:something extends :x:element {
    protected function render()
    {
        return <div class="hello">it's work!</div>;
    }
}

echo <ytake:something /> . "\n";

動作が確認できればOKです

その他

ビルドはできましたが、自分の環境では正しく動きませんでした github.com

HHVM/Hack Dependency Injection/Service Location Container公開

About

Hackで簡単に利用できる Dependency Injection/Service Location Containerライブラリを公開しました。

github.com

Pimpleをはじめとして、
多くのライブラリをそのままHHVM上で動かすことはもちろんできますが、
typescriptやflowといった厳格さを利用できるHackを活かす為、
strictモードで利用できる + psr11 に対応しています。

*ライブラリの名前はtypoではなくわざとです

Headacke

インストール方法

hhvm上で次のコマンドを利用する、composer.jsonに加えるなどしてください

$ hhvm --php $(which composer) require ytake/headacke
"require": {
  "hhvm": ">=3.11.0",
  "ytake/headacke": "~0.0"
},

利用方法

サービスロケータとして任意のものをコンテナに追加することができます

<?hh // strict
$container = new \Headacke\FactoryContainer();
$container->set('testing', $container ==> 'testing');
$container->get('testing'); 

上記の場合は コンテナに'testing' サービスを登録し、文字列の ‘testing’ が返却されます。

簡単ですね

Singleton

当然シングルトンで利用する場合は次のように登録します。

<?hh // strict

$container = new \Headacke\FactoryContainer();
$container->set('scope:singleton', $container ==> new \stdClass(), \Headacke\Scope::SINGLETON);

stdClassをシングルトンで利用する例です。
Scopeを指定してください

内部はHackで簡単に利用できるMemoizeです

<?hh // strict

  <<__Memoize>>
  protected function shared(string $id): mixed
  {
    return call_user_func($this->bindings->at($id), $this);
  }

Prototype

都度新しいインスタンスが欲しい場合は次の通りです。

<?hh // strict

$container = new \Headacke\FactoryContainer();
$container->set('scope:prototype', $container ==> new \stdClass(), \Headacke\Scope::PROTOTYPE);

scopeの指定をしていない場合はデフォルトでprototypeとなります

Dependency Injection

サービスロケータとして使う場合はこれまで紹介したものだけで十分ですが、
やはりアプリケーション内にcontainerが出てくるのは良いとは言えません。

この為、インスタンス生成したいクラスの定義を先に記述することで簡単にDIが利用できます。

parameter registration

<?hh // strict

$container->parameters(
  'string className',
  'parameter name',
  $container ==> 'parameter value'
);

インスタンスを生成したいクラスの引数などを事前に登録します。

lamdaでコンテナ自体にアクセスできますので、サービスロケータに登録したものを利用できます

次のクラスを例にしましょう

<?hh // strict

// Constructor Parameter Promotion 

final class MessageClass {
  public function __construct(protected string $message) {
  }
  public function message(): string {
    return $this->message;
  }
}

final class MessageClient {
  public function __construct(protected MessageClass $message) {

  }
  public function message(): MessageClass {
    return $this->message;
  }
}

MessageClientクラスのコンストラクタにMessageClassがコンストラクタで指定されています。

これをコンテナ経由で取得する場合は次のように記述できます。

<?hh // strict

$container = new \Headacke\FactoryContainer();
$container->set('message.class', $container ==> new MessageClass('testing'));
$container->parameters(
  MessageClient::class, 
  'message', 
  $container ==> $container->get('message.class')
);
$instance = $container->get(MessageClient::class);

コンテナに'message.class'サービスとして MessageClass を登録します
MessageClientクラスの引数として、'message.class' を利用するにように指定します。
最後にコンテナからインスタンス生成を行います。

簡単に利用できるようになっていますが、
残念ながらlaravelやleague/containerのようにauto wiringのサポートはしていません。
(getしたらそれに関連するクラスも全て依存解決する機能)
あくまで取得したいものは事前に定義しなければ動作しません!

Hackで開発をしたい方のヒントになればと思います。

vagrant box / gardening update information

laravel/homesteadのCentOS7版のようなbox ytake/gardening というものをatlasで公開しています。

ytake.hateblo.jp

先月このボックスのアップデートを行いました。 CentOSのすぐ開発できるboxが欲しい!という方にはおそらくピッタリでしょう!

github.com

アップデート内容

PHP エクステンション追加

このboxには実はかなり多彩なエクステンションが含まれています(!)

大規模アプリケーションで利用されるcassandra、couchbase(最新の4.5対応版)、rdkafka(apache kafka)
など万人向けではありませんが、導入が少し厄介なものに加えて、
microsoftsql serverがすぐに利用できるようにpdoドライバなども導入してあります。

その中でも、最近利用されるケースも多いv8jsエクステンションですが、
CentOSで利用するにはビルドや導入が結構難しかったりする場合もありますが、
今回のアップデートでエクステンションに追加しました。

CentOSでもサーバサイドレンダリングができるようになりますので、気軽に試すことができます。

含まれているエクステンションは以下の通りです

[PHP Modules]
amqp
apc
apcu
bcmath
bz2
calendar
cassandra
Core
couchbase
ctype
curl
date
dom
event
exif
fileinfo
filter
ftp
gd
gettext
hash
iconv
igbinary
imagick
json
ldap
libxml
mbstring
mcrypt
memcached
mongodb
msgpack
mysqli
mysqlnd
openssl
pcntl
pcre
pcs
PDO
pdo_dblib
pdo_mysql
pdo_pgsql
pdo_sqlite
pdo_sqlsrv
pgsql
phalcon
Phar
posix
rdkafka
readline
redis
Reflection
session
shmop
SimpleXML
soap
sockets
SPL
sqlite3
sqlsrv
standard
Stomp
sysvmsg
sysvsem
sysvshm
tokenizer
uopz
uuid
v8js
wddx
xdebug
xhprof
xml
xmlreader
xmlwriter
xsl
Zend OPcache
zlib
zmq

[Zend Modules]
Xdebug
Zend OPcache

php-dbgも利用できます

HHVM アップデート

HHVM 3.15 に更新しています。
LTSの3.18が利用できるようになればまた更新します。

MySQL PostgreSQL アップデート

これまでMySQL5.6でしたが5.7へやっとアップデートしました。
5.6から5.7に変わり、初回パスワードなどの対応が面倒くさく、後回しにしていましたw
PostgreSQLは9.5へアップデートしています。

これに伴い、データベースのアカウント、パスワードを下記のものに変更しました。

user: gardening
password: 00:secreT,@

Symfony対応

symfonyの開発がすぐにできるようにと対応しました。

対応方法は導入時に配置されるgardening.yaml(or gardening.json)に次のように記述します。

sites:
    - map: gardening.app
      to: /home/vagrant/yourProject/public
      type: symfony

かんたん! (HHVMで動かす場合は hhvm: true)

node.js周り

vagrantでの開発環境ということで以下のものをグローバルにインストールしています。

npm install -g typescript
npm install -g webpack
npm install -g nuclide
npm install -g yarn

HHVMに対応しているboxということもあり、nuclideで開発するための環境も一応用意しています。

その他細かい変更

  • supervisorの追加
  • elasticsearch 5.1アップデート
  • couchbase 4.5アップデート
  • golang 削除(環境問わず開発できるため削除しました)

RDBMS以外のデータベースの利用方法については、readmeに記載しています

オプションの利用方法

*気づかれていませんが、このboxはNginxとApache HTTP Serverの切り替えができます(!)

HHVM/Hackはじめの一歩

phpの拡張として、魅了的な要素がたくさん詰まったHHVM/Hack

挿入を始めるにあたって、壁にぶち当たるのがphpstormなどの様な高機能なIDEがない、

などがあげられるかもしれません

IDEに代表される様な補完機能がなかなか効かないだったり、
typecheckerで既存のライブラリが動かない、なんてこともあるのかもしれません。

hhvm-autoload

github.com

HHVMでcomposerを最適化するプラグインとして動作するライブラリです

hhi

Hackで開発する場合、厳格な型指定はメリットであり、
アプリケーション開発時にはstrict指定をすることがほとんどだと思います。

<?hh // strict
// 厳格モード

ちなみにdeclにするとtype checkされません

<?hh // decl

*何も指定しない場合はパーシャルモードである程度はtype checkしますが、それ以上のことはしないモードです

厳格モードにした場合は、phpライブラリを流用する場合に型エラーが多く発生します。

その場合は、.hhconfigassume_php=false を記述するなどの方法があります。

Typechecker: Setup

この他にhhiファイルと呼ばれる実装が無い型宣言ファイルを作成し、strictで動作させる様にすることができます。

type scriptやflowを使って開発している方はイメージがつくと思います。

このファイルを使うことで、type checker(nuclideも)がphpのライブラリであってもスムーズに利用できる様になります。

*nuclideで補完が効かないと云う方は是非お試しください

PHPUnit

HackUnitなどもありますが、デファクトスタンダードでもあるPHPUnitを利用する機会の方が多いでしょう。

PHPUnitはHackの様な完全な厳格さはありませんが、91-carriage/phpunit-hhi を使うことでコード補完などが利用できます。

git.simon.geek.nz

Hackにのみ存在する様なコードをテストする場合は前述のHackUnitなどを使用しましょう。

なにもない状態でHackを使っている場合は
挙げたものを加えるだけで今までよりもスムーズに開発できる様になるでしょう!