Phalcon4をXAMPPで動かしてみる

PHP界最速の実行速度と名高いマイクロフレームワーク「Phalcon」をWindows10のXAMPP環境で動かしてみます。


Hello World

まずは、環境を整えてHello Worldしてみます。

こちらからポータブル版XAMPPをダウンロードします。
PHPのバージョンは7.4.2にしました。

任意の場所に置いたら「~xampp\setup_xampp.bat」を実行して初期セットアップをします。
XAMPPの起動確認ができたら次はPhalcon本体をダウンロードします。


phpinfoを見てどのバイナリをダウンロードするか決めます。
私の環境は64bitのPHP7.4のThread Safe版なので選ぶのはこれ。
ダウンロードしたZIPを展開すると中に「php_phalcon.dll」があるので「~\xampp\php\ext\」に置き、 「~\xampp\php\php.ini」の最後の行に、
extension=php_phalcon.dll
を追加します。

このままPHPを起動すると「PHP Warning: Cannot load module ‘phalcon’ because required module ‘psr’ is not loaded in Unknown on line 0」のエラーメッセージが出るので、言われてる通りPSRのモジュールを追加します。

PSRはこちらからダウンロードできます。
Phalconと同じように自分の環境に合ったものをダウンロードします。
同じようにZIPの中にある「php_psr.dll」を置いて、
extension=php_psr.dll
extension=php_phalcon.dll
PSRを読み込むよう「php.ini」を編集します。

再度phpinfoを実行するとPhalconが使えるようになっています。


サンプルコードを参考にHello Worldを作ってみます。
<?php
use Phalcon\Mvc\Micro;

$app = new Micro();

$app->get(
    '/Hello/{name}',
    function ($name) {
        echo "Hello! {$name}";
    }
);

$app->handle(
    $_SERVER["REQUEST_URI"]
);
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_URI} !\.css$
    RewriteCond %{REQUEST_URI} !\.js$
    RewriteCond %{REQUEST_URI} !\.jpg$
    RewriteCond %{REQUEST_URI} !\.gif$
    RewriteCond %{REQUEST_URI} !\.png$
    RewriteRule ^.*$ index.php
</IfModule>
「~\xampp\htdocs\」に置いて、「http://localhost/Hello/Phalcon」にアクセスすると、
できました。
PhalconはPHPファイルの実体を置かなくてもuseでモジュールを呼び出せるんですね。
バイナリになってるぶん実行も普通のPHPファイルより早いのでしょう。

MVCをやってみる

折角なので簡単にMVCを作ってみます。
コチラが参考になりそうです。


Basicを使いますが他にもRESTなどがありますね。
PhalconはWebアプリケーションの作り方にいくつかパターンがあるようです。

ディレクトリ構造はこんな感じです。
.
└── xampp
    ├── app
    │   ├── controllers
    │   │   ├── IndexController.php
    │   │   └── SignupController.php
    │   ├── models
    │   │   └── Users.php
    │   └── views
    │   │   ├── index
    │   │   │    └── index.phtml
    │   │   ├── signup
    │   │   │    └── index.phtml
    │   │   └── index.phtml
    └── htdocs
        ├── .htaccess
        └── index.php

index.php

フレームワークの起点です。

オートロードとDIコンテナを設定してアプリケーションを起動しています。
<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Mvc\Application;
use Phalcon\Url;
use Phalcon\Db\Adapter\Pdo\Mysql;

// Define some absolute path constants to aid in locating resources
define('BASE_PATH', dirname(__DIR__));
define('APP_PATH', BASE_PATH . '/app');

// Register an autoloader
$loader = new Loader();

$loader->registerDirs(
    [
        APP_PATH . '/controllers/',
        APP_PATH . '/models/',
    ]
);

$loader->register();

// Set DI container
$container = new FactoryDefault();

$container->set(
    'view',
    function () {
        $view = new View();
        $view->setViewsDir(APP_PATH . '/views/');
        return $view;
    }
);

$container->set(
    'url',
    function () {
        $url = new Url();
        $url->setBaseUri('/');
        return $url;
    }
);

$container->set(
    'db',
    function () {
        return new Mysql(
            [
                'host'     => '127.0.0.1',
                'username' => 'root',
                'password' => null,
                'dbname'   => 'test',
            ]
        );
    }
);

$application = new Application($container);

try {
    // Handle the request
    $response = $application->handle(
        $_SERVER["REQUEST_URI"]
    );

    $response->send();
} catch (\Exception $e) {
    echo 'Exception: ', $e->getMessage();
}

コントローラー

他のPHPフレームワークと似ています。
ORMも簡単に使えそうです。
<?php

use Phalcon\Mvc\Controller;

class IndexController extends Controller
{
    /**
     * Welcome and user list
     */
    public function indexAction()
    {
        $this->view->users = Users::find();
    }
}
<?php

use Phalcon\Mvc\Controller;

class SignupController extends Controller
{
    public function indexAction()
    {

    }

    public function registerAction()
    {
        $user = new Users();

        $user->name = $this->request->getPost('name');
        $user->email = $this->request->getPost('email');

        // Store and check for errors
        $success = $user->save();

        if ($success) {
            echo "Thank you for registering!";
        } else {
            echo "Sorry, the following problems were generated: ";

            $messages = $user->getMessages();

            foreach ($messages as $message) {
                echo $message->getMessage(), "<br/>";
            }
        }

        $this->view->disable();
    }
}

モデル

モデルもよくある感じです。
テーブルを作るSQLを事前に流しておきます。
<?php

use Phalcon\Mvc\Model;

class Users extends Model
{
    public $id;
    public $name;
    public $email;
}
CREATE TABLE `users` (
    `id`    int(10)     unsigned NOT NULL AUTO_INCREMENT,
    `name`  varchar(70)          NOT NULL,
    `email` varchar(70)          NOT NULL,

    PRIMARY KEY (`id`)
);

ビュー

レイアウトビューと個別ビューに分けられます。
ヘルパーメソッドが用意されています。
Voltというテンプレートエンジンも使えるようです。
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Phalcon Tutorial</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <?php echo $this->getContent(); ?>
</div>
</body>
</html>
<?php
echo "<h1>Hello!</h1>";

echo $this->tag->linkTo(["signup", "Sign Up Here!", 'class' => 'btn btn-primary']);

if ($users->count() > 0) {
    ?>
    <table class="table table-bordered table-hover">
        <thead class="thead-light">
        <tr>
            <th>#</th>
            <th>Name</th>
            <th>Email</th>
        </tr>
        </thead>
        <tfoot>
        <tr>
            <td colspan="3">Users quantity: <?php echo $users->count(); ?></td>
        </tr>
        </tfoot>
        <tbody>
        <?php foreach ($users as $user) { ?>
            <tr>
                <td><?php echo $user->id; ?></td>
                <td><?php echo $user->name; ?></td>
                <td><?php echo $user->email; ?></td>
            </tr>
        <?php } ?>
        </tbody>
    </table>
    <?php
}
<h2>Sign up using this form</h2>

<?php echo $this->tag->form("signup/register"); ?>

    <p>
        <label for="name">Name</label>
        <?php echo $this->tag->textField("name"); ?>
    </p>

    <p>
        <label for="email">E-Mail</label>
        <?php echo $this->tag->textField("email"); ?>
    </p>

    <p>
        <?php echo $this->tag->submitButton("Register"); ?>
    </p>

</form>

実際に動かしてみるとこんな感じ。
データの登録⇒参照ができてます。

以前書いたSlimに近くかなり自由度が高いです。
https://www.blog.danishi.net/2019/11/19/post-2362/

本体がバイナリな分さらにサイズが軽量なのでドキュメントがもうちっと充実してくれれば小規模な開発とかで使っていけそう。