Share
Embed
PHP7 で堅牢なコードを書く - 例外処理、表明プログラミング、契約による設計 / PHP Conference 2016
2016/11/03 @ PHPカンファレンス2016
2016/12/15 @ PHPカンファレンス2016再演イベントにて改訂
2017/06/10 @ PHPカンファレンス福岡2017にて改訂
2017/06/10 @ PHPカンファレンス福岡2017講演録画
https://www.youtube.com/watch?v=54jHDHvcYAo
Takuto WADA PRO
June 10, 2017
Transcript
105 slides
-
ʲ࠶վగ൛ʳ 1)1Ͱݎ࿚ͳίʔυΛॻ͘ ྫ֎ॲཧɺද໌ϓϩάϥϛϯάɺܖʹΑΔઃܭ ాਓ !U@XBEB Nov 03, 2016 @ PHPカンファレンス2016
Dec 15, 2016 @ PHPカンファレンス2016再演 Jun 10, 2017 @ PHPカンファレンス福岡2017 -
class BugRepository { public static function findAll($params) { global $CONF;
$pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } } print_r(BugRepository::findAll([ 'assignedTo' => '12', 'status' => 'OPEN' ])); ॻ੶ʰ42-ΞϯνύλʔϯʱͷͻͲ͍ ίʔυྫΛΞϨϯδͯ͠ॻ͍͍ͯ·͢ ɹͱ͋Δͱ͜Ζʹɺ͜Μͳίʔυ͕͋Γ·ͨ͠ # " % -
$ php example.php Array ( [0] => Bug Object (
[bug_id] => 842 [summary] => 保存処理でクラッシュする [date_reported] => 2016-10-26 ) [1] => Bug Object ( [bug_id] => 5150 [summary] => XMLのサポート [date_reported] => 2016-10-26 ) [2] => Bug Object ( [bug_id] => 6060 [summary] => パフォーマンスの向上 [date_reported] => 2016-10-26 ) ) ɹॲཧ͕ޭ͢Δͱɺ͜Μͳײ͡ɻ -
public static function findAll($params) { global $CONF; $pdo = new
PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } ⏰੍ݶ࣌ؒඵ ɹॲཧࣦഊͷݪҼʹͳΔՄೳੑ͕͋ΔͷͲͷߦ # " % -
public static function findAll($params) { global $CONF; $pdo = new
PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } # " % σʔλϕʔεଓཱࣦ֬ഊ AVTSA AQBTTXEAΩʔ໊͕มߋ͞Εͨ -
public static function findAll($params) { global $CONF; $pdo = new
PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } # " % ςʔϒϧ໊ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ ͜͜Ͱ σʔλϕʔεଓΤϥʔ Fatal error: Call to a member function execute() on a non-object -
public static function findAll($params) { global $CONF; $pdo = new
PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } # " % QBSBNT͕OVMM QBSBNTͷΩʔ໊ͷෆҰக QBSBNTͷ͕จࣈྻʹมෆೳ ͜͜Ͱ σʔλϕʔεଓΤϥʔ -
public static function findAll($params) { global $CONF; $pdo = new
PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } # " % #VHΫϥε͕ະఆٛ ͜͜Ͱ σʔλϕʔεଓΤϥʔ ˞ઃఆʹΑΔ -
public static function findAll($params) { global $CONF; $pdo = new
PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } ɹॲཧࣦഊͷݪҼʹͳΔՄೳੑ͕͋ΔͷͲͷߦ # " % ᶃ ᶄ ᶅ ᶆ ͑ͬɺ͜Μͳʹ͋Δͷ -
ɹࢲݟͰQBSBNTͷΩʔ໊ͷෆҰக public static function findAll($params) { global $CONF; $pdo =
new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } QBSBNTͷΩʔ໊ͷෆҰக # " % -
$ php example.php PHP Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number:
parameter was not defined in example.php Array ( ) ɹΩʔ໊Λؒҧ࣮͑ͯߦ͢ΔͱͲ͏ͳΔ ཪͰͬͦ͜Γܯࠂ͕ग़Δ͚ͩͳͷ ۭྻ͕ฦͬͯ͘Δͷ ਖ਼ৗܥͱݟ͚͕͔ͭͳ͘ͳ͍ -
public static function findAll($params) { global $CONF; $pdo = new
PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } ɹॲཧࣦഊͷݪҼʹͳΔՄೳੑ͕͋ΔͷͲͷߦ # " % ᶃ ᶄ ᶅ ᶆ ݱ࣮ੈքΑ͏ͦ͜ -
w ʮޚతϓϩάϥϛϯάʯͱɺϓϩάϥϛϯάʹ ରͯ͠ޚతʹͳΔ͜ͱɺͭ·Γʮͦ͏ͳΔͣ ͩʯͱܾΊ͚ͭͳ͍͜ͱ w ޚతϓϩάϥϛϯάͷࠜఈʹ͋Δͷɺϧʔνϯ ʹෆਖ਼ͳσʔλ͕͞Εͨͱ͖ʹɺͦΕ͕ଞͷϧʔ νϯͷ͍ͤͰ͋ͬͨͱͯ͠ɺඃΛड͚ͳ͍Α͏ ʹ͢Δ͜ͱ w
͜ͷͨΊɺ w ֎෦ιʔε͔ΒͷσʔλͷΛͯ֬͢ೝ͢Δ w ϧʔνϯͷͯ͢ͷೖྗҾͷΛ֬ೝ͢Δ w ෆਖ਼ͳೖྗΛॲཧ͢Δํ๏Λܾఆ͢Δ ޚతϓϩάϥϛϯά -
public static function findAll($params) { if (is_null($params)) { throw new
InvalidArgumentException('params should not be null'); } if (!is_array($params)) { throw new InvalidArgumentException('params should be an array'); } if (count($params) !== 2) { throw new InvalidArgumentException('params should be have exact two items'); } if (!array_key_exists('assignedTo', $params) || !array_key_exists('status', $params)) { throw new InvalidArgumentException('params should have key `assignedTo` and `status` only'); } if (!is_int($params['assignedTo'])) { throw new InvalidArgumentException('params[`assignedTo`] should be an integer'); } if (!is_string($params['status'])) { throw new InvalidArgumentException('params[`status`] should be a string'); } if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) { throw new InvalidArgumentException('params[`status`] should be in `OPEN`,`NEW`,`FIXED`'); } global $CONF; $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); ɹͨͩͻͨ͢ΒೖྗΛνΣοΫ͠Α͏ͱͨ͠Γ # " % -
global $CONF; $dsn = $CONF['dsn'] ?? $CONF['ds'] ?? $CONF['dataSource']; $user
= $CONF['usr'] ?? $CONF['user']; $password = $CONF['passwd'] ?? $CONF['password']; $pdo = new PDO($dsn, $user, $password, [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $safeParams = [ 'assignedTo' => $params['assignedTo'] ?? $params['assigned_to'], 'status' => $params['status'] ?? 'OPEN', ]; $stmt->execute($safeParams); $className = class_exists('Bug') ? 'Bug' : 'BugModel'; return $stmt->fetchAll(PDO::FETCH_CLASS, $className); ɹෆਖ਼ͳೖྗ͕͋ͬͯࣗͰͳΜͱ͔͠Α͏ͱͨ͠Γ # " % -
/** * 担当者, ステータスに合致する Bug を検索し、ヒットした全件を Bug オブジェクト の配列として返す。 *
* @param array $params 格納した検索条件の連想配列。キー `assignedTo` にユーザID をintで, キー `status` にステータス文字列をstringで指定すること。キー、値それぞ れNULLは不可とする。 * @return Bug[] 検索結果を Bug オブジェクトにマッピングして返す。検索結果が0件 のときは空配列を返す。 */ public static function findAll($params) { global $CONF; $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); ɹυΩϡϝϯτͰؒҧ͍͢͞Λิ͓͏ͱͨ͠Γ # " % -
w ʮޚతϓϩάϥϛϯάʯͱɺൃੜΛ ࣄલʹ͝͏ͱ͍͏ίʔσΟϯάελΠϧ w Մಡੑͷߴ͍ίʔυͱదͳ໋໊نଇ w શͯͷؔͷΓΛνΣοΫ w σβΠϯύλʔϯͷ࠾༻ w
ཁ͢Δʹɺྑࣝ͋Δ࣮ફͷੵΈॏͶͰ͋Δ w ޚతϓϩάϥϛϯάɺਖ਼͍͠ίʔυ࡞ ͷͨΊͷنΛϓϩάϥϚ͕Ұ؏ͯ͠ద༻͢ ΔͨΊͷҰछͷίʔσΟϯάඪ४ ޚతϓϩάϥϛϯά -
public static function findAll(int $assignedTo, string $status) { if (is_null($params))
{ throw new InvalidArgumentException('params should not be null'); } if (!is_array($params)) { throw new InvalidArgumentException('params should be an array'); } if (count($params) !== 2) { throw new InvalidArgumentException('params should be have exact two items'); } if (!array_key_exists('assignedTo', $params) || !array_key_exists('status', $params)) { throw new InvalidArgumentException('params should have key `assignedTo` and `status` only'); } if (!is_int($params['assignedTo'])) { throw new InvalidArgumentException('params[`assignedTo`] should be an integer'); } if (!is_string($params['status'])) { throw new InvalidArgumentException('params[`status`] should be a string'); } if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) { throw new InvalidArgumentException('params[`status`] should be in `OPEN`,`NEW`,`FIXED`'); } ɹܕએݴʹΑͬͯʮग़དྷ͍͍ͯ͜ͱ͚ͩΛग़དྷΔʯΑ͏ʹ 1 ) 1 -
public static function findAll(int $assignedTo, string $status) { if (!in_array($status,
['OPEN', 'NEW', 'FIXED'], true)) { throw new InvalidArgumentException('params[`status`] should be in `OPEN`,`NEW`,`FIXED`'); } global $CONF; $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT); $stmt->bindValue(':status', $status, PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } ɹఆ͠ͳ͚ΕͳΒͳ͍ঢ়گ͕ݮͬͨ 1 ) 1 注: さらに堅くしたい場合は 呼び出し側ファイルで厳密な型チェックを有効にする declare(strict_types=1); -
abstract class Enum { private $scalar; public function __construct($value) {
$ref = new ReflectionObject($this); $constants = $ref->getConstants(); if (!in_array($value, $constants, true)) { throw new InvalidArgumentException("value [{$value}] is not defined"); } $this->scalar = $value; } final public function value() { return $this->scalar; } final public function __toString() { return (string)$this->scalar; } } ɹ)JSBLV͞Μͷ&OVN࣮Λ গ͠ΞϨϯδͯ͠ ͬͯΈΔ -
final class Status extends Enum { const OPEN = 'OPEN';
const NEW = 'NEW'; const FIXED = 'FIXED'; } $status = new Status(Status::OPEN); $status = new Status('OPEN'); // "InvalidArgumentException: value [HOGE] is not defined" $status = new Status('HOGE'); ɹ͋Β͔͡Ίఆٛ͞Ε͚ͨͩΛΠϯελϯεԽͰ͖Δ -
public static function findAll(int $assignedTo, Status $status) { if (!in_array($status,
['OPEN', 'NEW', 'FIXED'], true)) { throw new InvalidArgumentException('params[`status`] should be in `OPEN`,`NEW`,`FIXED`'); } global $CONF; $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT); $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } ɹఆ͠ͳ͚ΕͳΒͳ͍ঢ়گ͕͞Βʹݮͬͨ -
class BugRepository { private $pdo; public function __construct(PDO $pdo) {
$this->pdo = $pdo; } // 以下省略 } // 設定者 (DI コンテナ等) $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false, ]); $repo = new BugRepository($pdo); // 利用者 print_r($repo->findAll(12, new Status(Status::OPEN))); ɹ1%0ੜͱઃఆͷΛ֎෦ʹग़͠ɺίϯετϥΫλͰड͚औΔ -
public static function findAll($params) { if (is_null($params)) { throw new
InvalidArgumentException('params should not be null'); } if (!is_array($params)) { throw new InvalidArgumentException('params should be an array'); } if (count($params) !== 2) { throw new InvalidArgumentException('params should be have exact two items'); } if (!array_key_exists('assignedTo', $params) || !array_key_exists('status', $params)) { throw new InvalidArgumentException('params should have key `assignedTo` and `status` only'); } if (!is_int($params['assignedTo'])) { throw new InvalidArgumentException('params[`assignedTo`] should be an integer'); } if (!is_string($params['status'])) { throw new InvalidArgumentException('params[`status`] should be a string'); } if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) { throw new InvalidArgumentException('params[`status`] should be in `OPEN`,`NEW`,`FIXED`'); } global $CONF; if (!isset($CONF['dsn'])) { throw new LogicException('config key `dsn` not found'); } if (!isset($CONF['usr'])) { throw new LogicException('config key `usr` not found'); } if (!isset($CONF['passwd'])) { throw new LogicException('config key `passwd` not found'); } $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); if (!class_exists('Bug')) { throw new LogicException('class `Bug` does not exist'); } return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function findAll(int $assignedTo, Status $status) { $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT); $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } ୈ෦·ͱΊ༧ʹউΔޚͳ͠ -
w ࠷దͳΤϥʔॲཧΤϥʔ͕ൃੜͨ͠ιϑτΣΞ ͷछྨʹΑΓҟͳΔ w ਖ਼ੑͱɺෆਖ਼֬ͳ݁ՌΛܾͯ͠ฦ͞ͳ͍͜ͱΛ ҙຯ͢Δɻෆਖ਼֬ͳ݁ՌΛฦ͘͢Β͍ͳΒɺԿฦ ͞ͳ͍ํ͕·͠Ͱ͋Δ w ݎ࿚ੑͱɺιϑτΣΞͷ࣮ߦΛܧଓͰ͖ΔΑ͏ ʹखΛਚ͘͢͜ͱͰ͋ΔɻͦΕʹΑͬͯෆਖ਼͕֬݁
Ռ͕ͨΒ͞ΕΔ͜ͱ͕͔͋ͬͯ·Θͳ͍ w ҆શੑ ਖ਼֬ੑ Λॏࢹ͢ΔΞϓϦέʔγϣϯͰ ɺݎ࿚ੑΑΓਖ਼ੑ͕༏ઌ͞ΕΔʹ͋Δ w ίϯγϡʔϚΞϓϦέʔγϣϯͰɺਖ਼ੑΑΓ ݎ࿚ੑ͕༏ઌ͞ΕΔʹ͋Δ ਖ਼ੑͱݎ࿚ੑ -
public function findAll(int $assignedTo, Status $status) { $sql = 'SELECT
bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; if (($stmt = $this->pdo->prepare($sql)) !== false) { if ($stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT) !== false) { if ($stmt->bindValue(':status', $status->value(), PDO::PARAM_STR) !== false) { if ($stmt->execute() !== false) { if (($bugs = $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class)) !== false) { return $bugs; } } } } } return false; } ɹ1%0Τϥʔ࣌ͷΓ GBMTF ΛνΣοΫ͢Δ # " % ಈݓ NJYFEฦ͠ -
public function findAll(int $assignedTo, Status $status) { $sql = 'SELECT
bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; if (($stmt = $this->pdo->prepare($sql)) === false) { $error = $this->pdo->errorInfo(); report_error('prepare: ' . $error[2]); return false; } if ($stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT) === false) { $error = $stmt->errorInfo(); report_error('bindValue: ' . $error[2]); return false; } if ($stmt->bindValue(':status', $status->value(), PDO::PARAM_STR) === false) { $error = $stmt->errorInfo(); report_error('bindValue: ' . $error[2]); return false; } if ($stmt->execute() === false) { $error = $stmt->errorInfo(); report_error('execute: ' . $error[2]); return false; } if (($bugs = $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class)) === false) { $error = $stmt->errorInfo(); report_error('fetchAll: ' . $error[2]); return false; } return $bugs; } ɹΓ GBMTF ΛνΣοΫͯ͠ૣظϦλʔϯ # " % SFQPSU@FSSPSϩάʹग़ࣗ͢࡞ؔ NJYFEฦ͠ -
public function findAll(int $assignedTo, Status $status) { $sql = 'SELECT
bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT); $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } // 設定者 $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ]); $repo = new BugRepository($pdo); ɹGBMTFͷΘΓʹ1%0&YDFQUJPO͕ൃੜ͢ΔΑ͏ʹͳΔ $ php example.php PHP Fatal error: Uncaught PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'date_reported' in 'field list' in /path/ to/example.php:63 -
public function findAll(int $assignedTo, Status $status) { assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION);
$sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $this->pdo->prepare($sql); ɹൃੜͨ͠҉ͷલఏΛBTTFSUʹධՁࣜͰॻ͖ɺ໌ࣔ͢Δ あらたに PDO::ERRMODE_EXCEPTION に 依存するようになったことを assert で明示する -
$ php example.php PHP Warning: assert(): assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION) failed
in /path/to/example.php on line 59 PHP Fatal error: Uncaught Error: Call to a member function bindValue() on boolean in /path/to/example.php:63 ɹͬͦ͘͞ද໌ೖΓͷίʔυΛ࣮ߦͯ͠ΈΔ ධՁ͕ࣜग़ͯΘ͔Γ͍͢ Ͱܯࠂ͕ग़Δ͚ͩͰɺམͪͳ͍ མͪͳ͍ͷͰͦͷઌͰผͷΤϥʔʹͳͬͨ 1 ) 1 -
1)1ͷBTTFSUͷઃఆ 1 ) 1 PHP5 との互換性を保つためデフォルトでは警告になっている。 つまり php.ini で
assert.exception = 1 にすべし IUUQQIQOFUNBOVBMKBGVODUJPOBTTFSUQIQ -
$ php example.php PHP Fatal error: Uncaught AssertionError: assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) ===
PDO::ERRMODE_EXCEPTION) in /path/to/example.php:59 Stack trace: #0 /path/to/example.php(59): assert(false, 'assert($this->p...') #1 /path/to/example.php(75): BugRepository->findAll(12, Object(Status)) #2 {main} thrown in /path/to/example.php on line 59 ɹBTTFSUFYDFQUJPOʹͯ͠࠶࣮ߦ ͖ͪΜͱද໌ҧͰམͪΔ Α͏ʹͳͬͨ 1 ) 1 -
w ද໌ίϛϡχέʔγϣϯͱσόοάͷπʔϧͱ ͯ͠ಇ͘ w ίϛϡχέʔγϣϯͷ؍Ͱɺද໌Λॻ͘͜ͱ ʹΑͬͯɺͦͷίʔυΛॻ͍ͨͱ͖ͷલఏΛίʔ υͷಡΈख͕ཧղ͘͢͠ͳΔ w σόοάͷ؍ͰɺόάΛͦͷݪҼʹ͍ۙͱ͜ ΖͰൃݟ͘͢͠ͳΔ
w ςετίʔυΛॻ͍ͯ͋Εɺσόοάͷࢧԉ ͞΄ͲॏཁͰͳ͍͕ɺͦΕͰίϛϡχέʔ γϣϯͷ؍ʹ͓͚Δද໌ͷՁɺґવͱͯ͠ ༗ޮ ද໌ͷϝϦοτ -
ྫ֎ͱද໌ͷ͍͚ w ຊདྷͷΤϥʔॲཧʹද໌Λ͍ͬͯ ͚·ͤΜɻද໌ى͜Γಘͳ͍͜ͱΛ νΣοΫ͢ΔͨΊͷͷͰ͢ ৽൛ ୡਓϓϩάϥϚʔ w ൃੜ͕༧͞ΕΔঢ়گʹΤϥʔॲཧ
ίʔυΛ༻͠ɺൃੜͯ͠ͳΒͳ͍ ঢ়گʹΞαʔγϣϯΛ༻͢Δ $0%&$0.1-&5&ୈ൛ -
ɹ[FOEBTTFSUJPOT ද໌Φϯ 1 ) 1 public function findAll(int $assignedTo,
Status $status) { assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $this->pdo->prepare($sql); -
public function findAll(int $assignedTo, Status $status) { assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION);
$sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $this->pdo->prepare($sql); ɹ[FOEBTTFSUJPOT ද໌Φϑ 1 ) 1 php.ini に zend.assertions = -1 と設定すれば表明を除去できる -
w ΞαʔγϣϯɺQVCMJDϝιουͷҾνΣοΫ ʹ༻͠ͳ͍Ͱ͍ͩ͘͞ w ҾͷνΣοΫ௨ৗɺϝιουͷ༷ ·ͨن ͷҰ෦ʹͳ͓ͬͯΓɺΞαʔγϣϯͷ༗ޮແޮʹ͔ ͔ΘΒͣɺ͜ͷ༷ʹ४ڌ͢Δඞཁ͕͋Γ·͢ɻ w ΞϓϦέʔγϣϯͷਖ਼͍͠ಈ࡞ʹඞཁͳॲཧΛ࣮ߦ
͢ΔͨΊʹΞαʔγϣϯΛ༻͠ͳ͍Ͱ͍ͩ͘͞ IUUQTEPDTPSBDMFDPNKBWBTFKQEPDTUFDIOPUFTHVJEFTMBOHVBHFBTTFSUIUNM ʮΞαʔγϣϯΛ༻ͨ͠ϓϩάϥϛϯάʯΑΓ ɹඞਢͷҾνΣοΫόϦσʔγϣϯʹΘͳ͍ +BWB -
Throwable ├── Error │ ├── ArithmeticError │ │ └── DivisionByZeroError
│ ├── AssertionError │ ├── ParseError │ └── TypeError └── Exception ├── ErrorException ├── LogicException │ ├── BadFunctionCallException │ │ └── BadMethodCallException │ ├── DomainException │ ├── InvalidArgumentException │ ├── LengthException │ └── OutOfRangeException └── RuntimeException ├── OutOfBoundsException ├── OverflowException ├── PDOException ├── RangeException ├── UnderflowException └── UnexpectedValueException 1 ) 1 3VOUJNF&YDFQUJPOܥ ྫ֎ -PHJD&YDFQUJPOܥόά &SSPSܥόά -
ɹ#VHΫϥε͕ະఆٛͷঢ়گʹͲ͏උ͑Δ $ php example.php PHP Fatal error: Class 'Bug' not
found in /path/to/example.php on line 85 ͑ͳʹ͠ͳ͍ɻ ͜Εྫ֎తঢ়گͰͳ͘ʢจࣈ௨ΓʣόάͰ ͋ΓɺϓϩάϥϛϯάϛεͳͷͰɺΤϥʔϋϯ υϦϯάͯ͠ͳΒͳ͍ɻͳʹͤͣɺ͔ ʹམͱ͢ɻGBJMGBTU͕ॏཁɻ -
/** * 検索処理に使用する PDO インスタンスを渡し、バグリポジトリを初期化する。 * * @param PDO $pdo
PDO インスタンス。 PDO::ATTR_ERRMODE が PDO::ERRMODE_EXCEPTION に 設定されていること * @throws InvalidArgumentException PDO::ATTR_ERRMODE が適切に設定されていない場合 */ public function __construct(PDO $pdo) { if ($pdo->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_EXCEPTION) { throw new InvalidArgumentException('requires PDO::ERRMODE_EXCEPTION'); } $this->pdo = $pdo; } ɹίϯετϥΫλͷࣄલ݅Λදݱ͢Δ -
/** * 指定された担当者 ID およびステータスに合致する Bug を検索し、ヒットした全件を Bug オブジェクトの配列として返す。 *
* @param int $assignedTo 担当者ID * @param Status $status ステータス * @return Bug[] 条件に合致した Bug オブジェクトの配列を返す。検索に合致するものがな い場合は空配列を返す * @throws LogicException カラム名違いや文法エラー等SQLのミスが存在する場合 * @throws PDOException データベースとのやりとりに何らかの障害が発生した場合 */ public function findAll(int $assignedTo, Status $status): array { assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; try { $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT); $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } catch (PDOException $e) { if ($this->isGrammaticalError($e->getCode())) { throw new LogicException($e->getMessage(), $e->errorInfo[1], $e); } throw $e; } } ɹpOE"MMϝιουͷࣄޙ݅Λදݱ͢Δ -
} catch (PDOException $e) { if ($this->isGrammaticalError($e->getCode())) { throw new
LogicException($e->getMessage(), $e->errorInfo[1], $e); } throw $e; } ɹpOE"MMϝιουͷࣄޙ݅Λදݱ͢Δ SQLSTATE の値を調査し、 PDOException の内容が例外で はなくあきらかにバグの場合は、 バグを示す LogicException で包んで投げ直している。 その際に第3引数を忘れずに設定し、スタックトレースを つなぐ -
public static function findAll($params) { if (is_null($params)) { throw new
InvalidArgumentException('params should not be null'); } if (!is_array($params)) { throw new InvalidArgumentException('params should be an array'); } if (count($params) !== 2) { throw new InvalidArgumentException('params should be have exact two items'); } if (!array_key_exists('assignedTo', $params) || !array_key_exists('status', $params)) { throw new InvalidArgumentException('params should have key `assignedTo` and `status` only'); } if (!is_int($params['assignedTo'])) { throw new InvalidArgumentException('params[`assignedTo`] should be an integer'); } if (!is_string($params['status'])) { throw new InvalidArgumentException('params[`status`] should be a string'); } if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) { throw new InvalidArgumentException('params[`status`] should be in `OPEN`,`NEW`,`FIXED`'); } global $CONF; if (!isset($CONF['dsn'])) { throw new LogicException('config key `dsn` not found'); } if (!isset($CONF['usr'])) { throw new LogicException('config key `usr` not found'); } if (!isset($CONF['passwd'])) { throw new LogicException('config key `passwd` not found'); } $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [ PDO::ATTR_EMULATE_PREPARES => false ]); $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $pdo->prepare($sql); $stmt->execute($params); if (!class_exists('Bug')) { throw new LogicException('class `Bug` does not exist'); } return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function findAll(int $assignedTo, Status $status) { $sql = 'SELECT bug_id, summary, date_reported FROM Bugs WHERE assigned_to = :assignedTo AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT); $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class); } ୈ෦·ͱΊ༧ʹউΔޚͳ͠