Migrate phalcon version 3.x to version 4

其實 phalcon 4 已經release很久了,我一直拖著沒有把手邊的系統從 3.x 做升級,但總有該面對的一天

其實最後一根稻草是 phalcon 3.x 只支援到 php 7.3, 之後的 php 7.4 跟 php 8 都不支援,除非自己從 source code compile 才有可能。

安裝phalcon 4過程大致上跟 3.x 版差異不大,要比較注意的是要記得安裝 php-psr。

sudo apt-get update
sudo apt-get install -y php7.4 apache2
sudo apt-get install -y php7.4-opcache php-apcu php7.4-mbstring php7.4-zip php7.4-xml php7.4-mysql php7.4-json php7.4-gd php7.4-gmp php7.4-curl php-psr
sudo apt-get install -y wget
sudo apt-get install -y curl
wget http://pear.php.net/go-pear.phar
php go-pear.phar 
curl -s "https://packagecloud.io/install/repositories/phalcon/stable/script.deb.sh" | sudo bash
sudo apt-get install php7.4-phalcon
sudo apt-get install libapache2-mod-php

過去舊的 phalcon 3.4 project 我都是用 devtool 工具生成 template,所以最直接需解決的是這些舊專案怎麼在 4.x 的環境中正常運作。

修改public/index.php

主要是handle request 的部分要做處理

$ diff -r /var/opt/www/new4.x/public/index.php /var/opt/www/old3.4/public/index.php 
42,45c42,46
<     $request = new Phalcon\Http\Request();
<     $response = $application->handle($_GET['_url']);
<     $response->send();
---
> 
>     echo $application->handle()->getContent();

 

大修改的 session 與 flash session

過去 3.x 中的 Flash Session 一整個變得不一樣, Phalcon\Flash\Session constructor 一整個大改,原本(參考文件 https://github.com/phalcon/docs-app/blob/master/3.4/ur-in/api/Phalcon_Flash.md)傳入 css settings 就可以。
另外 Session 也是,過去 new SessionAdapter() 之後就可以很方便使用,在 4.x 版中要正確指定好 adapter。

直接把整個 app/config/service.php 貼上來比較快

<?php
declare(strict_types=1);

use Phalcon\Escaper;
use Phalcon\Flash\Direct as Flash;
use Phalcon\Flash\Session as FlashSession;
use Phalcon\Mvc\Model\Metadata\Memory as MetaDataAdapter;
use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Php as PhpEngine;
use Phalcon\Mvc\View\Engine\Volt as VoltEngine;
use Phalcon\Session\Adapter\Stream as SessionAdapter;
use Phalcon\Session\Manager as SessionManager;
use Phalcon\Url as UrlResolver;

/**
 * Shared configuration service
 */
$di->setShared('config', function () {
    return include APP_PATH . "/config/config.php";
});

/**
 * The URL component is used to generate all kind of urls in the application
 */
$di->setShared('url', function () {
    $config = $this->getConfig();

    $url = new UrlResolver();
    $url->setBaseUri($config->application->baseUri);

    return $url;
});

/**
 * Setting up the view component
 */
$di->setShared('view', function () {
    $config = $this->getConfig();

    $view = new View();
    $view->setDI($this);
    $view->setViewsDir($config->application->viewsDir);

    $view->registerEngines([
        '.volt' => function ($view) {
            $config = $this->getConfig();

            $volt = new VoltEngine($view, $this);

            $volt->setOptions([
                'path' => $config->application->cacheDir,
                'separator' => '_'
            ]);

            return $volt;
        },
        '.phtml' => PhpEngine::class

    ]);

    return $view;
});

/**
 * Database connection is created based in the parameters defined in the configuration file
 */
$di->setShared('db', function () {
    $config = $this->getConfig();

    $class = 'Phalcon\Db\Adapter\Pdo\\' . $config->database->adapter;
    $params = [
        'host'     => $config->database->host,
        'username' => $config->database->username,
        'password' => $config->database->password,
        'dbname'   => $config->database->dbname,
        'charset'  => $config->database->charset
    ];

    if ($config->database->adapter == 'Postgresql') {
        unset($params['charset']);
    }

    return new $class($params);
});


/**
 * If the configuration specify the use of metadata adapter use it or use memory otherwise
 */
$di->setShared('modelsMetadata', function () {
    return new MetaDataAdapter();
});

/**
 * Register the session flash service with the Twitter Bootstrap classes
 */
$di->set('flash', function () {
    $escaper = new Escaper();
    $flash = new Flash($escaper);
    $flash->setImplicitFlush(false);
    $flash->setCssClasses([
        'error'   => 'alert alert-danger',
        'success' => 'alert alert-success',
        'notice'  => 'alert alert-info',
        'warning' => 'alert alert-warning'
    ]);

    return $flash;
});

/**
 * Start the session the first time some component request the session service
 */
$di->setShared('session', function () {
    $session = new SessionManager();
    $files = new SessionAdapter([
        'savePath' => sys_get_temp_dir(),
    ]);
    $session->setAdapter($files);
    $session->start();

    return $session;
});

$di->set('flashSession', function () use ($session) {
    $session = $this->getSession();
    $escaper = new Escaper();
    $flashsession = new FlashSession($escaper, $session);

    $flashsession->setCssClasses([
        'error'   => 'alert alert-danger',
        'success' => 'alert alert-success',
        'notice'  => 'alert alert-info',
        'warning' => 'alert alert-warning'
    ]);

    return $flashsession;
});

完工後做點測試

<?php
declare(strict_types=1);

class TestController extends \Phalcon\Mvc\Controller
{
    public function s1Action()
    {
        $this->session->set("name", "kevin durant");
        $this->flashSession->error("test");
        $this->response->redirect("test/s2");
    }

    public function s2Action()
    {
        var_dump($this->session->name);
    }

}

Request /test/s1 就會被 redirect 到 /test/s2 且印出 session["name"] 與出現flash error message。

Image 3

Reference:

  1. PHP 8 與 7 的差異
  2. https://www.programmersought.com/article/43914253346/