ねっとぱんだ-プログラミング勉強ブログ-

Webデザイン、プログラミングの勉強ブログ。

【PHP】簡易掲示板


某学習サイトのチュートリアルで簡易掲示板を作りました。

全体像

  • index.php
  • bbs.dat

index.phpから入力した値をbbs.datに保存。
bbs.datのデータをあるだけindex.phpに表示する。

制作時間
3時間

反省、改善点

  • セキュリティの面を調べて付け足していく。
  • データベースとのやりとりで作れるようにする。

入力画面

<?php
//エラーを表示
ini_set( 'display_errors', 1 );
error_reporting( E_ALL );

$dataFile = 'bbs.dt';

//エスケープする関数
function h($s){
return htmlspecialchars($s,ENT_QUOTES,'UTF-8');
}

$dataFile = 'bbs.dat';

//REQUEST_METHODがPOSTの時。(postされたとき)
//messageとuserがセットされていたら
if($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['message'])&&isset($_POST['user'])){

    $message = trim($_POST['message']);
    $user = trim($_POST['user']);
    
//messageが空じゃなかったら
    if($message !== ''){
        //userが空だったらnonameと設定する三項演算子
        $user =($user === '') ? 'noname' : $user;
        //mesageとuserにtabと改行をつける
        $message = str_replace("\t",'',$message);
        $user = str_replace("\t",'',$user);
        //日付も一緒に書き込む
        $postedAt = date('Y-m-d H:i:s');
        //メッセージをタブで区切って改行する
        $newData  = $message."\t".$user."\t".$postedAt."\n";
        //ファイルを開く
        $fp = fopen($dataFile,'a');
        //このファイルに書き込む
        fwrite($fp,$newData);
        //ファイルを閉じる
        fclose($fp);
    }
}
//一行ずつデータを取り出して配列に入れる
$posts = file($dataFile,FILE_IGNORE_NEW_LINES);
$post = array_reverse($posts);
?>
<!DOCTYPE html>
<html>
<head lang="ja">
<meta charset="utf-8">
<title>簡易掲示板</title>
</head>
<body>
<h1>簡易掲示板</h1>
<!--datファイルを指定するのでactionは空-->
<form action="" method="POST">
mesage:<input type="text" name="message">
user:<input type="text" name="user">
<input type="submit" value="投稿">

</form>
<h2>投稿一覧(0件)</h2>
<ul>
<!--postsがあるかどうか-->
<?php if (count($posts)): ?>
    <!--postの中身をひとつづつ抽出していく-->
    <?php foreach ($posts as $post):?>
    <!--
    php7ではlistは左のパラメータから値を入れていく
    php5ではlistは右のパラメータから値を入れていく
    http://php.net/manual/ja/function.list.php
    -->
    <?php list($message, $user, $postedAt) = explode("\t",$post); ?>
    <?php ?>
    <li><?php echo h($message); ?><?php echo h($user); ?><?php echo h($postedAt); ?></li>
    <?php endforeach; ?>    
<?php else : ?>
    <li>まだ投稿はありません。</li>
<?php endif ; ?>
</ul>
</body>
</html>

【Unity、C#】玉転がしゲーム チュートリアル


unityの日本語チュートリアルで玉転がしゲームを作成しました。
Home · unity3d-jp/FirstTutorial Wiki · GitHub
f:id:p_and_a_fam:20170618123115p:plain

制作時間
2時間

目標

改善点、今後

  • APIを調べて独自の動きやゲーム性を追加していく。
  • マテリアル等、見た目を変えてみる。

コード

Player(玉)の動き

using System.Collections;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
	//speed変数を定義
	public float speed = 10;

	void FixedUpdate ()
	{
		//xとzにキーボードの入力を代入
		float x = Input.GetAxis("Horizontal");
		float z = Input.GetAxis("Vertical");
		//Rigibodyコンポーネントを取得
		Rigidbody rigidbody = GetComponent<Rigidbody>();
		//AddForceというAPIでrigidbodyのxとyに力を加える
		rigidbody.AddForce(x,0,z);
		//キーを押した時の値を大きくして早く動くようにする
		//速さの値を変数化してinspector画面から調整できるようにする
		rigidbody.AddForce(x * speed, 0, z * speed);
	}
}

Playerについていくカメラの動き

using System.Collections;
using UnityEngine;

public class FllowPlayer : MonoBehaviour
{

	//Transformのコンポーネントを持つオブジェクトを参照
	public Transform target;
	//相対距離を取得
	private Vector3 offset;
	void Start ()
	{
		offset = GetComponent<Transform> ().position - target.position;
	}

	void Update ()
	{
		//targetの座標に上で取得した相対距離をプラス
		GetComponent<Transform>().position = target.position + offset;
	}
		
}

触れたら消えるアイテム

using System.Collections;
using UnityEngine;

public class Item : MonoBehaviour {
	//接触した時に呼ばれる関数
	void OnTriggerEnter (Collider hit)
	{
		//もし接触したのがplayerタグだったら
		if (hit.CompareTag ("Player")) {
			//ゲームオブジェクトを排除
			Destroy (gameObject);
		}
	}

}

アイテム数のカウントとゲーム終了の表示

using System.Collections;
using UnityEngine;

public class GameController : MonoBehaviour
{
	//textを取得
	public UnityEngine.UI.Text scoreLabel;
	//WinnerLabelを取得
	public GameObject winnerLabelObject;
	public void Update ()
	{	//Itemタグの全部の数を表示
		int count = GameObject.FindGameObjectsWithTag ("Item").Length;
		//itemの数を代入
		scoreLabel.text = count.ToString ();
		//countが0だったら
		if (count == 0) {
			//winnerlabelobjectを表示する
			winnerLabelObject.SetActive(true);
		}
	}

}

Playerが触れたらリスタートする壁

using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

public class DangerWall : MonoBehaviour {
	//接触した時に呼ばれる関数
	void OnCollisionEnter (Collision hit){
		//もしPlayerと接触したら
		if (hit.gameObject.CompareTag ("Player")) {
			//現在のシーンを取得
			int sceneIndex = SceneManager.GetActiveScene().buildIndex;
			//現在のシーンを再読み込み
			SceneManager.LoadScene(sceneIndex);
		}
	}

}

【php】レコードの削除・更新とTransaction

レコードの更新、排除

レコードの削除

レコードの名前を指定して削除するsql文をprepareで指定
executeでプレースホルダーに消したい名前を指定して実行。

rowCount()
PDOStatement->rowCount() — 直近の SQL ステートメントによって作用した行数を返す。
引用:http://www.weblio.jp/content/PDOStatement-%3ErowCount()
    $stmt = $db->prepare('delete from users where name = :name');
    $stmt->execute([
        ':name'=>'takeuchi',
    ]);
    
    echo 'row deleted' . $stmt->rowCount();

レコードの変更

上のコードのsql文やプレースホルダーに入る値を変更用に変える。

    $stmt = $db->prepare('update users set score = :score where name = :name');
    
    $stmt->execute([
    ':score' => 100,
    ':name'=>'suzuki'
    ]);
    
    echo 'row updated:' . $stmt->rowCount();

transaction

必ず実行させたい一連の命令があるときに使う。データの整合性を保つときに使う。
以下の例ではsuzukiのスコアを10引いてharaに10足す、という一連の流れを常にセットで実行されるようにしている。

try{
    $db = new PDO(dsn,user,password);
    //ここの意味
    $db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
    /*transactionを始める*/
    $db->beginTransaction();
    $db->exec("update users set score = score -10 where name = 'suzuki'");
    $db->exec("update users set score = score +10 where name = 'hara'");
    //あっている時に実行
    $db->commit();
    
}catch(PDOException $e){
    //エラーが出たら処理を元に戻す。
    $db->rollback();
    
    echo $e->getMessage();
    exit;
}

【php】phpのエラー

PHPのエラー

大きく分けて3つ

パースエラー(シンタックスエラー)
文法を間違っている場合。
一行も実行されない。
実行時のエラー
実行が中断されるエラー(Fatal Error致命的なエラー)
問題のある点でエラーになり実行が止まる。
未定義の関数を呼び出そうとしたときなど
警告・注意
実行はするが警告される(Warning,Notice)
そのまま実行される。

エラーの重要度

  1. E_PARSE
  2. E_ERROR
  3. E_WARNING
  4. E_NOTICE
  5. E_DEPRECATED
  6. E_STRICT

エラーの種類

E_DEPRECATED,E_STRICT
コーディング規約に関する問題や将来的に廃止になる機能への警告
古いプロジェクトで発生しがち。その場合はエラーを報告しないようにする。
コーディング規約
多数のプログラマが参加するプロジェクトにおいて、プログラミング品質を均等にするために定める文書
https://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&uact=8&ved=0ahUKEwiFu7D7l73UAhXKy7wKHW5bAdIQFgglMAE&url=http%3A%2F%2Fqiita.com%2Ftadnakam%2Fitems%2F5d1280559eb75b29847c&usg=AFQjCNER9MsJTPZ_7l1AQzkE3SWT991ntg
E_PARSE
シンタックス(構文規則)エラー
E_ERROR
実行時の致命的なエラー
E_WARNING
実行時の警告
E_NOTICE
E_WORNINGよりも重要度の低い警告
E_DEPRECATED
現行では非推奨になったものに対するエラー。警告が出ても実際に廃止になるまでは実行される。
E_STRICT
変更した方がいい記述に対するエラー。

ユーザーエラー

E_USER_ERROR
ユーザー定義の致命的なエラー
E_USER_WARNING
ユーザー定義の重要な警告
E_USER_NOTICE
ユーザー定義の警告
E_USER_DEPRECATED
ユーザー定義の廃止予定の機能の警告

エラーの設定

error_reporting

どのエラーを報告するかの設定

//全てのエラーを報告
error_reporting = E_ALL
//E_NOTICEとE_DEPRECATED以外は全て報告
error_reporting = E_ALL^E_NOTICE^E_DEPRECATED
//E_STRICTを含むすべてのエラーを報告
error_reporting = E_ALL|E_STRICT
|php|<
:公式サイト:http://php.net/manual/ja/function.error-reporting.php
***display_errors
発生したエラーをどう表示するかを制御
On/Off/Stderrを設定
>|php|
display_errors = On
log_errors

エラーをログに出力するかどうか

log_errors = On
error_log

ファイル名を指す

//display_errorsをOffにした状態でもエラーを把握できる logrotate設定なども確認)
error_log = /var/log/php.log

【php】FETCH_CLASS

PDO::FETCH_CLASS

クラスを作る

class User{
//fetchクラスモードではカラムを自動的にクラスのパブリックのプロパティにセットしてくれる。
//privateにしたいときとかはここでセット
public $id;
public $name;
public $score;
public function show(){
    echo "$this->name($this->score)";
}
}

fetchclassを使ってクラス内のメソッドを呼び出す。
データベース内の名前が呼び出される。

//fetchall
$users = $stmt->fetchAll(PDO::FETCH_CLASS,'User');
foreach ($users as $user){
 $user->show();
}

【PHP】全件抽出と条件付き抽出

全件抽出

fetchAll(PDO::FETCH_ASSOC)
結果に含まれるすべての行を配列で取得。キーと値のペアの配列で帰ってくるよう指定。
$stmt  = $db->query('select * from users');
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($users as $user){
 var_dump($user);
}
//レコードの数を表示
echo $stmt->rowCount().'records found.';

ソースでみると
array(3) {
["id"]=>
string(1) "1"
["name"]=>
string(6) "suzuki"
["score"]=>
string(2) "24"
}
連想配列の一覧がレコードの数だけ表示されている。

条件付き抽出

sqlのwhere句で指定する値を?プレースホルダーにして値を指定する。

$stmt  = $db->prepare('select score from users where score > ?');
$stmt->execute([10]);
}

曖昧検索する場合、%はexecutの方で指定する。

$stmt  = $db->prepare('select name from users where name like  ?');
$stmt->execute(['%s%']);

limitを指定する場合
executeが渡す値は文字列になる。limitは文字列だとエラーになるので数値として渡す。

//nameをアルファベットの降順にして上位3位だけ表示
$stmt  = $db->prepare('select name from users order by name desc limit ?');
$stmt->bindValue(1,3,PDO::PARAM_INT);
$stmt->execute();

【PHP】PDOでデータを挿入、bind

PDOでsqlを実行する手段

  1. exec():結果を返さない、安全なsql
  2. query():結果を返す、安全、何回も実行されないsql
  3. prepare():結果を返す、安全対策が必要、複数回実行されるsql
    悪意のあるコードの対策ができる。

prepareを使ってレコードを挿入

prepare
文を実行する準備を行い、文オブジェクトを返す。
返り値としてはPDOStatementオブジェクトを返す。

以下では$stmtという変数に格納している。

execute()
PDOStatementに実行すると、オブジェクトに結果が格納される。

?プレースホルダーでの記述

$stmt  = $db->prepare('insert into users (name,score) values (?,?)');
$stmt->execute(['suzuki',24]);
echo 'inserted:'.$db->lastInsertId();
引用
http://php.net/manual/ja/pdo.prepare.php

名前付きプレースホルダーでの記述

//データを挿入
$stmt  = $db->prepare('insert into users (name,score) values (:name,:score)');
$stmt->execute([':name'=>'takeuchi',':score'=>60]);

bindValue

bindValue
値をパラメータにバインドする。
http://php.net/manual/ja/pdostatement.bindvalue.php
binde バインド
AとBを結びつける。AをBに割り当てる。

http://wa3.i-3-i.info/word12448.html

ループなどで一部のデータだけ変えたい時に使える。

書き方

bindValue(parameter,value,data_type);
parameter
疑問符プレースホルダの時に入れる値は1から始まる。
value
挿入する値
data_type
定義済み定数を(http://php.net/manual/ja/pdo.constants.php)入れる
例)PARAM_STR,PARAM_INT,PARAM_BOOL,PARAM_NULL

例えばnameはそのままでscoreだけを毎回変えていきたい場合

//データを挿入
$stmt  = $db->prepare('insert into users (name,score) values (?,?)');
$name = 'yamada';
$stmt->bindValue(1,$name,PDO::PARAM_STR);
$score = 7;
$stmt->bindValue(2,$score,PDO::PARAM_INT);
$stmt->execute();
//数値だけを変えて新たに挿入
$score = 12;
$stmt->bindValue(2,$score,PDO::PARAM_INT);
$stmt->execute();
//挿入されたデータのidを引っ張てくる
echo 'inserted:'.$db->lastInsertId();

名前付きプレースホルダーの場合

//データを挿入
$stmt  = $db->prepare('insert into users (name,score) values (:name,:sore)');
$name = 'yamada';
$stmt->bindValue(':name',$name,PDO::PARAM_STR);
$score = 7;
$stmt->bindValue(':sore',$score,PDO::PARAM_INT);
$stmt->execute();
//数値だけを変えて新たに挿入
$score = 12;
$stmt->bindValue(':sore',$score,PDO::PARAM_INT);
$stmt->execute();
//挿入されたデータのidを引っ張てくる
echo 'inserted:'.$db->lastInsertId();

bindParam

bindparam
変数への参照をバインド
//データを挿入
$stmt  = $db->prepare('insert into users (name,score) values (?,?)');
$name = 'ma';
$stmt->bindvalue(1,$name,PDO::PARAM_STR);
$stmt->bindParam(2,$score,PDO::PARAM_INT);
$score = 32;
$stmt->execute();
//変数の値を変えてexecuteするだけで挿入できる
$score = 3;
$stmt->execute();

最初,bindParamで書く時、scoreだけ書いたら
「SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens」というエラーがでましたが、nameもバインドするよう記述したら正常に実行できました。バインドした数(number of bound)と例に記した数(number of tokens)が合わないよ、という意味みたいです。