前回のPHPポケモンではメソッドを外部から実行できるようにコントローラー(インターフェース)を作成しました。なので、今回は引数が必要となるメソッドの実装に挑戦してみましょう。
最後にはデモページを準備しているので、気になった人はぜひ遊んでみてください。
第1回はコチラ
引数の処理
今回はpublicで準備したsetNicknameのメソッド(ニックネームを付ける)と、setExpのメソッド(経験値のセット)を外部から操作できるように作成します。この2つのメソッドは、前回作成したものと違い「引数」を指定しなければなりませんので、新しく引数用のフォームを作成し、メソッドを呼び出す際にセットする処理を追加します。
フォームの作成
まずはフォームの作成からです。setNicknameもsetExpも引数は一つですので、それぞれに1つずつ別入力用のフォームが必要です。それぞれのメソッドに合ったフォームを、switchで判定しながら追加しましょう。
出力用ファイル(/index.php)※$action_listのforeach部分
<?php foreach($action_list as $action => $title): ?>
<li>
<input type="radio" name="action" id="<?=$action?>" value="<?=$action?>" required>
<label for="<?=$action?>"><?=$title?></label>
<ul>
<?php switch($action): case 'setNickname': ?>
<li>
<input type="text" name="params[<?=$action?>]" size="30" placeholder="ニックネームを入力※最大5文字">
</li>
<?php break; ?>
<?php case 'setExp': ?>
<li>
<input type="number" min="1" name="params[<?=$action?>]" size="100" placeholder="経験値を入力">
</li>
<?php break; ?>
<?php endswitch; ?>
</ul>
</li>
<?php endforeach; ?>
ニックネームの入力は文字列のためtext、経験値は数値のためnumberのinputを準備しました。経験値はマイナスの入力を制御したいので、最小値に1を指定しています。
引数用フォームのnameには、params[メソッド名]という値を指定しています。
params[<?=$action?>]
これはどのメソッドの引数なのかを判別するためです。現状のフォーム仕様であれば、全ての値を送信しているため、メソッド名のキーを使ってコントローラー側で該当する値を取得します。
一覧取得用メソッドの追加
前回は選択できるメソッドをindex.phpに$action_listとして配列を用意していましたが、こちらをコントローラークラスのプロパティに移動します。
※管理しやすい方で構いません
コントローラー(/Classes/Controller)
/**
* アクション一覧
* @var array
*/
private $action_list = [
'getStats' => 'ステータス',
'getDetails' => '詳細',
'setNickname' => 'ニックネームを付ける',
'setExp' => '経験値をセット',
'reset' => 'リセット',
];
---省略
/**
* アクション一覧の取得
*
* @return array
*/
public function getActionList()
{
return $this->action_list;
}
あとは、出力用ファイルの$action_listにgetActionList()の結果を格納するだけです。
出力用ファイル(/index.php)
$action_list = $controller->getActionList();
引数の受け渡し
次に引数の受け渡し機能をコントローラーに追加します。まずはコンストラクタから修正しましょう。
コントローラー(/Classes/Controller.php)
/**
* @return void
*/
public function __construct($post, $session)
{
if(empty($post)){
// ポケモンをゲットする前
echo '<p>ポケモンを選んでください</p>';
}elseif(!isset($post['action'])){
// ポケモンをゲットした
$this->checkPokemon($post['pokemon']);
}else{
// アクションを選択した
$this->pokemon = $session['pokemon'] ?? null; # 更新時エラー回避用にnullをセット
// アクションメソッドの実行
$this->action($post['action'], $post['params'][$post['action']] ?? null);
}
}
アクションメソッド(action)に対して、今までは第1引数だけを指定していましたが、第2引数をメソッドの引数用として追加しています。パラメーターは、フォームで指定した通り、選択されたメソッドをキーに持ったパラメーターだけを指定しています。
$post['params'][$post['action']]
get関係のメソッドでは引数は指定されませんので、Null合体演算子を使ってエラーを回避しています。
次にアクションメソッドの処理を確認してみましょう。
コントローラー(/Classes/Controller.php)
/**
* アクション
*
* @param string $action(method_name)
* @param mixed $param
* @return void
*/
private function action($action, $param)
{
if($action === 'reset' || is_null($this->pokemon)){
// 初期化
$this->pokemon = null;
echo '<p>ポケモンを選んでください</p>';
return;
}
if(in_array($action, ['setExp', 'setNickname'], true) && empty($param)){
echo 'セットする値を入力してください';
return;
}
// メソッド実行結果を$resultに格納
$result = $this->pokemon->$action($param);
switch ($action) {
// 詳細
case 'getDetails':
$this->echoKeyToVal($result);
break;
// ステータス
case 'getStats':
$this->echoKeyToVal($result);
break;
// 経験値の取得
case 'setExp':
$this->pokemon = $result;
break;
}
}
まずは引数が指定されていない場合の、簡易エラーメッセージ返却処理部分を見てみましょう。
if(in_array($action, ['setExp', 'setNickname'], true) && empty($param)){
echo 'セットする値を入力してください';
return;
}
現状はsetExpとsetNicknameだけを判別すれば良いので、in_arrayを使って判定しています。このどちらかのメソッドが選択されていて、引数となるパラメーター($param)が空であれば、メッセージを出力してretrunで処理を中断しています。
あとは、メソッドの実行に対して引数を追加するだけです。
// メソッド実行結果を$resultに格納
$result = $this->pokemon->$action($param);
引数が必要ないメソッドに対してはnullをセットして渡しています。これで引数の受け渡しは完了です。
メソッドの判定
メソッドが増えてくると、記述ミスによる存在しないメソッドを呼び出せてしまう可能性が発生します。そのためにも、前回実装出来ていなかった「メソッドの判定」を追加しましょう。
判定するクラスであるポケモン(ピカチュウ、フシギダネ等)には、Traitが読み込まれているため、それらも含めて判定できるis_callableの関数を使用します。
is_callable(PHP.net)
コントローラー(/Classes/Controller.php)
/**
* アクション
*
* @param string $action(method_name)
* @param mixed $param
* @return void
*/
private function action($action, $param)
{
if($action === 'reset' || is_null($this->pokemon)){
// 初期化
$this->pokemon = null;
echo '<p>ポケモンを選んでください</p>';
return;
}
// 呼び出せるメソッドか判別
if(is_callable([$this->pokemon, $action])){
if(in_array($action, ['setExp', 'setNickname'], true) && empty($param)){
echo 'セットする値を入力してください';
return;
}
// メソッド実行結果を$resultに格納
$result = $this->pokemon->$action($param);
switch ($action) {
// 詳細
case 'getDetails':
$this->echoKeyToVal($result);
break;
// ステータス
case 'getStats':
$this->echoKeyToVal($result);
break;
// 経験値の取得
case 'setExp':
$this->pokemon = $result;
break;
}
}else{
echo '<p>このアクションは使用できません</p>';
}
}
今回は対象クラスのメソッドを判定するため、is_callableの引数に配列を渡しています。
is_callable([$this->pokemon, $action])
配列の要素1つ目にクラス(インスタンス)、2つ目にメソッドを指定することで、traitや親を含め呼び出しができるメソッドかどうかを確認してくれています。
それでは、出力結果を見てみましょう。
経験値をセット
ニックネームを付ける
それぞれ引数有りのメソッドが正常に機能していることが確認できました。
※PHPとHTMLを使用した簡易的なバリデーションしか実装していませんので、もう少し厳格に実装したい方は、フォーム用のバリデーションを別で作成するか、JavaScriptなどを使ってフォーム自体に制御をつけるようにしてください
ポケモンの追加
最後に、あらたなポケモンを追加して、デモページを作成します。まずは、初代残りの御三家であるヒトカゲ一家とゼニガメ一家の合計6匹分のクラスを実装しましょう。
ヒトカゲのクラス(Classes/Pokemon/Hitokage.php)
<?php
require_once(__DIR__.'/../Pokemon.php');
require_once(__DIR__.'/Lizardo.php');
// ヒトカゲ
class Hitokage extends Pokemon
{
/**
* 正式名称
* @var string(min:1 max:5)
*/
protected $name = 'ヒトカゲ';
/**
* 初期レベル
* @var array
*/
protected $default_level = [
5
];
/**
* 進化レベル
* @var integer
*/
protected $evolve_level = 16;
/**
* レベルアップで覚える技
* @var array
*/
protected $level_move = [
[1, 'ひっかく'],
[1, 'なきごえ'],
[9, 'ひのこ'],
[15, 'にらみつける'],
[22, 'いかり'],
[30, 'きりさく'],
[38, 'かえんほうしゃ'],
[46, 'ほのうのうず'],
];
/**
* 種族値
* @var array
*/
protected $base_stats = [
'HP' => 39,
'こうげき' => 52,
'ぼうぎょ' => 43,
'とくこう' => 60,
'とくぼう' => 50,
'すばやさ' => 65,
];
/**
* 進化 → リザード
*
* @return Classes\Pokemon\Lizardo
*/
protected function evolve()
{
return new Lizardo($this);
}
}
リザードのクラス(Classes/Pokemon/Lizardo.php)
<?php
require_once(__DIR__.'/../Pokemon.php');
require_once(__DIR__.'/Lizardon.php');
// リザード
class Lizardo extends Pokemon
{
/**
* 正式名称
* @var string(min:1 max:5)
*/
protected $name = 'リザード';
/**
* 進化前(クラス名)
* @var string
*/
protected $child_class = 'Hitokage';
/**
* 初期レベル
* @var array
*/
protected $default_level = [
16
];
/**
* 進化レベル
* @var integer
*/
protected $evolve_level = 36;
/**
* レベルアップで覚える技
* @var array
*/
protected $level_move = [
[1, 'ひっかく'],
[1, 'なきごえ'],
[1, 'ひのこ'],
[9, 'ひのこ'],
[15, 'にらみつける'],
[24, 'いかり'],
[33, 'きりさく'],
[42, 'かえんほうしゃ'],
[56, 'ほのうのうず'],
];
/**
* 種族値
* @var array
*/
protected $base_stats = [
'HP' => 58,
'こうげき' => 64,
'ぼうぎょ' => 58,
'とくこう' => 80,
'とくぼう' => 65,
'すばやさ' => 80,
];
/**
* 進化 → リザード
*
* @return Classes\Pokemon\Lizardon
*/
protected function evolve()
{
return new Lizardon($this);
}
}
リザードンのクラス(Classes/Pokemon/Lizardon.php)
<?php
require_once(__DIR__.'/../Pokemon.php');
// リザードン
class Lizardon extends Pokemon
{
/**
* 正式名称
* @var string(min:1 max:5)
*/
protected $name = 'リザードン';
/**
* 進化前(クラス名)
* @var string
*/
protected $child_class = 'Lizardo';
/**
* 初期レベル
* @var array
*/
protected $default_level = [
36
];
/**
* レベルアップで覚える技
* @var array
*/
protected $level_move = [
[1, 'ひっかく'],
[1, 'にらみつける'],
[1, 'なきごえ'],
[1, 'ひのこ'],
[9, 'ひのこ'],
[15, 'にらみつける'],
[24, 'いかり'],
[36, 'きりさく'],
[46, 'かえんほうしゃ'],
[55, 'ほのうのうず'],
];
/**
* 種族値
* @var array
*/
protected $base_stats = [
'HP' => 78,
'こうげき' => 84,
'ぼうぎょ' => 78,
'とくこう' => 109,
'とくぼう' => 85,
'すばやさ' => 100,
];
}
ゼニガメのクラス(Classes/Pokemon/Zenigame.php)
<?php
require_once(__DIR__.'/../Pokemon.php');
require_once(__DIR__.'/Kameil.php');
// ゼニガメ
class Zenigame extends Pokemon
{
/**
* 正式名称
* @var string(min:1 max:5)
*/
protected $name = 'ゼニガメ';
/**
* 初期レベル
* @var array
*/
protected $default_level = [
5
];
/**
* 進化レベル
* @var integer
*/
protected $evolve_level = 16;
/**
* レベルアップで覚える技
* @var array
*/
protected $level_move = [
[1, 'たいあたり'],
[1, 'しっぽをふる'],
[8, 'あわ'],
[15, 'みずてっぽう'],
[22, 'かみつく'],
[28, 'からにこもる'],
[35, 'ロケットずつき'],
[42, 'ハイドロポンプ'],
];
/**
* 種族値
* @var array
*/
protected $base_stats = [
'HP' => 44,
'こうげき' => 48,
'ぼうぎょ' => 65,
'とくこう' => 50,
'とくぼう' => 64,
'すばやさ' => 43,
];
/**
* 進化 → カメール
*
* @return Classes\Pokemon\Kameil
*/
protected function evolve()
{
return new Kameil($this);
}
}
カメールのクラス(Classes/Pokemon/Kameil.php)
<?php
require_once(__DIR__.'/../Pokemon.php');
require_once(__DIR__.'/Kamex.php');
// カメール
class Kameil extends Pokemon
{
/**
* 正式名称
* @var string(min:1 max:5)
*/
protected $name = 'カメール';
/**
* 進化前(クラス名)
* @var string
*/
protected $child_class = 'Zenigame';
/**
* 初期レベル
* @var array
*/
protected $default_level = [
16
];
/**
* 進化レベル
* @var integer
*/
protected $evolve_level = 36;
/**
* レベルアップで覚える技
* @var array
*/
protected $level_move = [
[1, 'たいあたり'],
[1, 'しっぽをふる'],
[1, 'あわ'],
[8, 'あわ'],
[15, 'みずてっぽう'],
[24, 'かみつく'],
[31, 'からにこもる'],
[39, 'ロケットずつき'],
[47, 'ハイドロポンプ'],
];
/**
* 種族値
* @var array
*/
protected $base_stats = [
'HP' => 59,
'こうげき' => 63,
'ぼうぎょ' => 80,
'とくこう' => 65,
'とくぼう' => 80,
'すばやさ' => 58,
];
/**
* 進化 → カメックス
*
* @return Classes\Pokemon\Kamex
*/
protected function evolve()
{
return new Kamex($this);
}
}
カメックスのクラス(Classes/Pokemon/Kamex.php)
<?php
require_once(__DIR__.'/../Pokemon.php');
// カメックス
class Kamex extends Pokemon
{
/**
* 正式名称
* @var string(min:1 max:5)
*/
protected $name = 'カメックス';
/**
* 進化前(クラス名)
* @var string
*/
protected $child_class = 'Kameil';
/**
* 初期レベル
* @var array
*/
protected $default_level = [
36
];
/**
* レベルアップで覚える技
* @var array
*/
protected $level_move = [
[1, 'たいあたり'],
[1, 'しっぽをふる'],
[1, 'みずてっぽう'],
[1, 'あわ'],
[8, 'あわ'],
[15, 'みずてっぽう'],
[24, 'かみつく'],
[31, 'からにこもる'],
[42, 'ロケットずつき'],
[52, 'ハイドロポンプ'],
];
/**
* 種族値
* @var array
*/
protected $base_stats = [
'HP' => 79,
'こうげき' => 83,
'ぼうぎょ' => 100,
'とくこう' => 85,
'とくぼう' => 105,
'すばやさ' => 78,
];
}
一覧取得用メソッドの追加
ポケモンが増えたので、ポケモンの選択肢も増やします。こちらも$action_listと同様にコントローラー内にプロパティとメソッドを準備しましょう。
コントローラー(/Classes/Controller.php)
/**
* ポケモン一覧
* @var array
*/
private $pokemon_list = [
'Pikachu' => 'ピカチュウ',
'Fushigidane' => 'フシギダネ',
'Hitokage' => 'ヒトカゲ',
'Zenigame' => 'ゼニガメ',
];
--省略
/**
* ポケモン一覧の取得
*
* @return array
*/
public function getPokemonList()
{
return $this->pokemon_list;
}
HTML構造化
更に出力ファイルをHTML構造で作成します。とは言っても、htmlやhead、bodyといったタグを追加するだけです。
デモページ用に合わせて実装していきます。
出力用ファイル(/index.php)
<?php
require_once(__DIR__.'/Classes/Controller.php');
session_start();
$controller = new Controller($_POST, $_SESSION);
$action_list = $controller->getActionList();
$pokemon_list = $controller->getPokemonList();
?>
<!DOCTYPE html>
<html lang="jp" dir="ltr">
<head>
<meta charset="utf-8">
<title>PHPポケモン</title>
</head>
<body>
<hr>
<h2>メソッドの実行</h2>
<form action="/" method="post">
<?php if(is_object($controller->pokemon ?? null)): ?>
<?php $_SESSION['pokemon'] = $controller->pokemon; # ポケモンのインスタンスをセッションに格納 ?>
<ol>
<?php foreach($action_list as $action => $title): ?>
<li>
<input type="radio" name="action" id="<?=$action?>" value="<?=$action?>" required>
<label for="<?=$action?>"><?=$title?></label>
<ul>
<?php switch($action): case 'setNickname': ?>
<li>
<input type="text" name="params[<?=$action?>]" size="30" placeholder="ニックネームを入力※最大5文字">
</li>
<?php break; ?>
<?php case 'setExp': ?>
<li>
<input type="number" min="1" name="params[<?=$action?>]" size="100" placeholder="経験値を入力">
</li>
<?php break; ?>
<?php endswitch; ?>
</ul>
</li>
<?php endforeach; ?>
</ol>
<?php else: ?>
<?php
// ポストとセッションをリセット
$_POST = [];
$_SESSION = [];
?>
<select name="pokemon" required>
<option>--選択してください--</option>
<?php foreach($pokemon_list as $class => $pokemon): ?>
<option value="<?=$class?>"><?=$pokemon?></option>
<?php endforeach; ?>
</select>
<?php endif; ?>
<input type="submit" value="実行">
</form>
<?php if(isset($controller->pokemon)): ?>
<hr>
<p>現在所有しているポケモン:<?=$controller->pokemon->getNickname()?>(<?=$controller->pokemon->getName()?>)</p>
<?php endif; ?>
</body>
</html>
デモページ
第11回終了時点でのデモページです。一部エラー回避や記述漏れなどの修正を加えていますが、同じように作成していけば以下のような簡易アプリケーションが完成します。
まとめ
いかがだったでしょうか。
今回のPHPポケモンは「引数メソッド実行編」をご紹介しました。
現在プログラミング学習に取り組んでいる方は、ぜひ参考にしてくださいね。