Kinh nghiệm lập trình website mã nguồn mở PHP

Đục core Magento 2 – phần cài đặt hoạt động như thế nào?

Giới thiệu

Mở đầu loạt bài viết Đục core Magento 2. Hôm nay chúng ta sẽ tìm hiểu bên dưới quá trình cài đặt của Magento. Dù cho bạn cài đặt bằng command line hay qua giao diện, thì cách hoạt động của quá trình setup là như nhau.

Bài viết này lấy cảm hứng từ bài Cài đặt Magento 2. Hiểu rõ bản chất của quá trình cài đặt sẽ giúp bạn dễ dàng biết và vượt qua được những lỗi có thể gặp trong giai đoạn này.

Đục core Magento 2 – phần cài đặt hoạt động như thế nào?

Bắt đầu đục

Câu lệnh để setup 1 trang magento mới là:

php bin/magento setup:install

Dòng lệnh này được khai báo tại:
vendor/magento/magento2-base/setup/src/Magento/Setup/Console/Command/InstallCommand.php

$this->setName('setup:install')
    ->setDescription('Installs the Magento application')
    ->setDefinition($inputOptions);

Tức là khi ta gõ setup:install thì method execute trong file đó sẽ được thực thi:

protected function execute(InputInterface $input, OutputInterface $output)
{
    $consoleLogger = new ConsoleLogger($output);
    $installer = $this->installerFactory->create($consoleLogger);
    $installer->install($input->getOptions());
}

Vào trong method install
vendor/magento/magento2-base/setup/src/Magento/Setup/Model/Installer.php

public function install($request)
{
    $script[] = ['File permissions check...', 'checkInstallationFilePermissions', []];
    $script[] = ['Enabling Maintenance Mode...', 'setMaintenanceMode', [1]];
    $script[] = ['Installing deployment configuration...', 'installDeploymentConfig', [$request]];
    if (!empty($request[InstallCommand::INPUT_KEY_CLEANUP_DB])) {
        $script[] = ['Cleaning up database...', 'cleanupDb', []];
    }
    $script[] = ['Installing database schema:', 'installSchema', []];
    $script[] = ['Installing user configuration...', 'installUserConfig', [$request]];
    $script[] = ['Enabling caches:', 'enableCaches', []];
    $script[] = ['Installing data...', 'installDataFixtures', []];
    ...
    while (list(, list($message, $method, $params)) = each($script)) {
        $this->log->log($message);
        call_user_func_array([$this, $method], $params);
        $this->logProgress();
    }

    $this->log->logSuccess('Magento installation complete.');
    $this->log->logSuccess(
        'Magento Admin URI: /'
        . $this->deploymentConfig->get(BackendConfigOptionsList::CONFIG_PATH_BACKEND_FRONTNAME)
    );
    ...
}

Ta thấy Magento khai báo hàng loạt các tác vụ: File permissions check, Enabling Maintenance Mode, Installing deployment configuration, Installing database schema… Đây chính là step by step của quá trình cài đặt.
Chú ý hàm: call_user_func_array([$this, $method], $params);
Chính là cách 1 method gọi 1 method khác trong 1 class, tức là khi Magento check permission, nó sẽ xuất ra màn hình dòng chữ: File permissions check… Rồi chạy hàm checkInstallationFilePermissions() trong class này, hãy xem họ check quyền như thế nào nhé:

public function checkInstallationFilePermissions()
{
    $results = $this->filePermissions->getMissingWritableDirectoriesForInstallation();
    if ($results) {
        $errorMsg = "Missing write permissions to the following directories: '" . implode("' '", $results) . "'";
        throw new \Exception($errorMsg);
    }
}

vendor/magento/magento2-base/setup/src/Magento/Setup/Model/FilePermissions.php

public function getMissingWritableDirectoriesForInstallation()
{
    $required = $this->getInstallationWritableDirectories();
    $current = $this->getInstallationCurrentWritableDirectories();
    return array_diff($required, $current);
}

Yahoo! Ở đây họ check quyền thư mục bằng cách: so sánh các thư mục cần thiết trong quá trình cài đặt, với các thư mục đã có quyền đọc ghi, nếu có sự khác biệt thì chứng tỏ có thư mục chưa có quyền. Nên nhớ hàm array_diff chỉ trả ra kết quả từ $required mà không có trong $current, 1 chiều.
Vậy những thư mục nào cần quyền trong quá trình cài đặt?

public function getInstallationWritableDirectories()
{
    if (!$this->installationWritableDirectories) {
        $data = [
            DirectoryList::CONFIG,
            DirectoryList::VAR_DIR,
            DirectoryList::MEDIA,
            DirectoryList::STATIC_VIEW,
        ];

Đó chính là: etc, var, media, static.
Tài liệu cài đặt Magento 2 cũng đề cập tới các thư mục này, nhưng tự khám phá ra trong code thú vị hơn phải không?
Magento check quyền tập tin và thư mục như thế nào?

protected function isWritable($code)
{
    $directory = $this->filesystem->getDirectoryWrite($code);
    return $this->isReadableDirectory($directory) && $directory->isWritable();
}

Magento đang check quyền cho thư mục bao gồm: quyền đọc và quyền ghi
Để đáp ứng quyền đọc, bao gồm: thư mục đó phải tồn tại, thư mục hợp lệ, thư mục đó đọc được

protected function isReadableDirectory($directory)
{
    if (!$directory->isExist() || !$directory->isDirectory() || !$directory->isReadable()) {
        return false;
    }
    return true;
}

Để đáp ứng quyền ghi:

public function isWritable($path = null)
{
    return $this->driver->isWritable($this->driver->getAbsolutePath($this->path, $path));
}

Vậy là ta đã học được một số hàm rất quan trọng, để làm việc với thư mục, class: vendor/magento/framework/Filesystem/Directory/Write.php
Tiếp tục khám phá, ta tìm được 1 class:
vendor/magento/framework/Filesystem/Driver/File.php
chuyên dành để làm việc với tập tin, cũng có các hàm: isExist(), isReadable(), isFile(), isWriteable()
Quá tuyệt vời phải không? Vậy là từ nay, để thực hiện các tác vụ, đọc file, xuất file… Ta cứ gọi tới các hàm này xài thôi, cách xài thì như phần setup Magento họ đã xài.
Ngoài ra còn có cái hàm checkRecursiveDirectories() khá hay:
vendor/magento/magento2-base/setup/src/Magento/Setup/Model/FilePermissions.php

/**
 * Check all sub-directories and files except for var/generation and var/di
 *
 * @param string $directory
 * @return bool
 */
private function checkRecursiveDirectories($directory)
{
    $directoryIterator = new \RecursiveIteratorIterator(
        new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS),
        \RecursiveIteratorIterator::CHILD_FIRST
    );
    $noWritableFilesFolders = [
        $this->directoryList->getPath(DirectoryList::GENERATION) . '/',
        $this->directoryList->getPath(DirectoryList::DI) .'/'
    ];

    $directoryIterator = new Filter($directoryIterator, $noWritableFilesFolders);

    try {
        foreach ($directoryIterator as $subDirectory) {
            if (!$subDirectory->isWritable()) {
                return false;
            }
        }

Wow, mới đục có 1 phát thôi mà ta đã nắm được hệ thống tập tin, thư mục rồi, vì các class trên có cung cấp các hàm tạo, xóa, copy nữa.

Đục phát nữa

Sau khi hệ thống check quyền, tiếp theo họ sẽ bật Maintenance mode, để người dùng sẽ thấy thông báo Web đang được bảo trì, đây là chức năng rất hữu ích mỗi khi web deploy, upgrade… Magento 2 cũng có command line để làm việc ấy. Nhảy vào hàm setMaintenanceMode() xem nào:
vendor/magento/framework/App/MaintenanceMode.php

public function set($isOn)
{
    if ($isOn) {
        return $this->flagDir->touch(self::FLAG_FILENAME);
    }
    if ($this->flagDir->isExist(self::FLAG_FILENAME)) {
        return $this->flagDir->delete(self::FLAG_FILENAME);
    }
    return true;
}

flagDir thật ra chính là class làm việc với thư mục ở phần trên, hàm touch() sẽ tạo ra 1 tập tin với tên .maintenance.flag để tại webroot/var
Tại sao hàm touch() bên trên chỉ đưa tên tập tin mà không tạo ngoài webroot mà lại ở trong var? Vì construct của class MaintenanceMode, set flagDir trỏ tới thư mục var.
Vậy là ta đã hiểu bản chất của vấn đề, sau này có quên dòng lệnh để bật tắt maintenance mode hoặc chỉ có quyền FTP, không có SSH, ta có thể dễ dàng upload 1 tập tin rỗng vào webroot/var/.maintenance.flag

Next cái maintenance này, ta xem tiếp hàm installDeploymentConfig(), hàm này khác hay hàm bên trên vì có cả tham số truyền vào là cái $param. À, vậy là hiểu, hàm này sẽ nhận các tham số ta đưa vào lúc chạy lệnh setup như: --base-url=http://magento2.loc --db-host=localhost --db-name=mage2 --db-user=root
Để cấu hình cho web đây mà, deployment config chính là như thế:

public function installDeploymentConfig($data)
{
    $this->checkInstallationFilePermissions();
    $userData = is_array($data) ? $data : $data->getArrayCopy();
    $this->setupConfigModel->process($userData);

Hàm process tiến hành phân tích các tham số truyền vào

public function process($inputOptions)
{
    $this->checkInstallationFilePermissions();

    $options = $this->collector->collectOptionsLists();

    foreach ($options as $moduleName => $option) {
        $configData = $option->createConfig($inputOptions, $this->deploymentConfig);

Hàm này sẽ set cấu hình cho website

public function createConfig(array $data, DeploymentConfig $deploymentConfig)
{
    $configData = [];
    $configData[] = $this->configGenerator->createCryptConfig($data, $deploymentConfig);
    $configData[] = $this->configGenerator->createSessionConfig($data);
    ...
    $configData[] = $this->configGenerator->createDbConfig($data);

Ở đây có cái hàm createDbConfig là Database chứ gì nữa, vào xem nào

public function createDbConfig(array $data)
{
    $configData = new ConfigData(ConfigFilePool::APP_ENV);

    $optional = [
        ConfigOptionsListConstants::INPUT_KEY_DB_HOST,
        ConfigOptionsListConstants::INPUT_KEY_DB_NAME,
        ConfigOptionsListConstants::INPUT_KEY_DB_USER,
        ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD,
        ConfigOptionsListConstants::INPUT_KEY_DB_MODEL,
        ConfigOptionsListConstants::INPUT_KEY_DB_ENGINE,
        ConfigOptionsListConstants::INPUT_KEY_DB_INIT_STATEMENTS,
    ];

Chuẩn rồi, đây chính là các cấu hình database cho website.
Vậy các cấu hình này sẽ được lưu vào database, trong table nào đó?
Không, chú ý bên dưới của hàm process

$this->writer->saveConfig($fileConfigStorage, $config->isOverrideWhenSave());
public function saveConfig(array $data, $override = false)
{
    $this->filesystem->getDirectoryWrite(DirectoryList::CONFIG)->writeFile($paths[$fileKey], $contents);

Thì ra những cấu hình này sẽ được ghi ra file, thú vị chưa? File nào ư? hãy xem lại phần đầu của hàm createDbConfig(), APP_ENV tương ứng với: webroot/etc/env.php

Đục core Magento 2 – phần cài đặt hoạt động như thế nào?
Đánh giá bài viết

1 phản hồi

Gửi phản hồi

Your email address will not be published. Required fields are marked *