Slide 1

Slide 1 text

Composer による オートロード導入時の課題解決の話 ホスティング事業部 Tech MTG vol.1

Slide 2

Slide 2 text

内村元樹 @genkiroid ホスティング事業部ロリポップグループ エンジニア https://genkiroid.github.io/ 自己紹介

Slide 3

Slide 3 text

1. Composer 導入の動機 2. 不要になる require_once を整理する際の課題 3. まとめ アジェンダ

Slide 4

Slide 4 text

Composer 導入の動機

Slide 5

Slide 5 text

● PHP5.6 ● 元々PHP4だったものを数年前にPHP5に移行 ● MVC構成と非MVC構成が混在 ● クラスのオートローディングは未導入 ● ライブラリは個別導入されており一元化されていない 対象システムの現状

Slide 6

Slide 6 text

● PHP5.6 ● 元々PHP4だったものを数年前にPHP5に移行 ● MVC構成と非MVC構成が混在 ● クラスのオートローディングは未導入 ● ライブラリは個別導入されており一元化されていない Composerにより解決できる問題

Slide 7

Slide 7 text

クラスのオートローディングがな いのは問題か?

Slide 8

Slide 8 text

大量のrequire系ステートメントの存在

Slide 9

Slide 9 text

● コードの見通しが悪い ● 本当に必要かどうか分からなくなっている ● requireする順序への暗黙の依存 ● requireが模範とされ増殖していく ● 不必要なタイミングでのロードによるパフォーマンス影響 ● etc それによる弊害

Slide 10

Slide 10 text

今回お話すること requireによる問題を解決するために、Composerによるクラスのオートロー ディングを導入することにしました。 Composerの導入自体は容易でしたが、不要になるrequire系ステートメントを 整理する作業には課題がありました。 今回は、その課題をどうのように解決したかについてお話しします。

Slide 11

Slide 11 text

不要になるrequireを整 理する際の課題

Slide 12

Slide 12 text

requireの対象の内容が以下のような場合、そのrequireは消せない。 ● グローバルスコープの変数を定義している ● グローバルスコープの定数を定義している ● グローバルスコープの関数を定義している ● ロード時に実行される処理が記載されている ● etc... つまり、 純粋なクラス定義ファイルのrequireしか消したらだめ。

Slide 13

Slide 13 text

● 目検?(ファイル数は4000を超える) ● grepなどを駆使してなんとかする? ● etc 純粋なクラス定義ファイルかどうかをどうやって確認する?

Slide 14

Slide 14 text

コードを書いて解決できそう

Slide 15

Slide 15 text

イメージ

Slide 16

Slide 16 text

1. 削除したrequire系ステートメントとそれが書かれていたファイルのパスを 収集 /** * getChangedFiles * * @param Gitonomy\Git\Repository $repo * @param string $revFrom * @param string $revTo * @return array */ public static function getChangedFiles(Repository $repo, $revFrom, $revTo) { return $repo->getDiff("{$revFrom}..{$revTo}")->getFiles(); }

Slide 17

Slide 17 text

2. requireに渡されているパス文字列(定数等含む)を取得 const INCLUDE_STMT_PATTERN = '/^(*)(include_once|include|require_once|require)(\((?P.*)\)|+(?P.*));( *)$/'; $isMatch = preg_match(Turpan::INCLUDE_STMT_PATTERN, $line, $matches); if (!$isMatch) { continue; } $tmp['file'] = realpath($file->getOldName()); $tmp['required_file'] = (!empty($matches['required_file_1'])) ? $matches['required_file_1'] : $matches['required_file_2']; $tmp['required_file'] = str_replace('__FILE__', "'{$tmp['file']}'", $tmp['required_file']);

Slide 18

Slide 18 text

3. evalしてrequireに渡されたパスの実行時の値を取得 $requiredPath = eval('return ' . $m['required_file'] . ';');

Slide 19

Slide 19 text

4. ファイルを静的解析し、クラス定義のみであるかを判定 $parser = (new ParserFactory)->create(ParserFactory::ONLY_PHP5); $requiredContent = file_get_contents($requiredPath); $nodes = $parser->parse($requiredContent); if (self::isPureClassFile($nodes)) { … }

Slide 20

Slide 20 text

https://packagist.org/packages/genkiroid/turpan

Slide 21

Slide 21 text

使い方 Create example.php for example.

Slide 22

Slide 22 text

実行結果

Slide 23

Slide 23 text

● クラス定義とrequire系ステートメント以外を含むようなファイルのrequire を削除している場合、Failする。 ● requireに渡しているパスに変数を使用しており、実行時にしかパスが分 からないような場合は、解析不能なためErrorとして報告される。 上記により、アグレッシブにrequireを削除しても、それが問題ないかどうかを 何度でも正確にすぐにチェック出来るようになった。 この点はコードによる解決ならではと言える。 課題の解決

Slide 24

Slide 24 text

まとめ

Slide 25

Slide 25 text

● 導入自体は容易なComposerだが、それとは別に解決しなければならな い問題があることもある。 ● 問題の解決をコードによって行い、最終的に大きな問題を起こすことなく Productionに導入出来たことは、コスト的にも良かったと考える。 ● 同じような問題を抱えている現場があるかもしれないので、今回書いた コードが参考になればと思う。 まとめ

Slide 26

Slide 26 text

ご清聴ありがとうございました