【PHP日記】

自分の知識不足を悟った末、なるべく1日一回、Webに関する知識を書いていくだけのブログ。

PHP雑学⑩ 【33日目】

PHPを学び始めた素人目線での、大事だったり大事じゃなかったりする雑知識メモ、その⑩です。


twigの『constant()』における名前空間

twigで定数を表示する関数である『constant()』関数を使うときは、phpファイル側でuseキーワードを使っていても名前空間を指定する必要があります。

// index.php
use exam\Bootstrap;
// index.html.twig
$link = constant('exam\\Bootstrap::ENTRY_URL');

constant()関数は、指定した定数の値を取得するための関数であり、その定数が名前空間内で定義されている場合は、完全修飾名(名前空間を含む完全な名前)を指定する必要があります。

この挙動は、constant()関数が文字列として渡された定数名を解析するので、その時点ではuse文による名前空間エイリアスが利用できないために起こります。



required属性を付与しても、php側での検証は必要

HTMLのinput要素にrequired属性が付与されている場合、その要素が空(未入力)の状態でフォーム送信を試みると、ブラウザはユーザーに入力を促す警告を表示し、フォームの送信をブロックします。

つまりそもそものフォームが送信されない、と言う挙動をとります。

<input type="radio" name="{{val}}" value="1" required>

ただし、この挙動はクライアントサイド(ブラウザ側)でのバリデーションの一部であり、ユーザーによって回避される可能性があるため、セキュリティ上の理由から、バックエンド側でも入力値の検証を行う必要があります。

if (isset($_POST[$val]) === true) {
    ...
}



value属性』で渡ってくる値はstringである

HTMLフォームから送信されるinputタグのvalue属性は基本的に「文字列」として扱われます。

たとえ入力された値が「数値」のみで構成されていても、送信されたデータは文字列としてPHPに渡されます。

なので、PHP側でこれらの値を数値に変換するには、以下のいずれかの方法を使います。

// intval関数、もしくはfloatval関数
$integerValue = intval($_POST['value']);
$floatValue = floatval($_POST['value']);
// 型キャスト
$integerValue = (int)$_POST['value'];
$floatValue = (float)$_POST['value'];



以上です。


データベースのあいまい検索を理解する 【32日目】

データベース内のアイテムを検索する際に必要な、『あいまい検索』についての小まとめです。


LIKEの挙動

SQLでは「LIKE句」を使うことで、その文字を含んだカラムを取得することができます。

SELECT * FROM users WHERE furigana LIKE '%あ%';    // 部分一致

「%」は0文字以上の任意の文字列を表すワイルドカードです。

「%」をつける位置によって、そのワードをどこに含んでいるかで検索を分けることができます。

SELECT * FROM users WHERE furigana LIKE 'あ%';    // 前方一致
SELECT * FROM users WHERE furigana LIKE '%あ';    // 後方一致

また、「%」自体を文字として検索する場合は、前に「\」をつけてワイルドカードの機能を無効にできます。
この「\」をエスケープ記号と呼ぶので覚えておきましょう。



文字数を指定してデータ検索

任意の1文字を意味するワイルドカード「_」を使うことで、文字数を指定してデータを検索することもできます。

SELECT * FROM users WHERE furigana LIKE '___文字列';    // 3文字の後に「文字列」がある



指定した文字列を含まないデータ検索

指定した文字列を含まない、いわゆるNOT検索を実装することもできます。

SELECT * FROM users WHERE furigana NOT LIKE '%あ%';

NOT LIKEに続けて文字列を指定することで、指定した文字列を含むレコードが全て除外されます



プレースホルダーでワイルドカードを使う時の注意点

PHP側でステートメントオブジェクトを使った処理を行うときは、ワイルドカードの扱いに気をつける必要があります。

たとえば、次のように後から値をバインドするために使用される記号(?)の前後に、直接「%」をつけてもうまく機能しません。

$table = ' product ';
$col = ' product_name, price, image ';
$where = ($word !== '') ? " product_name LIKE '%?%' " : '';
$arrVal = ($word !== '') ? [$word] : [];

$sql = " SELECT " . $col . " FROM " . $table . $where;

$stmt = $this->dbh->prepare($sql);
$res = $stmt->execute($arrVal);

これは、「?」自体をシングルクォートで囲ってしまうと、プレースホルダーがリテラルの一部として扱われ、正しく機能しないためです。

そのため、後から「%」を結合する形でexecuteに渡す必要があります。

$table = ' product ';
$col = ' product_name, price, image ';
$where = ($word !== '') ? " product_name LIKE ? " : '';
$arrVal = ($word !== '') ? ['%'.$word.'%'] : [];

$sql = " SELECT " . $col . " FROM " . $table . $where;

$stmt = $this->dbh->prepare($sql);
$res = $stmt->execute($arrVal);

これでうまく機能します。



以上です。



参考文献

https://tech.pjin.jp/blog/developer/php_advanced_3_3_13


PHP雑学⑨ 【31日目】

PHPを学び始めた素人目線での、大事だったり大事じゃなかったりする雑知識メモ、その⑨です。


($変数名 !== false)を明示するかどうか

『値がfalseでなければ上の文が実行されるif文』は2通りの記述方法があります。

if($loginSuccess) {
    echo 'ログイン成功';
  } else {
    echo 'ログイン失敗';
  }

このコードの書き方は、$loginSuccessがブール値を保持することが明確である場合に適しています。

変数がtrueかfalseのみを保持するなら、こちらの簡潔な形式が好ましいです。


  if($loginSuccess !== false) {
    echo 'ログイン成功';
  } else {
    echo 'ログイン失敗';
  }

一方、こちらの書き方は、$loginSuccessがブール値以外の値を取り得る場合や、falseとの厳密な比較が重要である場合に適しています。

例えば、$loginSuccessがnull0''などのfalsyな値*1を取り得る場合、これらをfalseと区別したいときには、明示的な表記をするのが好ましいです。



『header()』のあとには『exit()』を

header('Location: ' . Bootstrap::ENTRY_URL . 'complete.php');
exit();

exit()は、スクリプトの実行をその時点で終了させるコマンドです。
リダイレクトを行う際は、その後ろにあるコードが不用意に実行されることを防ぐために、記述しておくのが好ましいです。



twigを使うときのパス指定はphpファイル側が基準になる

CSSファイルをtwigにリンクするとき、パス指定はTwigテンプレートの位置ではなく、サーバー上で動作しているPHPファイルの位置(最終的にブラウザに送信されるHTML)が基準になります。

たとえば、次のようなディレクトリ構造があったとします。

- public_html/
  - index.php
  - css/
    - style.css
  - templates/
    - page.twig

「index.php」から「page.twig」を読み込み、page.twig内で「style.css」をリンクする場合、CSSへのパス指定はこうなります。

<link rel="stylesheet" href="css/style.css">

このとき「page.twig」がどのフォルダにあるかは関係ありません。



以上です。


*1:真偽値において"偽"とみなされる値のこと。

SQL雑学① 【30日目】

SQLを学び始めた素人目線での、大事だったり大事じゃなかったりする雑知識メモ、その①です。


全ユーザー名を取得する

SQLコマンドselect user from mysql.user;を入力することで、『mysql』データベース内の『user』テーブルから『user』カラムに含まれるデータを選択することができます。これで、MySQLに登録されている全てのユーザー名の一覧を取得することができます。

select user from mysql.user;

MySQLは、userテーブルにそれぞれのユーザーに関連する情報(ユーザー名、ホスト名、認証情報など)を保持しています。



テーブルに直接データを挿入する

もともと存在するテーブルに、カラム名を指定することなく直接データを挿入することもできます。

INSERT INTO item VALUES ( 1,  'たまねぎ', '青森県産の採れたて玉ねぎです。', 100, 'tamanegi.jpg', 1 );

itemはテーブル名で、それぞれの値をカラム名順で指定しています。



ビューを利用する

ビュー(VIEW)を作成することで、毎回テーブルから同じSQLを書いて取得しなくても、簡単に望み通りのデータが取得できます。
ビューを表示するには、作成後にSELECTコマンドで取得できます。

CREATE VIEW no1 AS SELECT user_id, password FROM users WHERE no = 1;
SELECT * FROM no1;
+---------+----------+
| user_id | password |
+---------+----------+
| exam000 | exam111  |
+---------+----------+



以上です。


テンプレートエンジン『twig』の使い方 【29日目】

使う機会があったので、今回は『Symfonyフレームワークの標準テンプレートである『twig』の使い方を簡単にまとめていきます。


twigの準備


『twig』のインストール

まずパッケージ管理マネージャの『composer』を使用して、『twig』をインストールをしていきます。

ルートディレクトリに「composer.json」ファイルを作成し、以下の内容で保存します。

{
   "require":{
      "twig/twig":"~1.0"
   }
}

次に、ターミナルで次のコマンドを実行し、twigをインストールしていきます。

composer update

「vendor」フォルダと「conposer.lock」ファイルがインストールされていればOKです。


テンプレートフォルダの指定

次に、PHP側でテンプレートファイル(.html.twig)が置かれているフォルダを指定します。

キャッシュフォルダも同時に指定するとパフォーマンスが上がりますが、不具合が起こる可能性があります。

$loader = new \Twig_Loader_Filesystem(テンプレートフォルダの指定);
$twig = new \Twig_Environment($loader, [
  'cache' => キャッシュフォルダの指定
]);



『twig』の読み込み

最後にPHP側からtwigを読み込むためのコードを書けば完了です。

require_once dirname(__FILE__) . './../vendor/autoload.php';



基本的な文法

for文
{% for key, val in qs %}
<tr>
<th>{{ val }}</th>
<td><input type="radio" name="{{ val }}" value="1" required></td>
<td><input type="radio" name="{{ val }}" value="2" required></td>
<td><input type="radio" name="{{ val }}" value="3" required></td>
<td><input type="radio" name="{{ val }}" value="4" required></td>
</tr>
{% endfor %}


if文
{% if err_msg is not empty %}
    <p>{{ err_msg }}</p>
{% endif %}


定数
<link rel="stylesheet" href="{{constant('exam\\Bootstrap::ENTRY_URL')}}css/style.css" type="text/css">


別ファイルの読み込み
{% include 'category_menu.html.twig' %}



以上です。


FETCH_ASSOC で躓いたところ 【28日目】


fetch_assocは、データベースから取得したオブジェクトを連想配列の形で返すコマンドですが、具体的にはデータベースから取得した1行ぶんのレコードを連想配列にして返す、という挙動を取ります。

    $stmt = $this->dbh->prepare($sql);
    $res = $stmt->execute($arrVal);

    $data = [];
    while ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) {
      array_push($data, $result);
    }
    return $data;

『1行のデータを配列にして返す処理』を1まとまりとして、それを繰り返しているため、$result には1行のレコードが連想配列になったものが格納されることになります。


そのため、$dataの中身は連想配列の多次元構造になります。

[
  [
    'id' => '1',
    'username' => 'user1',
    'email' => 'user1@example.com'
  ],
  [
    'id' => '2',
    'username' => 'user2',
    'email' => 'user2@example.com'
  ]
]

これは、レコードが1行しかなかった場合でも同じです。

[
  [
    'id' => '1',
    'username' => 'user1',
    'email' => 'user1@example.com'
  ]
]


そのため、中のデータを取り出したい場合は、インデックスを指定した上で、キーを指定する必要があります。

return $data[0]['username'];



以上です。


PHP雑学⑧ 【27日目】

PHPを学び始めた素人目線での、大事だったり大事じゃなかったりする雑知識メモ、その⑧です。


メモリアドレスを変数に代入する

このようなコードがあった場合、変数aには $c のメモリアドレスが代入されるため、$c の値が変われば、変数aの値を代わります。

$a = 1;
$c = 3;
$a = &$c;
$c++;
echo $a;    // 4



配列のキーは整数型にキャストされる

PHPの配列では、整数キーと文字列キーが同一視されるため、キーが '1' の文字列でも 1 の整数でも、内部的には同じ扱いになります。

<?php
   $fruitNumbers = [
       '2' => 'two',
   ];
   
   $numberFruits = [
       2 => 'two',
   ];

   print_r($fruitNumbers);
   print_r($numberFruits);

   var_dump($fruitNumbers === $numberFruits);
?>
Array
(
    [2] => two
)
Array
(
    [2] => two
)
bool(true)



dirname(--FILE--)の意味

phpdirname()は、指定されたファイルが存在する親ディレクトリのパスを取得する関数です。

require_once dirname(__FILE__) . './../vendor/autoload.php';

引数にマジック定数__FILE__を指定した場合、絶対パス表記の現在のファイル名を表すため、『この関数を呼び出したファイルが存在するディレクトリのパス』を取得することになります。



以上です。



参考文献

https://www.php.net/manual/ja/function.dirname.php