WAVE Log
ヒアドキュメント

$site = 'guitar site WAVE'; $str = <<<EOD ようこそ。<br /> ここは、{$site} です。 EOD; echo $str;

メール送信(mb_send_mail)

mail_form.html

<html> <head> <meta charset="utf-8" /> </head> <body> <form action="./mail.php" method="post"> <p>送り先</p><input type="text" name="to"> <p>件名</p><input type="text" name="title"> <p>メッセージ</p><textarea name="message" cols="60" rows="10"></textarea> <p><input type="submit" name="send" value="送信"></p> </form> </body> </html>

mail.php

<html> <head> <meta charset="utf-8" /> </head> <body> <?php mb_language("Japanese"); mb_internal_encoding("UTF-8"); $to = $_POST['to']; $title = $_POST['title']; $message = $_POST['message']; $headers = "From: from@example.com"; if(mb_send_mail($to, $title, $message, $headers)) { echo "メール送信成功です"; } else { echo "メール送信失敗です"; } ?> </body> </html>

PHP + MySQL

env.php

<?php define('DB_HOST', 'localhost'); define('DB_NAME', 'mydb'); define('DB_USER', 'myname'); define('DB_PASS', 'password');

dbconnect.php

<?php require_once 'env.php'; function connect() { $host = DB_HOST; $db = DB_NAME; $user = DB_USER; $pass = DB_PASS; $dsn = "mysql:host=$host;dbname=$db;charset=utf8mb4"; try { $dbh = new PDO($dsn, $user, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false ]); return $dbh; } catch(PDOException $e) { echo '接続失敗です!' . $e->getMessage(); exit(); } }

※ PDO::ERRMODE_EXCEPTION
エラーが発生した時に、PDOException の例外を投げてくれる。

※ PDO::FETCH_ASSOC
カラム名をキーとする連想配列で返す。

※ PDO::ATTR_EMULATE_PREPARES
データベース側が持つ「プリペアドステートメント」という機能のエミュレーションをPDO側で行うかどうかを設定する。

create.php

<?php require_once 'dbconnect.php'; function blogCreate($blogs) { $sql = 'INSERT INTO table_name (title, content, category, publish_status) VALUES (:title, :content, :category, :publish_status)'; $dbh = connect(); $dbh->beginTransaction(); try { $stmt = $dbh->prepare($sql); $stmt->bindValue(':title',$blogs['title'], PDO::PARAM_STR); $stmt->bindValue(':content',$blogs['content'], PDO::PARAM_STR); $stmt->bindValue(':category',$blogs['category'], PDO::PARAM_INT); $stmt->bindValue(':publish_status',$blogs['publish_status'], PDO::PARAM_INT); $stmt->execute(); $dbh->commit(); echo 'ブログを投稿しました!'; } catch(PDOException $e) { $dbh->rollBack(); exit($e); } }

※ PDO::PARAM_INT
SQL INTEGER データ型を表します。

※ PDO::PARAM_STR
SQL CHAR, VARCHAR, または他の文字列データ型を表します。

※ カラム名と IN 句にプレースホルダは使えない。

※ トランザクション
(一連の処理の整合性を保つための機能。一つでも処理が失敗すれば、その全ての処理を無効とする。)
beginTransaction() → トランザクション開始
commit() → データベースに反映させる
rollBack() → 処理を全て無効とする
注)トランザクションを利用する場合は、テーブル形式は InnoDB を使用する。MyISAM はトランザクション機能がない。

get.php

<?php require_once 'dbconnect.php'; // データを取得する // 引数:なし // 返り値:取得したデータ function getAll() { $dbh = connect(); //①SQLの準備 $sql = 'SELECT * FROM table_name order by id'; //②SQLの実行 $stmt = $dbh->query($sql); //③SQLの結果を受け取る $result = $stmt->fetchall(); return $result; $dbh = null; } // 引数:$id // 返り値:$result function getById($id) { if(empty($id)) { exit('IDが不正です。'); } $dbh = connect(); // SQL準備 $sql = 'SELECT * FROM table_name where id = :id'; $stmt = $dbh->prepare($sql); $stmt->bindValue(':id', (int)$id, PDO::PARAM_INT); // SQL実行 $stmt->execute(); // 結果を取得 $result = $stmt->fetch(); if(!$result) { exit('ブログがありません。'); } return $result; } // レコード数を取得 // 引数:$category // 返り値:$count function countCategory($category) { $dbh = connect(); $sql = 'SELECT COUNT(*) FROM table_name WHERE category = :category'; $stmt = $dbh->prepare($sql); $stmt->bindValue(':category', (int)$category, PDO::PARAM_INT); $stmt->execute(); $count = $stmt->fetchColumn(); return $count; }

※ fetch() と fetchall()
fetch → 1行ずつ取得
fetchall → 全データを配列に変換

※ query メソッドと prepare メソッド
query メソッド → ユーザからの入力を含まない SQL を実行
prepare メソッド → ユーザからの入力を含む SQL を実行

※ SELECT 文の実行結果が存在しない場合
fetch()メソッドはfalseを返します。COUNTの場合は、ゼロを返します。

結果の存在確認だけが目的ならば、EXISTS句を使用する。

<?php function checkCategoryExists($category) { $dbh = connect(); $sql = 'SELECT EXISTS(SELECT 1 FROM table_name WHERE category = :category)'; $stmt = $dbh->prepare($sql); $stmt->bindValue(':category', (int)$category, PDO::PARAM_INT); $stmt->execute(); $exists = $stmt->fetchColumn(); return $exists; }

カテゴリが存在する場合には1を返し、存在しない場合には0を返します。
SELECT 1 FROM table_nameの「1」は、EXISTS句の中で特定の行が存在するかどうかを確認するためだけに使用されるプレースホルダーのようなもので、具体的なデータを取得する必要がないため、「1」などの定数を使うことが一般的。

XSS対策とCSRF対策

functions.php

<?php /** * XSS対策:エスケープ処理 * * @param string $s 対象の文字列 * @return string 処理された文字列 */ function h($s) { return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); } /** * エスケープ処理をして改行の前に br タグを追加する */ function hbr($s) { return nl2br(htmlspecialchars($s, ENT_QUOTES, 'UTF-8')); } /** * CSRF対策 * @param void * @return string $csrf_token */ function setToken() { // トークンを生成 $csrf_token = bin2hex(random_bytes(32)); $_SESSION['csrf_token'] = $csrf_token; return $csrf_token; }

tokeninput.php
フォームからトークンを送信

<input type="hidden" name="csrf_token" value="<?php echo h(setToken()); ?>">

tokencheck.php
トークンの照会と削除(削除することで二重送信対策になる)

<?php session_start(); if ($_SERVER['REQUEST_METHOD'] == 'POST') { if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) { echo "不正なリクエストです。"; exit(); } // トークンの削除 unset($_SESSION['csrf_token']); }

パスワードハッシュ

ハッシュ化

$password = 'testpass'; $hash = password_hash($password, PASSWORD_DEFAULT);

検証

password_verify($password, $hash)

戻り値:パスワードとハッシュがマッチする場合に true、それ以外の場合に false を返す。

phpinfo

<?php phpinfo(); ?>

タイムゾーンを設定する

<?php date_default_timezone_set('Asia/Tokyo'); ?>

エラーの表示/非表示

表示

<?php ini_set( 'display_errors', 1 ); ?>

非表示

<?php ini_set( 'display_errors', 0 ); ?>

マジカル定数(マジック定数)

__FILE__
この定数を呼び出したファイルのフルパス。
インクルードされるファイルの中で使用された場合、インクルードされるファイルの名前が返されます。

<?php echo __FILE__; ?>

__DIR__
この定数が呼び出したファイルのあるディレクトリのフルパス。
インクルードされるファイルの中で使用された場合、インクルードされるファイルの存在するディレクトリを返します。
ルートディレクトリである場合を除き、ディレクトリ名の末尾にスラッシュはつきません。

<?php echo __DIR__; ?>

インクルード

同じ階層の場合の例

include __DIR__ . '/test.php'; require __DIR__ . '/test.php'; include_once __DIR__ . '/test.php'; require_once __DIR__ . '/test.php';

※ エラー発生時、include は後の処理も実行されるが、require は処理が中断され停止する。
※ _onceは一度だけファイルを読み込み、再読み込みはしない。

一つ上の階層の場合

include __DIR__ . '/../test.php';