PHPの最適化作業メモ

※以前別の場所で書いた文章を備忘的に書き記しておきます。

【投稿年月日】2009-01-08 【ジャンル】PHP/MySQL

 現在PHPの最適化作業をしているので、備忘的に書き残しておきます。主として参考にしたのは下記サイト。

ウノウラボ Unoh Labs: PHP最適化tipsまとめサイト
labs.unoh.net/2007/05/phptips.html
  1. staticが使えるなら、staticを使う。速度は4倍になる。
  2. __get, __set, __autoload は避ける。
  3. require_once() はコストがかかる。
  4. include や require では絶対パスで指定する。
  5. スクリプトの開始時間は $_SERVER[’REQUEST_TIME’] で得る。
  6. 正規表現は、文字列関数で代用できないか探る。(文字を見つけるだけならstrposなどでもよい)
  7. str_replace は preg_replace より早いが、strtr は str_replace の4倍早い。
  8. 文字列/配列両方を受け入れる柔軟さを持つ関数は避ける。変わりに個別の関数を用意する。
  9. @によるエラー制御は遅い
  10. $row[’id’] は $row[id] より7倍早い
  11. エラーメッセージはコストがかかる
  12. for ($x=0; $x < count($array); $x) の count() のようにループの度に呼ばれる関数はさけ、変数に格納する。
 弊社が運営するEDIUNETでの対応状況は以下の通りです。
  1. → 対応した。(※効果は?)
  2. → 使用していない。
  3. → 対応した。(※効果は?)
  4. → 対応した。(※効果があったみたい)
  5. → 使用していない。
  6. → 日頃から心がけているのでOK。
  7. → 日頃から心がけているのでOK。
  8. → 未対応。
  9. → 対応した。(※効果があったみたい)
  10. → 対応した。(※効果は?)
  11. → 対応した。(※効果があったみたい)
  12. → 対応した。(※効果は?)

 まずは簡単にできそうなところからと、取り合えず10.と12.をやりました。どれぐらい効果があるかは微妙ですが。。
10. $row[’id’] は $row[id] より7倍早い
12. for ($x=0; $x < count($array); $x) の count() のようにループの度に呼ばれる関数はさけ、変数に格納する。

 1.については、class中の「function()」について、内部に「$this->」が使われていない場合に「static function()」としました。ただ、クラスとオブジェクトについてよく理解していないので、このような使い方で正しいかどうか自信はありませんが。(追記: 数値が変わらない変数についてもstatic処理を施しました)
1. staticが使えるなら、staticを使う。速度は4倍になる。

 3.と4.もやってみました。
3. require_once() はコストがかかる。
4. include や require では絶対パスで指定する。
 絶対パスで指定する良い方法はないかと調べたところ、下記サイトを発見。

tips - 汎用インクルード用ファイルでの include, require では dirname(__FILE__) を使う
www.ecoop.net/memo/2006-03-29-2.html

 「dirname(__FILE__)」を使えばできるとのこと。実に手軽です。
【変更前】
require_once('./index.php');
【変更後】
require(dirname(__FILE__)).'/index.php');
 なお、一つ上のディレクトリにあるファイルを読み込みたい場合は以下の通り。
【変更前】
require_once('../index.php');
【変更後】
require(dirname(__FILE__).'/../index.php');

 9.と11.は結構大変でした。
9. @によるエラー制御は遅い
11. エラーメッセージはコストがかかる
 エラー文を出さないために、エラー制御演算子@を多用しているので、これはまずいということで調べました。(「@file_get_contents()」や「@fopen()」、「@mysql_connect()」などのように使っている)
 「file_get_contents()」や「fopen()」については、ファイルが存在するか否かを確認すればOKです。例えば、サイト内にあるファイルを「file_get_contents()」する場合は以下の通り。ファイルが存在するかどうかを「file_exists()」で確認してから読み込みます。
【変更前】
$text = file_get_contents($file);
【変更後】
$text = (file_exists($file)) ? file_get_contents($file) : '';
 また、外部にあるファイルを「file_get_contents()」する場合は以下の通り。URLが存在するかどうかを「curl_init()」で確認してから読み込みます。
【変更前】
$text = file_get_contents($url);
【変更後】
$text = (curl_init($url)) ? file_get_contents($url) : '';
 ちなみに、Windowsでは「curl_init()」の導入が少しやっかいです。(「curl_init()」の導入方法については「curl_init windows Fatal error」で検索してみてください)

 ところが、「mysql_connect()」の対処方法はなかなか分かりませんでした。と言うより、分からないのです。仕方がないので、.htaccess に「php_flag display_errors Off」を記述することで、エラーメッセージを非表示にすることにしました。
 参考にしたのは下記サイトです。

今日のエラーメッセージ - PHP Note
www.sound-uz.jp/php/note/errors
display_errors
On(表示)と Off(非表示)のいずれかを設定します。デフォルトは On でメッセージが表示されます。これを Off にするとメッセージは一切表示されませんので、運用時は Off に設定しましょう。
(中略)
.htaccess
#php_flag display_errors On
php_flag display_errors Off



 また、下記サイトでも最適化するTipsが紹介されていました。

PHPプログラムを向上させるコツ10個 - shoe-g.com 植山周志のぶっ飛びブログ
www.shoe-g.com/m/e/php.php
10. strlenの代わりにissetを使う
文字列の長さを得たいのならstrlenの代わりにissetを使いましょう
issetのほうが5倍動作が速いのです

 EDIUNETでは、決算書の数字を万億兆の単位で表示するために、strlen()を使って分岐を行なっています。こういう場合に使うのだろう、ということでやってみました。
【変更前】
function ChangeValue($val='') {
 $len = strlen($val);
 switch(TRUE) {
 case ($len<3 || ($val<0 && $len<4)) :
  return number_format($val*100)."万円";
 case ($len<7 || ($val<0 && $len<8)) :
  return number_format(substr($val, 0, -2))."億円";
 case ($len<11) :
  return number_format(substr($val, 0, -5)/10, 1)."兆円";
 }
}
【変更後】
function ChangeValue($val='') {
 $val .= ''; // おまじない
 switch(TRUE) {
 case (!isset($val[2]) || ($val<0 && !isset($val[3]))) :
  return number_format($val*100)."万円";
 case (!isset($val[6]) || ($val<0 && !isset($val[7]))) :
  return number_format(substr($val, 0, -2))."億円";
 case (!isset($val[10]) || ($val<0 && !isset($val[11]))) :
  return number_format(substr($val, 0, -5)/10, 1)."兆円";
 }
}
 ちなみに効果は?です。。


【おまけ】 「isset()」が出てきたので、セキュリティに関連する各種判定方法についてまとめておきます。
// 文字数制限(※6字以内)
function Count($str='') {
 if(!isset($str[6])) {
  return true;
 }
}
// 数字のみOK
function Num($str='') {
 if(is_numeric($str)) {
  return true;
 }
}
// 英数字のみOK
function StrNum($str='') {
 if(preg_match('/^[\w]+$/', $str)) {
  return true;
 }
}
// 英数字のみOKで、文字数制限(※6字以内)付き
function StrNumCount($str='') {
 if(preg_match('/^[\w]+$/', $str) && !isset($str[6])) {
  return true;
 }
}

EDIUNET | PHP/MySQL | 独り言 | 提供サービス | JavaScript