「PHP library」の版間の差分

提供:senooken JP Wiki
(Conditional operator)
 
(同じ利用者による、間の19版が非表示)
70行目: 70行目:


「[https://stackoverflow.com/questions/9363215/pure-php-html-views-vs-template-engines-views Pure PHP/HTML views VS template engines views - Stack Overflow]」が決定的だった。Smartyの開発者がSmartyのほうがTwigより速いと回答していた。2012年。Smartyでいいと思う。
「[https://stackoverflow.com/questions/9363215/pure-php-html-views-vs-template-engines-views Pure PHP/HTML views VS template engines views - Stack Overflow]」が決定的だった。Smartyの開発者がSmartyのほうがTwigより速いと回答していた。2012年。Smartyでいいと思う。
が、IDEのコード補完の対応状況がSmartyはいまいち。そこが生産性に響く。Laravelは嫌いだからTwigにする。


==== カスタムタグ ====
==== カスタムタグ ====
83行目: 85行目:


==== 2. Basic Syntax ====
==== 2. Basic Syntax ====
===== Comments =====
{* comment *}
コメント内に*}を記載できない。工夫が必要。
# 分割記述
# HTMLコメント: HTML上に残る。
# assignを利用した変数埋め込み。
# カスタムタグ
===== Variables =====
[https://www.smarty.net/docs/en/language.syntax.variables.tpl Variables | Smarty]
基本はPHPの変数と同じ。ただし、配列要素にアクセスする.記法がある。
<nowiki>{$foo}        <-- displaying a simple variable (non array/object)
{$foo[4]}    <-- display the 5th element of a zero-indexed array
{$foo.bar}    <-- display the "bar" key value of an array, similar to PHP $foo['bar']
{$foo.$bar}  <-- display variable key value of an array, similar to PHP $foo[$bar]
{$foo->bar}  <-- display the object property "bar"
{$foo->bar()} <-- display the return value of object method "bar"
{#foo#}      <-- display the config file variable "foo"
{$smarty.config.foo} <-- synonym for {#foo#}
{$foo[bar]}  <-- syntax only valid in a section loop, see {section}
{assign var=foo value='baa'}{$foo} <--  displays "baa", see {assign}
Many other combinations are allowed
{$foo.bar.baz}
{$foo.$bar.$baz}
{$foo[4].baz}
{$foo[4].$baz}
{$foo.bar.baz[4]}
{$foo->bar($baz,2,$bar)} <-- passing parameters
{"foo"}      <-- static values are allowed
{* display the server variable "SERVER_NAME" ($_SERVER['SERVER_NAME'])*}
{$smarty.server.SERVER_NAME}
Math and embedding tags:
{$x+$y}                            // will output the sum of x and y.
{assign var=foo value=$x+$y}        // in attributes
{$foo[$x+3]}                        // as array index
{$foo={counter}+3}                  // tags within tags
{$foo="this is message {counter}"}  // tags within double quoted strings
Defining Arrays:
{assign var=foo value=[1,2,3]}
{assign var=foo value=['y'=>'yellow','b'=>'blue']}
{assign var=foo value=[1,[9,8],3]}  // can be nested
Short variable assignment:
{$foo=$bar+2}
{$foo = strlen($bar)}              // function in assignment
{$foo = myfunct( ($x+$y)*3 )}      // as function parameter
{$foo.bar=1}                        // assign to specific array element
{$foo.bar.baz=1}                   
{$foo[]=1}                          // appending to an array
Smarty "dot" syntax (note: embedded {} are used to address ambiguities):
{$foo.a.b.c}        =>  $foo['a']['b']['c']
{$foo.a.$b.c}      =>  $foo['a'][$b]['c']        // with variable index
{$foo.a.{$b+4}.c}  =>  $foo['a'][$b+4]['c']      // with expression as index
{$foo.a.{$b.c}}    =>  $foo['a'][$b['c']]        // with nested index
PHP-like syntax, alternative to "dot" syntax:
{$foo[1]}            // normal access
{$foo['bar']}
{$foo['bar'][1]}
{$foo[$x+$x]}        // index may contain any expression
{$foo[$bar[1]]}      // nested index
{$foo[section_name]}  // smarty {section} access, not array access!
Variable variables:
$foo                    // normal variable
$foo_{$bar}              // variable name containing other variable
$foo_{$x+$y}            // variable name containing expressions
$foo_{$bar}_buh_{$blar}  // variable name with multiple segments
{$foo_{$x}}              // will output the variable $foo_1 if $x has a value of 1.
Object chaining:
{$object->method1($x)->method2($y)}
Direct PHP function access:
{time()}</nowiki>
===== Functions =====
====== PHP function ======
https://grok.com/share/c2hhcmQtMw%3D%3D_67e61609-8350-420f-9bfb-63292690e0a9
Smarty内でPHP標準関数や、メソッドを使う方法がある。
まず、PHP標準関数は使用可能箇所が限定されている。
# variable modifier
# {php}タグ内
# カスタム関数・プラグイン
基本は変数修飾子でだけ使える。
ただし、変数に割り当てたメソッドはどこでも使える。


===== Embedding Vars in Double Quotes =====
===== Embedding Vars in Double Quotes =====
105行目: 216行目:


v5以前だと工夫が必要。|defaultでやるのがいいとか。ただし、これは変数が空のときだけ。{$myvar?:'N/A'}相当は|defaultで代替できる。
v5以前だと工夫が必要。|defaultでやるのがいいとか。ただし、これは変数が空のときだけ。{$myvar?:'N/A'}相当は|defaultで代替できる。
===== Logical operator =====
Smarty v2では、Smartyの関数の引数部分では、論理演算子が使えない。{if}だと使えるのだが。しかたないので、専用の変数を用意する。
==== 5. Variable Modifiers ====
variable modifiers (変数修飾子) は、変数、カスタム関数、文字列に適用される。値の後に、|と修飾子名を指定する。複数指定可能で、その場合:で区切る。
Smarty v2までは、配列への適用時は@が必要だった。v3から不要になった。
PHP関数は全部指定可能。ただし、注意点がある。
# 引数の順序が直観的じゃない。値部分が第1引数で、修飾子の後ろに:で第2引数以後が続く。わかりにくい。
# セキュリティーポリシーで使用可能な関数が決まっている。


==== 7. Builtin-in Functions ====
==== 7. Builtin-in Functions ====
132行目: 256行目:
  {assign var="fruits.cherry" value="Cherry"}
  {assign var="fruits.cherry" value="Cherry"}
根本的にはPHP側で変数で用意しておくのがよい。
根本的にはPHP側で変数で用意しておくのがよい。
assignではboolの評価式を変数に保存とかはできない。そういうことをしたい場合、captureを使う。
https://chatgpt.com/share/682c4a41-5ec4-800b-9b65-c0d7b3e08613
{capture name="is_special"}{strip}
    {if $a == 2 || $a == 3}1{else}0{/if}
{/strip}{/capture}
$smarty.capture.is_special
stripで改行類の除去を忘れずに。


===== {capture} =====
===== {capture} =====
169行目: 302行目:


指定したファイルを取得してそれをそのまま表示する。
指定したファイルを取得してそれをそのまま表示する。
===== {foreach} =====
* [https://www.smarty.net/docsv2/en/language.function.foreach.tpl {foreach},{foreachelse} | Smarty]
* [https://www.smarty.net/docs/en/language.function.foreach.tpl#foreach.construct.continue {foreach},{foreachelse} | Smarty]
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} =====
[https://www.smarty.net/docsv2/en/language.function.if.tpl {if},{elseif},{else} | Smarty]
ifで使える演算子に癖がある。
{| class="wikitable"
!Qualifier
!Alternates
!Syntax Example
!Meaning
!PHP Equivalent
|-
|==
|eq
|$a eq $b
|equals
|==
|-
|!=
|ne, neq
|$a neq $b
|not equals
|!=
|-
|>
|gt
|$a gt $b
|greater than
|>
|-
|<
|lt
|$a lt $b
|less than
|<
|-
|>=
|gte, ge
|$a ge $b
|greater than or equal
|>=
|-
|<=
|lte, le
|$a le $b
|less than or equal
|<=
|-
|===
|
|$a === 0
|check for identity
|===
|-
|!
|not
|not $a
|negation (unary)
|!
|-
|%
|mod
|$a mod $b
|modulous
|%
|-
|is [not] div by
|
|$a is not div by 4
|divisible by
|$a % $b == 0
|-
|is [not] even
|
|$a is not even
|[not] an even number (unary)
|$a % 2 == 0
|-
|is [not] even by
|
|$a is not even by $b
|grouping level [not] even
|($a / $b) % 2 == 0
|-
|is [not] odd
|
|$a is not odd
|[not] an odd number (unary)
|$a % 2 != 0
|-
|is [not] odd by
|
|$a is not odd by $b
|[not] an odd grouping
|($a / $b) % 2 != 0
|}
特に注意が必要なのは、!==がないこと。たぶん、! ($a === $b) みたいなことが必要。
smarty v3だとたぶんいけるとおもう。
他に、!==以外の、「 All PHP conditionals and functions are recognized, such as ||, or, &&, and, is_array(), etc.」も使える。xorとかもいけると思われる。


===== {literal} =====
===== {literal} =====
374行目: 628行目:


Smarty自体にはページネーション用の機能はない。自分で作る。
Smarty自体にはページネーション用の機能はない。自分で作る。
===== 配置場所 =====
https://chatgpt.com/c/6808a726-7964-800b-ac9c-36722d04ef0a
templatesディレクトリーに.tplを配置するのが一般的だが、別にControllerのところに配置しても問題ない。
MVCでディレクトリーを分けるより、MVを1箇所にまとめたほうがわかりやすく感じる。ディレクトリーを行き来するのが面倒くさい。


== ORM ==
== ORM ==
487行目: 748行目:
「[https://packagist.org/search/?tags=search Packagist]」の検索結果をみても、tntsearchが特に人気の模様。
「[https://packagist.org/search/?tags=search Packagist]」の検索結果をみても、tntsearchが特に人気の模様。


== PEAR ==
== PEAR+PECL ==


=== About ===
=== About ===
[https://ja.wikipedia.org/wiki/PEAR PEAR - Wikipedia]
[https://ja.wikipedia.org/wiki/PEAR PEAR - Wikipedia]
[https://pear.php.net/manual/en/guide.users.commandline.cli.php Manual :: Command line installer (PEAR)]


PEAR (PHP Extension and Application Repository, ペア)
PEAR (PHP Extension and Application Repository, ペア)
499行目: 762行目:


準標準ライブラリー。名前空間なしで見知らぬクラス名が登場したらPEARの可能性がある。
準標準ライブラリー。名前空間なしで見知らぬクラス名が登場したらPEARの可能性がある。
PEARとPECLはコマンド体系などがかなり似ていて、設定も相互利用している。「[https://www.php.net/manual/ja/install.pecl.pear.php PHP: 共有 PECL 拡張モジュールを、pecl コマンドを用いてコンパイルする - Manual]」でもpeclコマンドへのリンクがpearになっている。


==== Directory ====
==== Directory ====
507行目: 772行目:
特に重要なのが、php_dir (=/usr/share/pear or php)。ここにPEARのPHPファイルやクラス定義がある。シンボル参照などで重要。
特に重要なのが、php_dir (=/usr/share/pear or php)。ここにPEARのPHPファイルやクラス定義がある。シンボル参照などで重要。


=== CLI ===
==== install ====
pear install [options] [channel/]<package>
この書式。パッケージのインストールは4の方法がある。
# ローカルファイル指定
# URLのファイル指定
# package.xml指定
# パッケージ名指定 (Package[-version/state][.tar]: バージョン類を指定しなかったら最新stable。
=== Other ===
==== proxy ====
[https://mzryuka.hatenablog.jp/entry/2019/01/30/223016 PHP環境をDockerで作ろうとしてpeclで「No releases available for package "pecl.php.net/grpc"」エラーになったのはProxyが原因だった - みずりゅの自由帳]
RUN pear config-set http_proxy <nowiki>http://proxy.twave.co.jp:8080</nowiki>
RUN pear channel-update pear.php.net
peclもproxy設定は共通。
== Other ==
=== Other ===
==== バージョン確認 ====
使用しているモジュール、パッケージ類のバージョンを確認したい時がある。対象に応じて、いくつか方法がある。
composerの場合
composer show
PEARライブラリーの場合
pear list
上記で見つからない場合。pearを使わないでインストールしている場合。php -iでinclude_pathを確認して、以下のコマンドでpearのxmlファイルを検索。
find /include_path/ -name "*.xml"
grep -A 2 release /usr/local/lib/php/package.xml
grep -A 2 release /usr/local/lib/php/package2.xml
その他のライブラリーの場合
クラスに::VERSIONなどのプロパティーがあることがあるのでこれ。とか、ソースコード。
== HTTP ==
=== Guzzle ===
[https://docs.guzzlephp.org/en/stable/ Guzzle, PHP HTTP client — Guzzle Documentation]
PHPでデファクトスタンダード的なHTTPクライアントライブラリー。内部でcurlやfile_get_contentsも使っている。とりあえず、これを使っておけば大丈夫な感じがする。
==== Quick start ====
[https://docs.guzzlephp.org/en/stable/quickstart.html Quickstart — Guzzle Documentation]
use GuzzleHttp\Client;
$client = new Client([
    // Base URI is used with relative requests
    'base_uri' => '<nowiki>http://httpbin.org'</nowiki>,
    // You can set any number of default request options.
    'timeout'  => 2.0,
]);
最初にClientインスタンスを作成する。この際に、base_uriを指定する。
$response = $client->get('<nowiki>http://httpbin.org/get'</nowiki>);
$response = $client->delete('<nowiki>http://httpbin.org/delete'</nowiki>);
$response = $client->head('<nowiki>http://httpbin.org/get'</nowiki>);
$response = $client->options('<nowiki>http://httpbin.org/get'</nowiki>);
$response = $client->patch('<nowiki>http://httpbin.org/patch'</nowiki>);
$response = $client->post('<nowiki>http://httpbin.org/post'</nowiki>);
$response = $client->put('<nowiki>http://httpbin.org/put'</nowiki>);
$promise = $client->getAsync('<nowiki>http://httpbin.org/get'</nowiki>);
$promise = $client->deleteAsync('<nowiki>http://httpbin.org/delete'</nowiki>);
$promise = $client->headAsync('<nowiki>http://httpbin.org/get'</nowiki>);
$promise = $client->optionsAsync('<nowiki>http://httpbin.org/get'</nowiki>);
$promise = $client->patchAsync('<nowiki>http://httpbin.org/patch'</nowiki>);
$promise = $client->postAsync('<nowiki>http://httpbin.org/post'</nowiki>);
$promise = $client->putAsync('<nowiki>http://httpbin.org/put'</nowiki>);
その後、Clientインスタンスのメソッド呼び出しでリクエストする。
$responseはPSR-7 responseの実装になっている。
$code = $response->getStatusCode(); // 200
$reason = $response->getReasonPhrase(); // OK
// Check if a header exists.
if ($response->hasHeader('Content-Length')) {
    echo "It exists";
}
// Get a header from the response.
echo $response->getHeader('Content-Length')[0];
// Get all of the response headers.
foreach ($response->getHeaders() as $name => $values) {
    echo $name . ': ' . implode(', ', $values) . "\r\n";
}
$body = $response->getBody();
// Implicitly cast the body to a string and echo it
echo $body;
// Explicitly cast the body to a string
$stringBody = (string) $body;
// Read 10 bytes from the body
$tenBytes = $body->read(10);
// Read the remaining contents of the body as a string
$remainingBytes = $body->getContents();
Query String Parameters
$response = $client->request('GET', '<nowiki>http://httpbin.org?foo=bar'</nowiki>);
$client->request('GET', '<nowiki>http://httpbin.org'</nowiki>, [
    'query' => ['foo' => 'bar']
]);
$client->request('GET', '<nowiki>http://httpbin.org'</nowiki>, ['query' => 'foo=bar']);
Uploading Data
use GuzzleHttp\Psr7;
// Provide the body as a string.
$r = $client->request('POST', '<nowiki>http://httpbin.org/post'</nowiki>, [
    'body' => 'raw data'
]);
// Provide an fopen resource.
$body = Psr7\Utils::tryFopen('/path/to/file', 'r');
$r = $client->request('POST', '<nowiki>http://httpbin.org/post'</nowiki>, ['body' => $body]);
// Use the Utils::streamFor method to create a PSR-7 stream.
$body = Psr7\Utils::streamFor('hello!');
$r = $client->request('POST', '<nowiki>http://httpbin.org/post'</nowiki>, ['body' => $body]);
$response = $client->request('POST', '<nowiki>http://httpbin.org/post'</nowiki>, [
    'form_params' => [
        'field_name' => 'abc',
        'other_field' => '123',
        'nested_field' => [
            'nested' => 'hello'
        ]
    ]
]);
===== Exceptions =====
[https://docs.guzzlephp.org/en/stable/quickstart.html?highlight=exception#exceptions Quickstart — Guzzle Documentation]
Guzzle::requestは200以外が返ってくる場合、Exceptionを投げる。基本的に使うときは常にtry/catchが必要。
use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\ClientException;
try {
    $client->request('GET', '<nowiki>https://github.com/_abc_123_404'</nowiki>);
} catch (ClientException $e) {
    echo Psr7\Message::toString($e->getRequest());
    echo Psr7\Message::toString($e->getResponse());
}
ExceptionはgetResponseで応答を取得できるので、問題ない。
[[Category:PHP]]
[[Category:PHP]]

2025年10月24日 (金) 10:04時点における最新版

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。

高速らしい。

そもそもテンプレートエンジンがいるのかどうかという議論がある。

UI/UXを突き詰めると、JavaScriptを使わざるを得ず、サーバーテンプレートエンジンは初回だけなので、いっそのことJSで全部やろうというのが最近の流れの模様。

PHP自体が一種のテンプレートエンジンという主張がある。が、関数をあれこれ書く必要があり、可読性が悪い。

SmartyよりTwigのほうが性能が上とか。

Volt: テンプレートエンジン — Phalcon 3.0.2 ドキュメント (Japanese / 日本語)」。高速フレームワークのPhalconではVoltを使っている。

Twig

Twig v3のほうが速いらしいが、Smarty v3のほうが速いというデータもある。

Smarty

Pure PHP/HTML views VS template engines views - Stack Overflow」が決定的だった。Smartyの開発者がSmartyのほうがTwigより速いと回答していた。2012年。Smartyでいいと思う。

が、IDEのコード補完の対応状況がSmartyはいまいち。そこが生産性に響く。Laravelは嫌いだからTwigにする。

カスタムタグ

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

Comments
{* comment *}

コメント内に*}を記載できない。工夫が必要。

  1. 分割記述
  2. HTMLコメント: HTML上に残る。
  3. assignを利用した変数埋め込み。
  4. カスタムタグ
Variables

Variables | Smarty

基本はPHPの変数と同じ。ただし、配列要素にアクセスする.記法がある。

{$foo}        <-- displaying a simple variable (non array/object)
 {$foo[4]}     <-- display the 5th element of a zero-indexed array
 {$foo.bar}    <-- display the "bar" key value of an array, similar to PHP $foo['bar']
 {$foo.$bar}   <-- display variable key value of an array, similar to PHP $foo[$bar]
 {$foo->bar}   <-- display the object property "bar"
 {$foo->bar()} <-- display the return value of object method "bar"
 {#foo#}       <-- display the config file variable "foo"
 {$smarty.config.foo} <-- synonym for {#foo#}
 {$foo[bar]}   <-- syntax only valid in a section loop, see {section}
 {assign var=foo value='baa'}{$foo} <--  displays "baa", see {assign}
 
 Many other combinations are allowed
 
 {$foo.bar.baz}
 {$foo.$bar.$baz}
 {$foo[4].baz}
 {$foo[4].$baz}
 {$foo.bar.baz[4]}
 {$foo->bar($baz,2,$bar)} <-- passing parameters
 {"foo"}       <-- static values are allowed
 
 {* display the server variable "SERVER_NAME" ($_SERVER['SERVER_NAME'])*}
 {$smarty.server.SERVER_NAME}
 
 Math and embedding tags:
 
 {$x+$y}                             // will output the sum of x and y.
 {assign var=foo value=$x+$y}        // in attributes 
 {$foo[$x+3]}                        // as array index
 {$foo={counter}+3}                  // tags within tags
 {$foo="this is message {counter}"}  // tags within double quoted strings
 
 Defining Arrays:
 
 {assign var=foo value=[1,2,3]}
 {assign var=foo value=['y'=>'yellow','b'=>'blue']}
 {assign var=foo value=[1,[9,8],3]}   // can be nested
 
 Short variable assignment:
 
 {$foo=$bar+2}
 {$foo = strlen($bar)}               // function in assignment
 {$foo = myfunct( ($x+$y)*3 )}       // as function parameter 
 {$foo.bar=1}                        // assign to specific array element
 {$foo.bar.baz=1}                    
 {$foo[]=1}                          // appending to an array
 
 Smarty "dot" syntax (note: embedded {} are used to address ambiguities):
 
 {$foo.a.b.c}        =>  $foo['a']['b']['c'] 
 {$foo.a.$b.c}       =>  $foo['a'][$b]['c']         // with variable index
 {$foo.a.{$b+4}.c}   =>  $foo['a'][$b+4]['c']       // with expression as index
 {$foo.a.{$b.c}}     =>  $foo['a'][$b['c']]         // with nested index
 
 PHP-like syntax, alternative to "dot" syntax:
 
 {$foo[1]}             // normal access
 {$foo['bar']}
 {$foo['bar'][1]}
 {$foo[$x+$x]}         // index may contain any expression
 {$foo[$bar[1]]}       // nested index
 {$foo[section_name]}  // smarty {section} access, not array access!
 
 Variable variables:
 
 $foo                     // normal variable
 $foo_{$bar}              // variable name containing other variable 
 $foo_{$x+$y}             // variable name containing expressions 
 $foo_{$bar}_buh_{$blar}  // variable name with multiple segments
 {$foo_{$x}}              // will output the variable $foo_1 if $x has a value of 1.
 
 Object chaining:
 
 {$object->method1($x)->method2($y)}
 
 Direct PHP function access:
 
 {time()}
Functions
PHP function

https://grok.com/share/c2hhcmQtMw%3D%3D_67e61609-8350-420f-9bfb-63292690e0a9

Smarty内でPHP標準関数や、メソッドを使う方法がある。

まず、PHP標準関数は使用可能箇所が限定されている。

  1. variable modifier
  2. {php}タグ内
  3. カスタム関数・プラグイン

基本は変数修飾子でだけ使える。

ただし、変数に割り当てたメソッドはどこでも使える。

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}だと使えるのだが。しかたないので、専用の変数を用意する。

5. Variable Modifiers

variable modifiers (変数修飾子) は、変数、カスタム関数、文字列に適用される。値の後に、|と修飾子名を指定する。複数指定可能で、その場合:で区切る。

Smarty v2までは、配列への適用時は@が必要だった。v3から不要になった。

PHP関数は全部指定可能。ただし、注意点がある。

  1. 引数の順序が直観的じゃない。値部分が第1引数で、修飾子の後ろに:で第2引数以後が続く。わかりにくい。
  2. セキュリティーポリシーで使用可能な関数が決まっている。

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側で変数で用意しておくのがよい。

assignではboolの評価式を変数に保存とかはできない。そういうことをしたい場合、captureを使う。

https://chatgpt.com/share/682c4a41-5ec4-800b-9b65-c0d7b3e08613

{capture name="is_special"}{strip}
    {if $a == 2 || $a == 3}1{else}0{/if}
{/strip}{/capture}
$smarty.capture.is_special

stripで改行類の除去を忘れずに。

{capture}

{capture} | Smarty

ブロック内を変数に押し込めるための機能。

{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}

{fetch} | Smarty

指定したファイルを取得してそれをそのまま表示する。

{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}

{if},{elseif},{else} | Smarty

ifで使える演算子に癖がある。

Qualifier Alternates Syntax Example Meaning PHP Equivalent
== eq $a eq $b equals ==
!= ne, neq $a neq $b not equals !=
> gt $a gt $b greater than >
< lt $a lt $b less than <
>= gte, ge $a ge $b greater than or equal >=
<= lte, le $a le $b less than or equal <=
=== $a === 0 check for identity ===
! not not $a negation (unary) !
% mod $a mod $b modulous %
is [not] div by $a is not div by 4 divisible by $a % $b == 0
is [not] even $a is not even [not] an even number (unary) $a % 2 == 0
is [not] even by $a is not even by $b grouping level [not] even ($a / $b) % 2 == 0
is [not] odd $a is not odd [not] an odd number (unary) $a % 2 != 0
is [not] odd by $a is not odd by $b [not] an odd grouping ($a / $b) % 2 != 0

特に注意が必要なのは、!==がないこと。たぶん、! ($a === $b) みたいなことが必要。

smarty v3だとたぶんいけるとおもう。

他に、!==以外の、「 All PHP conditionals and functions are recognized, such as ||, or, &&, and, is_array(), etc.」も使える。xorとかもいけると思われる。

{literal}

https://chatgpt.com/c/6799d1f8-dd54-800b-a5cc-b22c453083fb

{などはSmartyのタグとして解釈される部分では解釈される。CSSやJavaScriptなどで{をそのまま使いたい場合、{literal}タグで解釈を阻止できる。

ただ、{literal}が不要なケースもある。capture/block内。これらがliteral相当の機能がある。

{strip}

{strip} | Smarty

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で追記の記法がいくつかあるので注意が必要。基本は、$smarty.block.childを親側に配置だと思う。

  • prepend
  • append
  • $smarty.block.child
  • $smarty.block.parent

prependとappendはparentを中心に考える。appendは子の後ろに親を追加する。子側でオプション指定が必要になる。

上書きと追記を子で切り替えられる利点はあるが、上書きすることないなら、childを親に配置したほうがわかりやすい。

{extends}

{extends} | Smarty

{include}

{include} | Smarty

外部の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変数を定義前は参照できない。したがって、以下の順序で指令を使う。

  1. capture定義
  2. includeでcapture変数引き渡し

さらに、以下のロジックで末端の定義変数を全体のテンプレートで最後に全部参照できるようにする。

  1. 画面固有.tpl
  2. 共通.tpl
  3. 全体.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に反映されない。

  1. postfilterでbody以下に登場するstyle/cssをheadの直前に配置するなどする。
  2. インラインCSSで対応。

インラインCSSで対応できる分はこれで対応したらいいと思う。インラインCSSでどうにもできないケースになったら、そのときに考える。

最悪、JavaScriptでstyle要素を追記して対応する。JavaScriptに依存するのであまりよくないが、最悪これでなんとでもなる。

Pagination

Smarty自体にはページネーション用の機能はない。自分で作る。

配置場所

https://chatgpt.com/c/6808a726-7964-800b-ac9c-36722d04ef0a

templatesディレクトリーに.tplを配置するのが一般的だが、別にControllerのところに配置しても問題ない。

MVCでディレクトリーを分けるより、MVを1箇所にまとめたほうがわかりやすく感じる。ディレクトリーを行き来するのが面倒くさい。

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. 1. **Cycle ORM**

- **概要**: Cycle ORMは、PHP向けに軽量で高性能なORMとして設計されており、メモリ使用量やクエリ生成において優れた最適化がなされています。また、`DataMapper`パターンを採用しており、柔軟性が高く、複雑なデータ構造にも対応可能です。

- **特徴**:

- メモリ効率が良く、データベースへのアクセスが高速。

- マッピング定義が柔軟で、データベーススキーマの同期などの管理機能も備えている。

- **適合するケース**: 高速性が求められるが、複雑なデータ構造やリレーションも必要な場合。

- **URL**: [Cycle ORM](https://cycle-orm.dev/)

      1. 2. **Doctrine ORM(高度に最適化した場合)**

- **概要**: Doctrineは多機能で、エンタープライズレベルのアプリケーションにも使用されていますが、設定やキャッシュの適切な調整で高速化が可能です。Doctrineは大規模なデータベースや複雑なエンティティ構造を扱うことを得意としており、クエリキャッシュやメタデータキャッシュを活用すると、高速なクエリ処理が実現できます。

- **特徴**:

- キャッシュ(メタデータ、クエリ、結果)を駆使することでパフォーマンスが向上。

- バッチ処理やフェッチモードの調整により、余分なSQLクエリを抑制できる。

- **適合するケース**: 大規模なアプリケーションで、柔軟性や機能性と高速性のバランスが求められる場合。

- **URL**: [Doctrine ORM](https://www.doctrine-project.org/projects/orm.html)

      1. 3. **Eloquent ORM(軽量化する場合)**

- **概要**: Eloquentは、Laravelの標準ORMで、柔軟で書きやすいAPIを提供しています。ただし、Eloquentは`Active Record`パターンを採用しているため、大量のデータや複雑なクエリには向きません。しかし、シンプルなアプリケーションであれば、十分な速度を発揮できます。

- **特徴**:

- データベースアクセスが少ないケースで非常に簡便で高パフォーマンス。

- 適切なスコープやクエリビルダーを利用することで高速なデータアクセスが可能。

- **適合するケース**: シンプルなアプリケーションで、多少の軽量化(不必要なEloquentモデルの削除など)を行い、APIの利便性と速度を両立したい場合。

- **URL**: [Eloquent ORM](https://laravel.com/docs/eloquent)

      1. 4. **直接的なクエリビルダー使用(PDO, Laminas DBなど)**

- **概要**: ORMを使わず、PDOやクエリビルダー(Laminas DBなど)を用いて、データベースと直接やり取りすることも非常に高速な手法です。ORMのオーバーヘッドがないため、特にトランザクション内での多くのデータ操作が求められる場合や、複雑なクエリが必要な場面では、これが最速のアプローチです。

- **特徴**:

- ORMを介さず直接データベースを操作するため、最小限のオーバーヘッドで済む。

- 柔軟で高度なクエリやバッチ操作も容易。

- **適合するケース**: 速度重視の大規模アプリケーションや、複雑なクエリが必要なケース。

これらを総合的に考えると、 **「Cycle ORM」** が最もバランスよく高性能を発揮しやすく、ORMの利便性を保持しながらも高速なデータ操作が可能です。しかし、最高速を求めるのであれば、ORMを使わずにクエリビルダーやPDOを直接活用することが理想的です。

Cycle ORMが安定して速いらしい。これでいくか。

Migrate

PHPで「Doctrine Migrations」を使ってみる

CakePHPに採用されているPhinxのほうが人気なのでPhinxを使ったほうがよいだろう。

ORMとあわせたほうがいいかも。

Test

  • PHPUnit

Search

検索キーワードをフォームから受信後、DBにSQLで検索をかけて取得結果を返すのが基本。

それとは別に、検索用のアプリにリクエストを受け渡しして検索するという方法がある。どちらでもいけるような、ドライバーのライブラリーがある。

検索サービスで有名なのは以下。

 Laravel Scoutでtntsearchを使う方法がある (Laravel Scout + TNTSearchによる小規模プロジェクトへの全文検索機能の追加 #PHP - Qiita/Laravel ScoutとTNTSearchを使用してサイト全文検索を実装してみる – helog)。

Packagist」の検索結果をみても、tntsearchが特に人気の模様。

PEAR+PECL

About

PEAR - Wikipedia

Manual :: Command line installer (PEAR)

PEAR (PHP Extension and Application Repository, ペア)

PHPで利用可能なライブラリーを提供しているサービス。PEARはPHPで書かれたライブラリー。PECLはC言語の拡張ライブラリー (extension)。

PHP 5までPHP本体に同梱されていた。

準標準ライブラリー。名前空間なしで見知らぬクラス名が登場したらPEARの可能性がある。

PEARとPECLはコマンド体系などがかなり似ていて、設定も相互利用している。「PHP: 共有 PECL 拡張モジュールを、pecl コマンドを用いてコンパイルする - Manual」でもpeclコマンドへのリンクがpearになっている。

Directory

Pearのインストール場所 - めも帖

PEAR同梱のpear config-showで各種インストールディレクトリーがわかる。

特に重要なのが、php_dir (=/usr/share/pear or php)。ここにPEARのPHPファイルやクラス定義がある。シンボル参照などで重要。

CLI

install

pear install [options] [channel/]<package>

この書式。パッケージのインストールは4の方法がある。

  1. ローカルファイル指定
  2. URLのファイル指定
  3. package.xml指定
  4. パッケージ名指定 (Package[-version/state][.tar]: バージョン類を指定しなかったら最新stable。

Other

proxy

PHP環境をDockerで作ろうとしてpeclで「No releases available for package "pecl.php.net/grpc"」エラーになったのはProxyが原因だった - みずりゅの自由帳

RUN pear config-set http_proxy http://proxy.twave.co.jp:8080
RUN pear channel-update pear.php.net

peclもproxy設定は共通。

Other

Other

バージョン確認

使用しているモジュール、パッケージ類のバージョンを確認したい時がある。対象に応じて、いくつか方法がある。

composerの場合

composer show

PEARライブラリーの場合

pear list

上記で見つからない場合。pearを使わないでインストールしている場合。php -iでinclude_pathを確認して、以下のコマンドでpearのxmlファイルを検索。

find /include_path/ -name "*.xml"
grep -A 2 release /usr/local/lib/php/package.xml
grep -A 2 release /usr/local/lib/php/package2.xml

その他のライブラリーの場合

クラスに::VERSIONなどのプロパティーがあることがあるのでこれ。とか、ソースコード。

HTTP

Guzzle

Guzzle, PHP HTTP client — Guzzle Documentation

PHPでデファクトスタンダード的なHTTPクライアントライブラリー。内部でcurlやfile_get_contentsも使っている。とりあえず、これを使っておけば大丈夫な感じがする。

Quick start

Quickstart — Guzzle Documentation

use GuzzleHttp\Client;

$client = new Client([
    // Base URI is used with relative requests
    'base_uri' => 'http://httpbin.org',
    // You can set any number of default request options.
    'timeout'  => 2.0,
]);

最初にClientインスタンスを作成する。この際に、base_uriを指定する。

$response = $client->get('http://httpbin.org/get');
$response = $client->delete('http://httpbin.org/delete');
$response = $client->head('http://httpbin.org/get');
$response = $client->options('http://httpbin.org/get');
$response = $client->patch('http://httpbin.org/patch');
$response = $client->post('http://httpbin.org/post');
$response = $client->put('http://httpbin.org/put');
$promise = $client->getAsync('http://httpbin.org/get');
$promise = $client->deleteAsync('http://httpbin.org/delete');
$promise = $client->headAsync('http://httpbin.org/get');
$promise = $client->optionsAsync('http://httpbin.org/get');
$promise = $client->patchAsync('http://httpbin.org/patch');
$promise = $client->postAsync('http://httpbin.org/post');
$promise = $client->putAsync('http://httpbin.org/put');

その後、Clientインスタンスのメソッド呼び出しでリクエストする。

$responseはPSR-7 responseの実装になっている。

$code = $response->getStatusCode(); // 200
$reason = $response->getReasonPhrase(); // OK
// Check if a header exists.
if ($response->hasHeader('Content-Length')) {
    echo "It exists";
}

// Get a header from the response.
echo $response->getHeader('Content-Length')[0];

// Get all of the response headers.
foreach ($response->getHeaders() as $name => $values) {
    echo $name . ': ' . implode(', ', $values) . "\r\n";
}
$body = $response->getBody();
// Implicitly cast the body to a string and echo it
echo $body;
// Explicitly cast the body to a string
$stringBody = (string) $body;
// Read 10 bytes from the body
$tenBytes = $body->read(10);
// Read the remaining contents of the body as a string
$remainingBytes = $body->getContents();

Query String Parameters

$response = $client->request('GET', 'http://httpbin.org?foo=bar');


$client->request('GET', 'http://httpbin.org', [
    'query' => ['foo' => 'bar']
]);

$client->request('GET', 'http://httpbin.org', ['query' => 'foo=bar']);

Uploading Data

use GuzzleHttp\Psr7;

// Provide the body as a string.
$r = $client->request('POST', 'http://httpbin.org/post', [
    'body' => 'raw data'
]);

// Provide an fopen resource.
$body = Psr7\Utils::tryFopen('/path/to/file', 'r');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);

// Use the Utils::streamFor method to create a PSR-7 stream.
$body = Psr7\Utils::streamFor('hello!');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);

$response = $client->request('POST', 'http://httpbin.org/post', [
    'form_params' => [
        'field_name' => 'abc',
        'other_field' => '123',
        'nested_field' => [
            'nested' => 'hello'
        ]
    ]
]);
Exceptions

Quickstart — Guzzle Documentation

Guzzle::requestは200以外が返ってくる場合、Exceptionを投げる。基本的に使うときは常にtry/catchが必要。

use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\ClientException;

try {
    $client->request('GET', 'https://github.com/_abc_123_404');
} catch (ClientException $e) {
    echo Psr7\Message::toString($e->getRequest());
    echo Psr7\Message::toString($e->getResponse());
}

ExceptionはgetResponseで応答を取得できるので、問題ない。