エントリー

タグ「PHP」の検索結果は以下のとおりです。

PHP5へ移行中

PHP Labo のプログラムをPHP5へ移行中…。(サーバーはロリポップ)

PHPの設定を .htaccess でも ini_set() でも変更できない問題は、一部の設定を php.ini で設定 で決定っぽい。
ということは、プログラムごとに設定を変更するのは不可能なのか…。しかも、php.ini も一部の設定しか変更できないです。

upload_max_filesize など、変更できない値はどうしようもないのかなぁ…。2M で固定されてしまったのはちょっと痛い。
あと、PHP4では何もしなくても PEAR が使えたけど、PHP5からは使えないみたい。

ちなみに、PHP Labo の新しいプログラムは、php.ini 編集画面で magic_quotes_gpcOff に設定すれば問題なく動作するようです。(PEAR は使っていないし。)
.htaccess に書かれたPHPの設定は無視されるけど、大きな問題は無し。

PHP Labo では、php.ini を以下のように設定しておいた。これでしばらく様子見…。

設定項目設定内容
default_charset UTF-8
mbstring.language Japanese
mbstring.internal_encoding UTF-8
mbstring.encoding_translation Off
mbstring.http_input pass
mbstring.http_output pass
magic_quotes_gpc Off
session.use_trans_sid 0
short_open_tag Off
safe_mode On

MySQL&SQLite用のデータベース管理ツール(改良版)

以前に作成したデータベース管理ツールを改良しました。

PHP Labo のプログラムをPHP5用に書き換えるにあたり、簡易な管理ツールが欲しくなったので作成。以前作成したものを使えば大丈夫…と思いきや、PDOを使わないとSQLite3には接続できないので…。

そんな訳で、実行にはPHP5+PDOが必要です。また、以前は断念したデータのインポート機能も実装。なかなか便利。

詳細とダウンロードは以下のページから行えます。

http://www.php-labo.net/download/tool/admin/

PHP4→PHP5へ移行中

PHP Labo のプログラムを地道に書き換え中。PDO関連の修正が、単純な置換では対処できないので面倒。プラグインも含めると結構なファイル数になるなぁ…。

PHPと言えば、ロリポップがPHP5に対応するので大喜び…と思っていたけど、どうも色々問題があるようです。特に、PHPの設定を .htaccess でも ini_set() でも変更できないのは痛すぎる。

公式サイトには

現在、当該事項の対応・検証を行っております。

とか書かれているので、変更できるようになることを期待。というか、変更できなかったら使い勝手が悪すぎるので、サーバー引越しも検討しなければ…。

他の格安サーバーでは、何か問題は出ていないのかな…。

PDOの不具合

PDOのプレースホルダが意図したとおりに動作しません…。

$stmt = $pdo->prepare('SELECT * FROM address LIMIT ?, ?');
$flag = $stmt->execute(array(0, 5));

こんな風に書くと、SQLの文法エラーと言われてしまいます。
プレースホルダを文字で指定しても駄目。

$stmt = $pdo->prepare('SELECT * FROM address LIMIT :from, :to');
$flag = $stmt->execute(array(':from' => 0, ':to' => 5));

LIMIT句以外なら、普通にプレースホルダが機能するんだけどなぁ…。
ちなみに、bindValue() で値を指定してから execute() を実行するようにすればLIMIT句でも大丈夫だった。

$stmt = $pdo->prepare('SELECT * FROM address LIMIT ?, ?');
$stmt->bindValue(1, 0, PDO::PARAM_INT);
$stmt->bindValue(2, 5, PDO::PARAM_INT);
$flag = $stmt->execute();

文字で指定しても当然のように動作します。

$stmt = $pdo->prepare('SELECT * FROM address LIMIT :from, :to');
$stmt->bindValue(':from', 0, PDO::PARAM_INT);
$stmt->bindValue(':to',   5, PDO::PARAM_INT);
$flag = $stmt->execute();

なかなか原因が判らなかったけど、これはPDOの不具合らしい。

何とも厄介な…。公式サイトには

PDO::execute() errors when parameters are used in LIMIT clause

と書かれているので execute() の問題らしいけど、bindValue() を使えばバージョンに関わらず実行できる…と思っていいのかな…。
うっかりミスでSQLインジェクションが起こり得ないようにするためにプレースホルダを使っている(僕の場合は)ので、「LIMIT句に値を渡すときだけ直接指定する」という書き方は極力避けたいのだけど。

PDOお試し中

概要だけは知っていたけど、実際に使うのは多分初めて。PEAR::DB と同じような感覚で使えて良い感じです。

PDO でのエラー処理は trycatch で捕捉するのが定番みたいですが、PEAR::DB みたいに戻り値のチェックで処理することもできるのですね。
PHP Labo のプログラムは非オブジェクト指向(手続き型)で書くので、エラー処理も昔ながらの方法にしようかなぁ…。その場合、こんな感じ?

<?php

try {
  $pdo = new PDO(
    'mysql:dbname=phpdb;host=localhost',
    'user',
    'pass',
    array(
      PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT
    )
  );
} catch (PDOException $e) {
  exit($e->getMessage());
}

$stmt = $pdo->query('SELECT * FROM address');
if (!$stmt) {
  list($state, $code, $message) = $pdo->errorInfo();
  exit($state . ':' . $code . ':' . $message);
}

while ($data = $stmt->fetch(PDO::FETCH_ASSOC)) {
  echo $data['no'] . ':' . $data['name'] . "<br>\n";
}

?>

コンストラクタのエラーは trycatch でしか補足できなかった。他に方法が無いか、後でもう少し調べてみます。でもやっぱり、データベースのエラーは全部 trycatch で処理した方がスッキリ書けるかなぁ…。
今のところ使っていないけど、トランザクションを使う場合は特に便利だし。

また、SQLite2を使うかSQLite3を使うかで、コンストラクタに渡す値が微妙に異なります。うーん、設定ファイルでSQLite2、SQLite3、MySQLを切り替えるようにするかな。

PHP4→PHP5への移行

このサイトはロリポップを使用しているのですが、とうとうPHP5に移行するらしいです。ちなみに Web Liberty はWADAXを使用していますが、結構前にPHP5に移行済み。
その他のサーバーもPHP4が使えなくなるのは時間の問題だと思うので、PHP Labo のプログラムをPHP4非対応に修正していきます。
代わりに、PHP5で追加された機能を色々使います。PEARが不要になるので、設置も楽になるハズ。

そんな訳で、自分用に移行メモ。「オブジェクトのコピー方法が云々」とか紹介しているのではなくて、あくまでも PHP Labo のプログラムを修正するための自分用作業メモ。ちなみに、厳密にはPHP5.1以降への移行が前提になっています。

  • PEAR::DB ではなく PDO を使用する。
  • opendir() ではなく scandir() を使用する。
  • fopen() + fwrite() + fclose()file_put_contents() で処理できる。ただし、ロック方法などを後で調べる。
  • fgetcsv() はPHP4とは異なり日本語処理に難があるため、使い物にならなくなっている(ような気がする)。文字コードの問題っぽい?
  • セッションハイジャック対策に session_regenerate_id(true) を使用する。
  • XMLの解析には SimpleXML が便利。
  • 初期設定ではMySQLではなくSQLiteを使用するようにしておく。

思いついたら追加します。
あと、PHP5への移行と同時に、ちょっとだけ機能強化もする予定。

ようやく仕事が直接関係しないプログラミングができると思うと、何だかテンションが上がるぞ。

PHPでツリー表示プログラム

これも必要になったのでメモ。

<?php

function put_tree($no, $line, $broths, $childs, $texts) {
  echo '<span class="line">' . $line . '</span>';
  echo '▼[' . $no . ']' . $texts[$no] . '<br>';

  $line = preg_replace('/├$/', '│', $line);
  $line = preg_replace('/└$/', ' ', $line);

  $no = isset($childs[$no]) ? $childs[$no] : 0;

  while ($no > 0) {
    $tail = $broths[$no] ? '├' : '└';
    put_tree($no, $line . $tail, $broths, $childs, $texts);
    $no = $broths[$no];
  }
}

?>
<html>
<head>
<title>ツリー表示</title>
<style>
.line {
  font-family: "MS ゴシック", monospace;
}
</style>
</head>
<body>
<?php

$logs = array(
  array(1, 0, 'あああああ'), //記事番号・親記事番号・記事内容
  array(2, 1, 'いいいいい'),
  array(3, 1, 'ううううう'),
  array(4, 2, 'えええええ'),
  array(5, 3, 'おおおおお'),
  array(6, 3, 'かかかかか'),
  array(7, 0, 'ききききき'),
  array(8, 6, 'くくくくく'),
  array(9, 8, 'けけけけけ'),
  array(10, 7, 'こここここ')
);

$roots  = array();
$broths = array();
$childs = array();
$texts  = array();

foreach ($logs as $log) {
  list($no, $pno, $text) = $log;

  if ($pno == 0) {
    $roots[] = $no;
  } else {
    $broths[$no]  = isset($childs[$pno]) ? $childs[$pno] : 0;
    $childs[$pno] = $no;
  }
  $texts[$no] = $text;
}
rsort($roots);

foreach ($roots as $root) {
  put_tree($root, '', $broths, $childs, $texts);
}

?>
</body>
</html>

実行結果は以下のとおり。

▼[7]ききききき
└▼[10]こここここ
▼[1]あああああ
├▼[3]ううううう
│├▼[6]かかかかか
││└▼[8]くくくくく
││ └▼[9]けけけけけ
│└▼[5]おおおおお
└▼[2]いいいいい
 └▼[4]えええええ

ツリー生成手順は、以下の書籍を参考にしています。プログラミングをはじめた頃によく読んでいた本なので、久しぶりに読んで懐かしい…。

CGI&SSIを使うとホームページ作成にこんなに差が出る

PHPで2ch互換トリップ生成

必要になったので自分用にメモ。トリップ生成時の文字コードはShift-JISにしておく。

<?php

$tripkey = '#istrip';? //パスワードとする文字列(# 付き)
$tripkey = substr($tripkey, 1);

$salt = substr($tripkey . 'H.', 1, 2);
$salt = preg_replace('/[^\.-z]/', '.', $salt);
$salt = strtr($salt, ':;<=>?@[\\]^_`', 'ABCDEFGabcdef');

$trip = crypt($tripkey, $salt);
$trip = substr($trip, -10);
$trip = '◆' . $trip;

echo $trip;

?>

トリップの概要とコードは以下のページを参考にしました。(参考というか、ほぼそのまま。)

トリップ (2ちゃんねる) - Wikipedia

ファイルロック解除処理

またもPHPでの排他処理について。ファイルに書き込みを行う場合は

$fp = fopen('data.txt', 'r+') or exit('error!');
flock($fp, LOCK_EX);

~ここで書き込みを行う~

flock($fp, LOCK_UN);
fclose($fp);

で問題ないと思ったけど、まだ駄目っぽい。ロックの解除に問題があり、ファイルの破損は完全に回避できないらしい。調べてみると色々出てきた。

@ITのPHPの記事が突っ込みどころ満載 - 暴言満載

fcloseの前にflock(ファイルポインタ, LOCK_UN) する人は実に多いのですが、これははっきりと間違いだと断言します。flockをfcloseの前に解除するということは、fcloseの前に他のプロセスが割り込む可能性が出るということです。ファイルへの書き込みは、fwriteとかfputsとかしてからfflushまたはfclose実行までのどこかで行われる、というのがファイル周りのI/Oの基本です。なので、fcloseもロックの範囲内に入れなければなりません。fcloseでflockが解除されるのはそういう理由があるのです。

PHP/ファイルロック/設計 - TestWiki - PukiWiki Plus!

プロセスレベルの書き込みバッファによって、書き込みデータがOSに渡されずに残っている可能性がある。

fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);

で明示的にフラッシュするか、flock($fp,LOCK_UN)しない

fclose($fp); // LOCK_UN してはならない

をすることで、バッファがフラッシュされた後、ロックが解除される。

PHP: flock - Manual

ロックの解放には fclose() でを使用します (これは、スクリプトが終了した場合にも自動的にコールされます)。

公式サイトの解説は「ロックの解放には fclose() でを使用します」と書いている割に、サンプルコードでは flock($fp, LOCK_UN); で解除しています。こんな風に書かれていたら、どっちでも大丈夫なのかと思ってしまうよ…。
公式サイトのサンプルコードは、あまり信用しないほうがいいのかなぁ…。公式サイトなのに。

ついでに書くと「でを使用します」って誤字ですよね…?

ともかく、ロックの解除は fclose() にまかせて

$fp = fopen('data.txt', 'r+') or exit('error!');
flock($fp, LOCK_EX);

~ここで書き込みを行う~

fclose($fp);

が正しいっぽい。

データベースとファイル

PHPでデータベース対応のプログラムをいくつか作ってきましたが、データベースとファイルの両方に対応したプログラムを作成しようと考え中。PHP Labo のプログラムとは別に、1つ徹底的に高機能なプログラムを作りたいのです。

その際、データを扱う処理をどんなふうに書くか悩み中。

同じ命令でMySQLもSQLiteもファイルも扱えるような、有名どころのクラスがあればいいけど、どうも無いっぽい。うーん、プログラム内で単純に条件分岐して各処理を併記するかなぁ…。可読性が下がりそうで少し嫌だけど。

ページ移動

ユーティリティ

カテゴリー

検索

エントリー検索フォーム
キーワード

過去ログ

過去ログ表示フォーム
キーワード

Feed