カテゴリーアーカイブ PHP

著者:杉浦

PHPを例にした正規表現の再帰

 正規表現によって多重括弧のような入れ子構造を表現することは難しいです。正規表現の難題、入れ子構造
 入れ子構造が正しいことを示すにはスタックのデータ構造を表現することが望ましいですが、通常の正規表現ではスタックを用いることはできません。一部の正規表現エンジンにのみ備わっている再帰機能によってスタック構造を表現できます。再帰機能を備えた正規表現を使用可能な環境にはPHP、python、ruby、一部テキストエディタなどがあたります。正規表現のみに頼らないで文字列操作コードを書くことが可能なプログラミング言語では実装されていない場合が少なくないです。次の正規表現はPHPで動作する正規表現です。PHP: 再帰的パターン – Manual

/\((?:[^\(\)]+|(?R))*\)/

 (?R)が再帰の記号です。この時点で再度正規表現全体を呼び出します。これは最大の大きさで成立している括弧を抜き出す目的の正規表現です。動作結果は下図の通りです。
 
 詳しい動作の過程は次の通りです。この過程の説明はPHP Recursive Patterns – Stan Angeloff’s blogの説明の仕方を大きく参考にしています。


/
  \(          # 開き括弧 '(' を見つける.
    (?:       # 新しいグループの開始。これをしないと最初と最後の'('と')'が|に巻き込まれる。
      [^\(\)]+# '('でも')'でもない文字をどん欲に消費する。
      |       # or
      (?R)    # '('か')'を読んだら、また最初から。再帰処理。
    )
    *         # 可能な限り深いのネストを探索する.
  \)          # '('に対応する閉じ括弧 ')' を見つける.
/
  • この記事いいね! (0)
takahashi 著者:takahashi

PHPからSlack/Discordに、Webhookで簡単に通知を送る。

SlackやDiscordを使っていると何かと便利なのがWebhook通知。

専用のチャンネルを用意し、何かイベントが発生したときの通知などに、メールの代わりにこのWebhookで通知を飛ばしてやることで取りこぼしなく通知を確認できます。

このWebhook、既存のサービスやシステム以外で通知を飛ばしたいときに”どうやってやればいいんだろう?”と思ってしまう方もいらっしゃるかと思います。
調べるとcurlとかめんどくさいパラメータアレコレとか指定するものが多く出てくるのでつい面倒な印象を持ってしまいがちですが、いろいろ調べていたところ、PHPで超簡単にできる方法を見つけました!

PHP から Slack の Incoming webHooks を叩く – Qiita

<?php

//処理内容を定義
function send_to_slack($message) {
  $webhook_url = 'Webhook受付URL';
  $options = array(
    'http' => array(
      'method' => 'POST',
      'header' => 'Content-Type: application/json',
      'content' => json_encode($message),
    )
  );
  $response = file_get_contents($webhook_url, false, stream_context_create($options)); //要求を$webhook_urlのURLに投げて結果を受け取る
  return $response === 'ok'; //$responseの値がokならtrueを返す
}

//メッセージの内容を定義
$message = array(
  'username' => '送付元として表示するユーザー名', 
  'text' => 'メッセージ内容', //Slackの場合
  //'content' => 'メッセージ内容', //Discordの場合
);

send_to_slack($message); //処理を実行
?>

これだけで行けちゃうそうです。
要するに、送付先とデータ形式とメソッド(POST・GETなど)さえ合っていれば、どんな方法でも大丈夫なようです。

ちなみにコードをそのままDiscordに転用する場合は、$messageに入れている要素名’text’を’content’に書き換える必要があります。
‘text’のまま実行してしまうと400エラーが返ってきてしまい、Webhookの内容が送信されません。

上記の内容をテキストエディタなどで書き、
hoge.php
のような適当なファイル名で保存してください

あとはコマンドライン上で

php ./hoge.php

のようにしてやればWebhookが実行され、入力した内容がSlack/Discordに届くはずです。

内容さえ書き換えてやればいくらでも応用ができるので、例えばリモートでサーバーを再起動したときの起動通知とか、システムで障害が発生したときのエラー通知なんかにも使うことができます。

メール通知の場合だと、メールサーバーが落ちて送付できなかったり、通知メールが遅延してしまうことがあったりもするので、Webhook経由でも通知が送れると便利ですね。

  • この記事いいね! (0)
著者:杉浦

OnlinePHPFunctionsの紹介

 OnlinePHPFunctions – Test PHP functions onlineはPHPのオンライン実行環境です。要は自分でサーバを立てずともPHPを実行できる簡単な環境です。
 OnlinePHPFunctionsの主な機能はSandboxとPHP Functionsです。Sandboxでは下図の様な簡単なエディタでPHPを記述、実行できます。

 OnlinePHPFunctionsの特に好きな点は様々なverのPHPを容易に動かせる点です。4.4.9~7.2.4まで様々な環境があり、コードが使えるか使えないかを試せます。例えばPHPにおいて配列の定義構文としてarray()の代わりに[]が使えるようになったのはPHP5.4からです。そのため先ほどのコードの設定をRun on PHP version:5.3.0に変更すると次の様になります。

 使えない構文として文法エラーをもらうことなりました。PHP Functionsではこのサンドボックス機能を活かして実際に動くPHPの関数を見ることが出来ます。

  • この記事いいね! (0)
著者:杉浦

phpの型の比較表

 詳しく知りたい方はPHP: PHP 型の比較表 – Manual を読みましょう。
 phpは暗黙の型変換を行う言語です。これは一々明示的に型変換を行う手間を省く便利さを持ちながらも想定外の動作の原因にもなります。

if($a)

という条件文が

$a = 0

の時に偽と判定されるバグり方は定番らしいです。別言語で私もやりました。その様な曖昧な動作ですが仕様は一律で決められています。マニュアルで確認しましょう。
 下の表はどちらもPHP: PHP 型の比較表 – Manual からの引用です。上の表は直感的に様々な対応をしてくれています。数字==”数字”⇒TRUEなんかは特に便利です。下の表は誤解が生じない明確な形です。失敗がまずい時、誤解を招かないコードを書きたい時なにかに良いです。

== による緩やかな比較
TRUE FALSE 1 0 -1 “1” “0” “-1” NULL array() “php” “”
TRUE TRUE FALSE TRUE FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE
FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE TRUE
1 TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
0 FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE TRUE TRUE
-1 TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
“1” TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
“0” FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
“-1” TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
NULL FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE
array() FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE
“php” TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
“” FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE
=== による厳密な比較
TRUE FALSE 1 0 -1 “1” “0” “-1” NULL array() “php” “”
TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
0 FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
-1 FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
“1” FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
“0” FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
“-1” FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
NULL FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
array() FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
“php” FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
“” FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
  • この記事いいね! (0)
村上 著者:村上

【PHP】「Parse error: syntax error」の対処法

学生さんが書いたプログラムを修正する機会があり、その時に遭遇したエラーです。
分かってみればとても初歩的なミスだったのですが、その時は気が付くのに時間がかかってしまいました。

参考にさせていただいたサイトはこちらから。

「Parse error: syntax error」とは? \ PHPプログラミングの教科書 [php1st.com]
https://php1st.com/1263/

 

さて、今回のエラー「Parse error: syntax error」ですが、こちらは構文エラーが原因です。
入力ミスなどで、PHPの解釈(分析・翻訳)がうまくいかなかったときに発生します。

なお、今回のエラーは全角スペースが含まれていることが原因でした。
使用しているエディタが全角スペースと半角スペースが少しわかりにくい表示だったので、気が付くのが遅くなってしまいました…。
半角スペースに直したところ、無事問題なく実行できました。

で、このエラーですが、エラー文の最後に行数が表示されるのですが、この行数は、必ずしもエラーが発生した行の位置を示しているとは限らないとのこと。
私の遭遇した状況では、エラー文で示された行数と、実際ミスをしていた行数が一致していましたが、場合によっては、表示されている行数よりも上でエラーが発生していることもあるようです。
なので、エラー文の行数はあまり信用しないほうがよさそうです。

 

以上、初心者がやらかしがちなPHPのエラーでした。
今回の状況も踏まえまして、特に初心者の皆さんは今使っているエディターの半角スペースや全角スペースをきちんと可視化しておくことをおすすめします。

  • この記事いいね! (0)
asabakento 著者:asabakento

おすすめの書籍

これからプログラミングを始めたい人が最初に考えがちなのが、どういった学習をしていけばいいかということだと思います。

自分は、この「気づけばプロ並みPHP」という書籍を使ってプログラミングの動きを体系的に学びました。

この書籍の良い所は、プログラミングの基礎だけでなく、サーバーやデータベースも業務で使われているシステムに限りなく近いスタンスで触ることができることです。

また、章ごとにコンポーネントを作っていくので、一つ一つ完成させてゴールを目指すことでモチベーションを保つことができるという点では大きいです。

(さすがにどこでエラーになったかはブラウザで自分でデバッグしなければいけませんけどね。)

キャラクターも可愛いのでとっつきやすいと思います。

また、PHPはブラウザだけでも動かすことができるので、これからプログラミングを始めたい人やコードの動きを改めて見直したいなって思う人には向いていると思いました。(swiftとかjavaだと環境設定も手間かかるし対応するOSも変わってきますしね)

自分が今まで読んできた書籍は、厚くはないものの記載されているコードがどういう時に使われるのかどう応用されるのか説明がないまま漠然とコードを書かされているような感じにさせられるものばかりだったので、これならネットで落ちているソースコードを真似てミニアプリでも作ったほうが身になるなって感じで途中でやめていました。

自分もまだ駆け出しの身で、作りたいものがあっても辞書引きしながらでないとうまく作れないので今でもたまに初心にかえってQiita見たり色々読み返したりしています。

一番大事なのは、つくりたいものが明確にあることですが、自分にあった書籍に出会うことも大事なのかなって感じます。

以上です。

  • この記事いいね! (0)
takahashi 著者:takahashi

Cookieの罠。PHP(サーバー側)からCookieを取得・設定すると遅延する!?

今回、盛大にハマったので記事にします。

詳細はいろいろな事情があって公開できないので、それっぽいコードで説明します。
いま、PHPを使ったWebアプリを開発しているのですが、このアプリはいろいろな事情からCookieを多用する設計になっています。

そしてこのCookieは基本的にPHP側(サーバーサイド)で書き込みを行うようにしています。PHPで書いた方が処理が簡単だったからです。

フレームワークとしてFuelPHPを使用しているので、下記の関数でCookieの操作ができます。

//Cookieの設定
Cookie::set(クッキーのキー, クッキーに格納する値, 有効期限, cookieを有効にするWebページのパス, cookieを有効にするドメイン, $secure = セキュアな接続の時のみ cookie を送信するか否か。, http経由での接続でのみ有効にするか否か);

//Cookieの呼び出し
Cookie::get(クッキーのキー, クッキーが取得できなかった時に返す値);

詳しくはこちらを参照してください。
Cookie クラス – FuelPHP 日本語マニュアル

で、例えば下記のようなコードを書いたとします。

$a="hoge"
$result = Cookie::set("testkey",$a); //Cookieの送信に成功すればtrueを返す。
$b = Cookie::get("testkey",""); //書き込んだCookieを取得。

echo $result;
var_dump($a); //①
var_dump($b); //②

としたときに、$resultが”true”になっていれば、①と②の出力結果は同一のものになる、のが期待する動作なのですが、場合によって、②の時点での$bの値が”hoge”ではなく””になってしまうという事態が今回発生しました。

そんな馬鹿なと何度も確認したり、Cookie::setする前に対象のキーを削除したりいろいろ試したのですが、解決せず。

ここでふと、Google Chromeに、自分が保持しているクッキーの一覧が表示できる機能があったことを思い出し、こちら側でも確認してみました。

調査したいページを開いた状態でChromeの開発者ツール内のApplicationタブを開き、左側カラムの

Storage->Cookies->対象のサイトのURL

と辿っていくと、そのURLで保存されているCookieの一覧を見ることができます。

PHPがわの表示といっしょに、こちらの値の変化も確認していたところ、ブラウザ側で見たCookie”testkey”の値にはちゃんと期待した値の”hoge”という文字列が見えていました。

サーバーサイドからブラウザにCookieを渡す場合、実際にはHTTPのレスポンスヘッダに値を入れてブラウザに渡しています。
ブラウザからWebサーバーに対して接続した際に、基本的に応答が返ってくるまでに少し時間がかかります。

よくよく考えれば、サーバーに対しての通信で多少の遅延が発生するのなら、ブラウザへ値を保存する際に遅延が発生しても不思議ではないですよね。

今回はこの遅延が原因で、サーバーサイドで値がうまく取れなかったのではないか、と結論付けました。

…ということで、Cookieの値を保存・取得する際は、多少遅延が発生する可能性があるのでCookieを扱う際は注意が必要かも…というお話でした(;´∀`)

  • この記事いいね! (0)
著者:杉浦

読むのが早いソースコード

 ソースコードは早く理解されるコードであることが要求されます。早く理解されるためには、書かれている文字が何なのか読み取られるという方法があります。同じ動作を関数化することはよく使われる手法です。

function natcmp_name($a, $b) {
	if (empty($a['nickname']) || $a['nickname'] == "") {
		$a['nickname'] = $a['fullname'];
	}
	if (empty($b['nickname']) || $b['nickname'] == "") {
		$b['nickname'] = $b['fullname'];
	}
}

 これは連想配列aと連想配列bを比較する関数です。比較の方法はニックネームの自然な辞書順の比較、もしニックネームがなければ代わりにフルネームを使う、です。この関数はabならば>0を返します。自然な辞書順というのは主に数字に関連した部分です。単純な辞書順では一文字ずつ文字を読み取り、違いが出た時点で順序を決定します。この単純な辞書順において’1abc’という文字列は’10abc’という文字列よりも後に並びます。これは2文字目のaと0の比較で0の方が辞書順で前に来るのが適当であるためです。自然な辞書順において’1abc’という文字列は’10abc’という文字列よりも後に並びます。
 この関数を用いてある3人の名前の並び順を決定する。コードを書くと次の様になります。

function natcmp_name($a, $b) {
	if (empty($a['nickname']) || $a['nickname'] == "") {
		$a['nickname'] = $a['fullname'];
	}
	if (empty($b['nickname']) || $b['nickname'] == "") {
		$b['nickname'] = $b['fullname'];
	}
	strnatcmp($a["nickname"], $b["nickname"]);
}
natcmp_name($person[1],$parson[2]);
natcmp_name($person[2],$parson[3]);
natcmp_name($person[3],$parson[1]);

 3回の比較が12行程度で記述できました。これを関数抜きで記述した場合、次のようになります

if (empty($person[1]['nickname']) || $person[1]['nickname'] == "") {
	$person[1]['nickname'] = $person[1]['fullname'];
}
if (empty($person[2]['nickname']) || $person[2]['nickname'] == "") {
	$person[2]['nickname'] = $person[2]['fullname'];
}
strnatcmp($person[1]["nickname"], $person[2]["nickname"]);
if (empty($person[2]['nickname']) || $person[2]['nickname'] == "") {
	$person[2]['nickname'] = $person[2]['fullname'];
}
if (empty($person[3]['nickname']) || $person[3]['nickname'] == "") {
	$person[3]['nickname'] = $person[3]['fullname'];
}
strnatcmp($person[2]["nickname"], $person[3]["nickname"]);
if (empty($person[3]['nickname']) || $person[3]['nickname'] == "") {
	$person[3]['nickname'] = $person[3]['fullname'];
}
if (empty($person[1]['nickname']) || $person[1]['nickname'] == "") {
	$person[1]['nickname'] = $person[1]['fullname'];
}
strnatcmp($person[3]["nickname"], $person[1]["nickname"]);

 21行であり、比較回数が増えればもっともっと増えます。これに目を通すのは前の例より時間がかかります。さらに置換ミスやタイプミスによる想定外の動作に気づきにくいという問題もあります。
 もっとも、一番読むのが早いのは何も書かれていないコードです。上に並べたコードは比較をしましたが比較から何も返ってきません、データを参照して、操作しているだけです。最後のコードに至ってはデータを上書きで壊すバグの元ですらあります。再利用の可能性のある場合でもコメントアウト、再利用の可能性のない場合は完全に消去。読む必要のある部分は0行になります。

  • この記事いいね! (0)
村上 著者:村上

【PHP】文字列を全角←→半角に変換するmb_convert_kana()関数

覚えておくと意外と便利なので、備忘録としてまとめ。
PHPで全角を半角に、もしくはその逆を行う方法です。

今回使う関数は、mb_convert_kana()です。
参考にさせていただいたページはこちら。

PHP関数 – 半角 ⇔ 全角 変換 – mb_convert_kana() – PHP入門のカルマ
https://webkaru.net/php/function-mb-convert-kana/

 

使い方はこちら。

$result = mb_convert_kana([変換したい文字列], [変換方法]);

上記のように指定します。
なお、変換方法は省略が可能で、省略した場合は、KV(=「半角カタカナ」を「全角カタカナ」にし、濁点付きの文字を一文字に変換)になります。
変換方法の一覧は下記のとおりです。

 

オプション 変換内容
r 「全角」英字を「半角」に変換
R 「半角」英字を「全角」に変換
n 「全角」数字を「半角」に変換
N 「半角」数字を「全角」に変換
a 「全角」英数字を「半角」に変換
A 「半角」英数字を「全角」に変換
s 「全角」スペースを「半角」に変換
S 「半角」スペースを「全角」に変換
k 「全角カタカナ」を「半角カタカナ」に変換
K 「半角カタカナ」を「全角カタカナ」に変換
h 「全角ひらがな」を「半角カタカナ」に変換
H 「半角カタカナ」を「全角ひらがな」に変換
c 「全角カタカナ」を「全角ひらがな」に変換
C 「全角ひらがな」を「全角カタカナ」に変換
V 濁点付きの文字を一文字に変換。K、Hと共に使用

よく使いそうなのは、「as」かな?
このオプションでは、英数字とスペースを半角にします。
なお、オプションは複数指定できます。

 

以上、文字列を全角←→半角に変換する方法でした。
全角が入力されては困る項目などでは、少なくとも「askh」あたりを指定しておけば、ある程度は対応できるかと思います。
是非、ご活用ください。

  • この記事いいね! (0)
takahashi 著者:takahashi

FuelPHP初期設定時に”It is not safe to rely on the system’s timezone settings.”エラーが出た時の対処法

先日、自分用にFuelPHPのテスト環境を作成していた時のこと。
ファイルやウェブサーバー周りの設定が終わって、テストでFuelPHPを動作させたところ…

Fuel\Core\PhpErrorException [ Error ]:
date_default_timezone_get(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone.

初めて見るエラー(汗
前回別の環境でセットアップした時はこんなエラーは出たことがなく、原因がわからなかったのでちょっと焦りました。

困っていろいろ調べていたら、こちらのサイトを発見。

[PHP]php.iniのtimezoneを設定する – Qiita

どうやら、PHP自身にデフォルトタイムゾーンを指定しないとダメ見たいです。

今回はCentOS7を使っていたので、php.iniは
/etc/php.ini
にありました。

php.ini内の

[date]セクションの部分に、赤枠のように記入します。

date.timezone = "Asia/Tokyo"

これでphp.iniを保存し、Apacheを再起動。

再度実行してみると

無事スタートページが表示されました。

  • この記事いいね! (0)