PHP library
Framework
- Symfony
- CakePHP
- FuelPHP: 2010年誕生。
- Codeigniter: シンプル、軽量。
- Zend
- Laravel: 2011年誕生。
- Phalcon: 高速で有名。
- Yii - Wikipedia
素直にPhalconを使うのが安定ではないか?ああ、C拡張が必要だから、共用サーバーへのインストールで困る。
Directory
https://chatgpt.com/c/67bd2c93-ee1c-800b-b4ff-d40a326dcf0c
MVC系の画面の場合、Controllerの共通処理 (Service) は、classやService系のディレクトリーに配置する。
act/action系のディレクトリーは、ユーザーアクセスやルーティングと紐づいているのでここにあまり入れない。
テンプレート系のVewファイルはincludeやlayoutなどの専用のディレクトリーに格納するなど。
Template
いろいろある。
- Blade: Laravel標準。
- DIV: 1ファイルでシンプル。大規模には向かない。
- Smarty: 万能。
- Twig: 拡張はしにくい。
使うとしたら、歴史の長いSmarty。
- Smartyとは?基礎知識と具体的なメリットをわかりやすく解説 - システム開発のプロが発注成功を手助けする【発注ラウンジ】
- Smartyとは? - smarty @Wiki - atwiki(アットウィキ)
高速らしい。
そもそもテンプレートエンジンがいるのかどうかという議論がある。
- Smarty って、要らなくない? — INWORKS
- 素のPHPはもはやテンプレートエンジンとしては使えない - ぱせらんメモ
- 本当に倒すべきだったのは jQuery ではなくテンプレートエンジンだった - fsubal
- サーバサイドHTMLテンプレートからの脱却のススメ (1/4)|CodeZine(コードジン)
UI/UXを突き詰めると、JavaScriptを使わざるを得ず、サーバーテンプレートエンジンは初回だけなので、いっそのことJSで全部やろうというのが最近の流れの模様。
PHP自体が一種のテンプレートエンジンという主張がある。が、関数をあれこれ書く必要があり、可読性が悪い。
SmartyよりTwigのほうが性能が上とか。
「Volt: テンプレートエンジン — Phalcon 3.0.2 ドキュメント (Japanese / 日本語)」。高速フレームワークのPhalconではVoltを使っている。
Twig
- 素のPHPはもはやテンプレートエンジンとしては使えない - ぱせらんメモ
- Templating Engines in PHP |Articles - Fabien Potencier
- PHPテンプレートエンジンを使おう Twig編 | AkisiのWEB制作日記
- Decoded Node: Smarty vs. Twig (a benchmark done poorly)
- GitHub - AliShareei/smarty-vs-twig-benchmark: A benchmark of up-to-date versions of Smarty and Twig templating engines
- GitHub - dominics/smarty-twig-benchmark: A benchmark of up-to-date versions of Smarty and Twig templating engines
- The fastest template engine for PHP | by Serge Gotsuliak | Medium
- Pure PHP/HTML views VS template engines views - Stack Overflow
Twig v3のほうが速いらしいが、Smarty v3のほうが速いというデータもある。
Smarty
- GitHub - smarty-php/smarty: Smarty is a template engine for PHP, facilitating the separation of presentation (HTML/CSS) from application logic.
- Smarty Documentation
「Pure PHP/HTML views VS template engines views - Stack Overflow」が決定的だった。Smartyの開発者がSmartyのほうがTwigより速いと回答していた。2012年。Smartyでいいと思う。
カスタムタグ
https://chatgpt.com/c/6789e2f7-6df0-800b-ac87-2254034abd4e
関数プラグイン{function}とブロックプラグイン{block}の2種類で独自のタグを定義できる。
{function} - Smarty Documentation
{block} - Smarty Documentation
デフォルトのプラグインディレクトリー (plugins) にfunciton.<function-name>.phpとして保存して使う。
2. Basic Syntax
Embedding Vars in Double Quotes
Embedding Vars in Double Quotes | Smarty
Smartyは二重引用符内で、assigned variableを認識する。その都合、変数名は数字、文字、_のみが許容される。
ピリオド.やオブジェクト演算子->などは、その都合でバックチック``で囲む必要がある。
Smarty v3では、二重引用符内でタグも許容する。これは、修飾子付きの変数などで便利だ。
バックチックは二重引用符とセットで使う。変数で.や->がある場合の認識用。{}ではない。
Conditional operator
Ternary operators · smarty-php/smarty · Discussion #660
- {($foo) ? $var1 : $var2}
- {$myvar|default:"foobar"}`
- {if $myvar}was set{else}not set{/if}
Smarty v5から条件演算子に対応している。ただし、評価部分は丸括弧が必要。公式文書で文書化はされていないらしい。
v5以前だと工夫が必要。|defaultでやるのがいいとか。ただし、これは変数が空のときだけ。{$myvar?:'N/A'}相当は|defaultで代替できる。
Logical operator
Smarty v2では、Smartyの関数の引数部分では、論理演算子が使えない。{if}だと使えるのだが。しかたないので、専用の変数を用意する。
7. Builtin-in Functions
{assign}
Chapter 8. Custom Functions | Smarty
template内で変数を割り当てする。
{assign var='name' value='Bob'} The value of $name is {$name}.
https://chatgpt.com/c/67a0393a-9920-800b-805a-1c344febfdc2
ただし、Smarty v2のassignは配列リテラルの代入はできない。工夫が必要。
一番簡単なのはexplode修飾子。
{assign var="fruits" value="apple,banana,cherry"|explode:","}
他にはappendや
{append var="fruits" value="Apple"} {append var="fruits" value="Banana"} {append var="fruits" value="Cherry"}
空の配列を作って、キーを指定して追加。
{assign var="fruits" value=""} {* まず空の配列を作成 *} {assign var="fruits.apple" value="Apple"} {assign var="fruits.banana" value="Banana"} {assign var="fruits.cherry" value="Cherry"}
根本的にはPHP側で変数で用意しておくのがよい。
{capture}
ブロック内を変数に押し込めるための機能。
{capture name="<name>"} {/capture}
nameで指定した<name>にcapture内が入る。
nameを指定しない場合、defaultとみなされる。
- name
- assign
- append
特にappendのパラメーターが重要。これを使うことでcssやjsを共通のheaderの共通部分に押し込める。
captureの内容を参照する場合は、$smarty.capture.<name>で参照する。
capture appendで変数名を指定する。Smarty 3.1からはこの変数が自動結合されている。
3.1未満の場合、配列になっているので使う際にforeachなどが必要。
https://chatgpt.com/c/67cab73d-0878-800b-b3ec-766c42eaf42b
なお、capture内で変数を使っている場合、captureブロック定義時に固定される。$smarty.capture.<name>を使う際に、引き渡したい場合、evalのような工夫が必要になる。
あるいは、他に以下の方法がある。
- if/assign: 短いテキストだけが変わる場合、if/assignでそこだけ変数で可変にするのもいい。
- capture: if/assignのassign部分が複数行の場合に有効。
- replace: captureした文章の一部分だけを変更する場合。
{fetch}
指定したファイルを取得してそれをそのまま表示する。
{foreach}
Smarty v3からcontinue/breakが登場。
{foreach from=<array> item=<element> key=<key> name=<prop>}
nameはSmarty v2専用。foreachのプロパティーへのアクセスに必要。
- index
- iteration
- first
- last
- show
- total
name=fooのとき、$smarty.foreach.foo.last}のようにして、foreach内でforeachのプロパティーにアクセスできる。
ただし、Smarty v3から構文が変わった。name=は使わない方がよさそう。
{if},{elseif},{else}
{literal}
https://chatgpt.com/c/6799d1f8-dd54-800b-a5cc-b22c453083fb
{などはSmartyのタグとして解釈される部分では解釈される。CSSやJavaScriptなどで{をそのまま使いたい場合、{literal}タグで解釈を阻止できる。
ただ、{literal}が不要なケースもある。capture/block内。これらがliteral相当の機能がある。
{strip}
tpl内の空白空行を除去して1行でHTMLを表示する。tpl内のコードの可読性を確保しながら、結果に余計な空白を作らない。
html要素の前後を{strip}で囲むのが基本的な使用方法。
{* the following will be all run into one line upon output *} {strip} <table border='0'> <tr> <td> <a href="{$url}"> <font color="red">This is a test</font> </a> </td> </tr> </table> {/strip}
なお、{strip}は変数には影響を与えない。変数内に作用したければ、strip修飾子を使う。
Advanced feature
Template inheritance
About
継承により、オブジェクト指向プログラミングの概念をテンプレートに取り込める。ただし、block/extendsはSmarty v3.0からしか使えない。v2ではincludeやcaptureを駆使するしかない。
子テンプレートで拡張可能な基本テンプレートを定義できる。拡張は、子テンプレート親の名前付きブロック領域の上書きを意味する。
- 子テンプレートは、blockタグ内しか記述できず、blockタグ以外は削除される。
- blockタグの内容は、append/prependオプションでマージ可能。または、$smarty.block.parent/$smarty.block.childのプレースホルダーを使える。
- テンプレート継承は、コンパイル時に単一のコンパイル済みテンプレートを作成する。includeよりもパフォーマンスが大幅に向上する。
- 子テンプレートは先頭でextendsタグで親を指定して、拡張する。extendsタグの他に、PHPでfetch/display指定時にextendsを指定することでもできる。
イメージ。
// layout.tpl <html> <head> <title>{block name=title}Default Page Title{/block}</title> {block name=head}{/block} </head> <body> {block name=body}{/block} </body> </html>
// myproject.tpl {extends file='layout.tpl'} {block name=head} <link href="/css/mypage.css" rel="stylesheet" type="text/css"/> <script src="/js/mypage.js"></script> {/block}
// mypage.tpl {extends file='myproject.tpl'} {block name=title}My Page Title{/block} {block name=head} <link href="/css/mypage.css" rel="stylesheet" type="text/css"/> <script src="/js/mypage.js"></script> {/block} {block name=body}My HTML Page Body goes here{/block}
描画結果は以下。
<html> <head> <title>My Page Title</title> <link href="/css/mypage.css" rel="stylesheet" type="text/css"/> <script src="/js/mypage.js"></script> </head> <body> My HTML Page Body goes here </body> </html>
継承関係で使うタグ
- block
- extends
- include
- insert: ただし非推奨。
なお、extendsした場合、子テンプレートで入力内容を表示させるには、基本的にblockを使うしかない。親の変数にassignで代入して表示することは一応できる。それ以外は無視されて表示されない。
{block}
blockで追記の記法がいくつかあるので注意が必要。基本は、$smarty.block.childを親側に配置だと思う。
- prepend
- append
$smarty.block.child
$smarty.block.parent
prependとappendはparentを中心に考える。appendは子の後ろに親を追加する。子側でオプション指定が必要になる。
上書きと追記を子で切り替えられる利点はあるが、上書きすることないなら、childを親に配置したほうがわかりやすい。
{extends}
{include}
外部のtplファイルを読み込める。Smarty v2までだとこれを使うしかない。
{include file='links.tpl' title='Newest links' links=$link_array}
fileだけ必須。他はvar=value形式で、変数をinclude先に渡せる。
{include file='common.tpl' value="`$app.var1` `$app.var2`"}
変数名はなんでもいいが、こだわりがないならvalueでいい気がする。assignなどでも共通だし。
引数で連結したい場合、""と``を組み合わせる。
Smarty v2
block/extendsはSmarty v3.0から導入された。v2で似たような継承を使った共通化には工夫が必要になる。
include/captureを駆使して実現する。captureで定義した変数は、定義後しか使用できない。capture変数を定義前は参照できない。したがって、以下の順序で指令を使う。
- capture定義
- includeでcapture変数引き渡し
さらに、以下のロジックで末端の定義変数を全体のテンプレートで最後に全部参照できるようにする。
- 画面固有.tpl
- 共通.tpl
- 全体.tpl
captureで定義してからしか変数を参照できないので、以下の順番でincludeの階層関係を作って、末端の変数が全体で参照できるようにする。
Other
Version
https://chatgpt.com/c/6799d1f8-dd54-800b-a5cc-b22c453083fb
// .tpl {$smarty.version}
phpinfo();
require_once('libs/Smarty.class.php'); $smarty = new Smarty(); echo 'Smarty Version: ' . $smarty->getVersion();
CSSのコンポーネント
Smartyでコンポーネント単位でCSSを適用したい。
https://chatgpt.com/c/6799d1f8-dd54-800b-a5cc-b22c453083fb
CSSはhead要素のstyleに置くしかないのをどうにか1ファイル単位で管理したい。
できれば1個のtplファイルでHTML/CSS/JSをコンポーネントとして扱えるといい。最悪インラインCSSでも。
AIに質問したら、CSSだけのファイルをtplに固めてheader.tplでhead要素内に配置するといういまいちな回答。
- captureでglobalヘッダーに追記。
- block
- assignで変数に設定。
captureとblockがいい。appendでスタイルを追記できる。
extendsしているならblockでいい。extendsしていなくてincludeでやっているなら、captureでやる。
<style> {foreach from=$styles item=style} {$style} {/foreach} </style>
{capture append='styles'}{literal} .js_marquee_challenge_title { position: relative; width: 260px; height: 33px; margin: 0 auto; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } {/literal}{/capture}
smarty v2だと配列になるのでforeachでやる。
captureが{を解釈するのでliteralでやる。
問題。base.tplでheader.tplとcomponent.tplをincludeすると、component.tplの変数がheader.tplに反映されない。
- postfilterでbody以下に登場するstyle/cssをheadの直前に配置するなどする。
- インラインCSSで対応。
インラインCSSで対応できる分はこれで対応したらいいと思う。インラインCSSでどうにもできないケースになったら、そのときに考える。
最悪、JavaScriptでstyle要素を追記して対応する。JavaScriptに依存するのであまりよくないが、最悪これでなんとでもなる。
Pagination
- https://chatgpt.com/c/67b69a17-9410-800b-8a2a-529beb733175
- Smartyでページ送り ( しゃいん☆のブログ| 名古屋市 Webシステム開発 サーバ構築 ネットワーク構築 株式会社コネクティボ )
Smarty自体にはページネーション用の機能はない。自分で作る。
ORM
Ref:
いろいろある。Doctrineが有名。
- Doctrine: Symfonyで採用。有名。
- Eloquent
- Propel: Symfony v1.2で採用されていた。
- PHP activerecord
- PHPDAO
- PDO: PHP標準。
- Xyster
ただ、速度を優先する場合、PDOが最速になるらしい。
ORMは別になくてもいいか。いや、validateなどでほしいか。
ChatGPT: PHPのORMで最高速なのはどれ?
PHPのORMで「最高速」を目指す場合、目的やデータ量、柔軟性や機能性のニーズによって適切な選択肢が変わります。ただ、以下は一般的に高速性が評価されているORMやアプローチです。
- 1. **Cycle ORM**
- **概要**: Cycle ORMは、PHP向けに軽量で高性能なORMとして設計されており、メモリ使用量やクエリ生成において優れた最適化がなされています。また、`DataMapper`パターンを採用しており、柔軟性が高く、複雑なデータ構造にも対応可能です。
- **特徴**:
- メモリ効率が良く、データベースへのアクセスが高速。
- マッピング定義が柔軟で、データベーススキーマの同期などの管理機能も備えている。
- **適合するケース**: 高速性が求められるが、複雑なデータ構造やリレーションも必要な場合。
- **URL**: [Cycle ORM](https://cycle-orm.dev/)
- 2. **Doctrine ORM(高度に最適化した場合)**
- **概要**: Doctrineは多機能で、エンタープライズレベルのアプリケーションにも使用されていますが、設定やキャッシュの適切な調整で高速化が可能です。Doctrineは大規模なデータベースや複雑なエンティティ構造を扱うことを得意としており、クエリキャッシュやメタデータキャッシュを活用すると、高速なクエリ処理が実現できます。
- **特徴**:
- キャッシュ(メタデータ、クエリ、結果)を駆使することでパフォーマンスが向上。
- バッチ処理やフェッチモードの調整により、余分なSQLクエリを抑制できる。
- **適合するケース**: 大規模なアプリケーションで、柔軟性や機能性と高速性のバランスが求められる場合。
- **URL**: [Doctrine ORM](https://www.doctrine-project.org/projects/orm.html)
- 3. **Eloquent ORM(軽量化する場合)**
- **概要**: Eloquentは、Laravelの標準ORMで、柔軟で書きやすいAPIを提供しています。ただし、Eloquentは`Active Record`パターンを採用しているため、大量のデータや複雑なクエリには向きません。しかし、シンプルなアプリケーションであれば、十分な速度を発揮できます。
- **特徴**:
- データベースアクセスが少ないケースで非常に簡便で高パフォーマンス。
- 適切なスコープやクエリビルダーを利用することで高速なデータアクセスが可能。
- **適合するケース**: シンプルなアプリケーションで、多少の軽量化(不必要なEloquentモデルの削除など)を行い、APIの利便性と速度を両立したい場合。
- **URL**: [Eloquent ORM](https://laravel.com/docs/eloquent)
- 4. **直接的なクエリビルダー使用(PDO, Laminas DBなど)**
- **概要**: ORMを使わず、PDOやクエリビルダー(Laminas DBなど)を用いて、データベースと直接やり取りすることも非常に高速な手法です。ORMのオーバーヘッドがないため、特にトランザクション内での多くのデータ操作が求められる場合や、複雑なクエリが必要な場面では、これが最速のアプローチです。
- **特徴**:
- ORMを介さず直接データベースを操作するため、最小限のオーバーヘッドで済む。
- 柔軟で高度なクエリやバッチ操作も容易。
- **適合するケース**: 速度重視の大規模アプリケーションや、複雑なクエリが必要なケース。
これらを総合的に考えると、 **「Cycle ORM」** が最もバランスよく高性能を発揮しやすく、ORMの利便性を保持しながらも高速なデータ操作が可能です。しかし、最高速を求めるのであれば、ORMを使わずにクエリビルダーやPDOを直接活用することが理想的です。
Cycle ORMが安定して速いらしい。これでいくか。
Migrate
- Phinx
- Doctrine Migrations
- cycle/migrations: Cycle ORM: Databases - Migrations
「PHPで「Doctrine Migrations」を使ってみる」
CakePHPに採用されているPhinxのほうが人気なのでPhinxを使ったほうがよいだろう。
ORMとあわせたほうがいいかも。
Test
- PHPUnit
Search
検索キーワードをフォームから受信後、DBにSQLで検索をかけて取得結果を返すのが基本。
それとは別に、検索用のアプリにリクエストを受け渡しして検索するという方法がある。どちらでもいけるような、ドライバーのライブラリーがある。
- Laravel Scout - Laravel 9.x - The PHP Framework For Web Artisans
- teamtnt/tntsearch: A fully featured full text search engine written in PHP
- ruflin/elastica - Packagist (40 Best PHP Libraries For Web Applications in 2022)
検索サービスで有名なのは以下。
- ElasticSearch/OpenSearch
- MeiliSearch
- Algolia
- Sphinx Search (Sphinx | Open Source Search Engine)
- Apache Solr (Welcome to Apache Solr - Apache Solr)/Apache Lucene (Apache Lucene - Welcome to Apache Lucene)
Laravel Scoutでtntsearchを使う方法がある (Laravel Scout + TNTSearchによる小規模プロジェクトへの全文検索機能の追加 #PHP - Qiita/Laravel ScoutとTNTSearchを使用してサイト全文検索を実装してみる – helog)。
「Packagist」の検索結果をみても、tntsearchが特に人気の模様。
PEAR
About
PEAR (PHP Extension and Application Repository, ペア)
PHPで利用可能なライブラリーを提供しているサービス。PEARはPHPで書かれたライブラリー。PECLはC言語の拡張ライブラリー (extension)。
PHP 5までPHP本体に同梱されていた。
準標準ライブラリー。名前空間なしで見知らぬクラス名が登場したらPEARの可能性がある。
Directory
PEAR同梱のpear config-showで各種インストールディレクトリーがわかる。
特に重要なのが、php_dir (=/usr/share/pear or php)。ここにPEARのPHPファイルやクラス定義がある。シンボル参照などで重要。