Home > PHP

PHP Archive

Twitter 検索

この記事の所要時間: 040

先日はじめたTwitterですが、なかなか楽しいです。

Twitterの仕組み自体ももちろん面白い良いのですが、日々ユーザが増えていき、システムが改良され(今日は日本語入力が改善されたもよう)、どんどん完成度が高まっていくさまが何だか楽しいです。

で、ちょうどCakePHPで何か作ろうと思っていたので、こんなの作ってみました。

Twitter 検索

Twitter 検索 : PHPで検索

Twitterの検索では、既にtwittersearchがあるのですが、それとは違いこちらは日本語ユーザのみの検索ができます。

興味のあるキーワードを入れて、Friends探しに使って貰えたら良いかと。

# 超見切り発車気味な公開なので、突然消える可能性もありますが。;-)

CakePHP SQLをログに記録

この記事の所要時間: 227

CakePHPで発行したSQLをログに記録する方法です。

DEBUG>=2にすれば画面下に表示されるSQL文ですが、DB処理後にリダイレクト等で遷移すると消えてしまいます。さらに本番稼働時は画面に表示するわけにはいかないのでやはりログに出力したいところです。

フレームワークに手を入れるのが一番簡単なのですが、バージョンが上がると面倒なので既存のDboSourceを継承したクラスにログ記録を追加します。

1. DboSourceを継承

dboはフレームワークで用意されているものだけでなく、app/model/dboにあるものもフレームワークで利用する事ができます。

ここではPostgreSQLを使うとしてDboPostgresを継承したDboPostgresLogを作ります。

ログ出力をON/OFFする定数LOG_SQLは後でcore.phpで定義します。

[app/model/dbo/dbo_postgres_log.php]

<?php
uses ('model' . DS . 'dbo' . DS . 'dbo_postgres');

class DboPostgresLog extends DboPostgres {
  /**
   * @var integer
   */
  var $queryNo = 1;

	function execute($sql) {
    $ret = parent::execute($sql);

    if (defined('LOG_SQL') && LOG_SQL) {
      $this->log(sprintf("%d. %s", $this->queryNo, $sql), LOG_DEBUG);
      $this->queryNo++;
    }

    return $ret;
	}
}
?>

2. 作成したDboPostgresLogを使用する

DATABASE_CONFIGのDB設定でdriverを’postgres_log’を指定します。

[app/config/database.php]

class DATABASE_CONFIG
{
  var $default = array('driver' => 'postgres_log',
                       'connect' => 'pg_connect',
                       'host' => 'localhost',
                       'login' => 'user',
                       'password' => 'pass',
                       'database' => 'foo',
                       'prefix' => '');
}

3. core.phpでLOG_SQLを定義

SQLをログに出力するか否かを設定するLOG_SQLをcore.phpで定義します。

LOG_SQLがtrueならSQL出力し、LOG_SQLが未定義もしくはfalseの場合は出力しません。

[app/config/core.php]

<?php
/**
 * If set to true, logging sql queries
 */
	define('LOG_SQL', true);
?>

これで実行されたSQL文が[app/tmp/logs/debug.log]に出力されます。

この方法はDEBUGの値に関係無くログ出力ができるのは良いのですが、DboSourcesを継承するのがイマイチです。フレームワーク(DboSources自身)で対応して貰えれば一番良いですけどね。;-)

Firecakeのコメントにcakeinfoが

この記事の所要時間: 027

FirecakeはCakePHPのデバッグ情報をFirebugに表示する便利なHelperです。

Bakeryでソースを見ているとコメントにこんな記述が。

Looking for more ideas, I also used cakeinfo.php, * a script released under MIT license by Masashi Shinbara.

cakeinfoを参考にして頂いているようです。嬉しい事です。

CakePHP Model#save()内でvalidates()を呼ばない

この記事の所要時間: 11

ref: CakePHPの何か-CakePHPのModelを使う

お馴染みyandoさんのプレゼン資料で気になったソースがあったので勝手に添削w。

35p:Validation設定例

<?php
class UsersController extends AppController {
  function fuga(){
    $data&#91;'User'&#93; = array(
      'login_id' => '+*+*+*+*',
      'password' => 'abcde',
      'name' => ''
      );
      if ($this->User->validates($data)) {
        $this->User->save($data);
      } else {
        $this->validateErrors($this->User);
      }
   }
}
?>

User#save()の部分ですが、Model#save()メソッド内ではModel#validates()が呼ばれるので、このままだとUser#validates()が2度実行されることになります。

このソースのようにsave()の前にvalidates()を実行するならsave()の第2引数にfalseを渡します。これならsave()内でvalidates()は実行されません。

<?php
      if ($this->User->validates($data)) {
        // validates()はsave()内では呼ばれない
        $this->User->save($data, false);
      }
?>

あとこの例のようにController#validationErrorsを参照しないのなら、Controller#validateErrors()は呼ばなくても良いです。

なので↓のようにシンプルに書けたりします。

<?php
class UsersController extends AppController {
  function fuga(){
    $data&#91;'User'&#93; = array(
      'login_id' => '+*+*+*+*',
      'password' => 'abcde',
      'name' => ''
      );
      $this->User->save($data);
   }
}
?>

まあ実際は登録完了後はリダイレクトしたりビューテンプレートを変えたりするので結局上のソースのようになるのですが。;-)

URLをリンクにするSmartyプラグイン

  • 2007-03-30 (金)
  • PHP
この記事の所要時間: 041

テキスト中にURLが含まれている場合にそれをaタグで囲むSmartyプラグインです。

既にありそうなのですが見つけられなかったので作りました。よろしければどうぞ。

modifier.url_link.php

<?php
/*
 * Smarty plugin
 * URL to link
 *
 * @param  string $value
 * @param  string $target
 * @return string
 */
function smarty_modifier_url_link($value, $target = null) 
{
    $options = "";
    if (!empty($target)) {
      $options = sprintf(" target=\"%s\"", $target);
    }

    $value = ereg_replace("&#91;&#91;:alpha:&#93;&#93;+://&#91;^<>[:space:]]+[[:alnum:]/]",
                         "<a href=\"\\0\"" . $options . ">\\0</a>", $value); 
    return $value; 
}
?>

plugins/ディレクトリに設置すれば↓のように使えます。

{$text|escape|url_link}

aタグにtarget属性を付加する場合は設定する値を渡します。

{$text|escape|url_link:"_blank"}

Smartyはビューテンプレートでちょっとした加工ができるので便利です。SmartyテンプレートがPHP以外の言語でも使えたら良いのに。

SmartyでSJISテンプレートを使う

  • 2007-03-27 (火)
  • PHP
この記事の所要時間: 032

SmartyテンプレートをSJISで書いていた時に「本」等の文字でSmartyパースエラーが発生していました。

「本」はSJISで[0x967B]なので、[0x7B]がSmartyのデリミタ[{]として評価されパースエラーになります。

これは{literal}{/literal}で該当文字を囲めば回避することができます。

{literal}本文{/literal}

またSmaryのデリミタを変える事もできるので、この方法で回避することもできそうです。

ref:Smartyの構文解析を回避

PHPからPostgreSQLに大量のデータを登録する

  • 2007-03-16 (金)
  • PHP
この記事の所要時間: 18

PostgreSQLに大量のデータを登録する際はINSERTを使って一件づつ処理するより、COPYを使って一気に登録した方が実行速度が(かなり)速いです。

そこでPHPでもCOPY文を使ってみましょう。

pg_copy_fromを使う

登録するデータが配列に入っているならpg_copy_fromだけで一気に処理できます。

配列の1要素が1レコードに対応しています。1要素内では各カラムをデリミタ(デフォルトは”\t”)で連結します。

<?php
$values = array();
$values&#91;&#93; = "1\tname1";
$values&#91;&#93; = "2\tname2";
$values&#91;&#93; = "3\tname3";

$db = pg_connect("dbname=hogedb");
pg_copy_from($db, 'table', $values);
?>

単純なCSVならデリミタを[,]に変えればokです。

<?php
$values = array();
$values&#91;&#93; = "1,name1";
$values&#91;&#93; = "2,name2";
$values&#91;&#93; = "3,name3";

$db = pg_connect("dbname=hogedb");
pg_copy_from($db, 'table', $values, ',');
?>

pg_put_lineを使う

登録するデータを一件ずつ処理したいならpg_put_lineで1レコードずつ出力する方法します。

はじめにCOPY文を発行しているので、デリミタの指定などSQL内で変更できます。

登録データ出力後に[\.]を発行してから、最後にpg_end_copyを呼ぶ必要がありますのでご注意を。

<?php
$db = pg_connect("dbname=hogedb");
pg_query($db, "copy table from stdin");
for ($i = 1 ; $i <= 10 ; $i++) {
  $line = sprintf("%d\tname%d\n", $i, $i);
  pg_put_line($db, $line);
}
pg_put_line($db, "\.\n");
pg_end_copy($db);
?>

ちなみにPostgreSQLではCOPY文もトランザクション内に含める事ができます。当然ROLLBACKも可能です。

数万件レベルのデータでも体感できる程パフォーマンスが違うので、もし知らない方は一度お試しあれ。

Zend PHP 5 Certification self test

  • 2007-03-13 (火)
  • PHP
この記事の所要時間: 19

Zend社のサイトにZend認定資格「Zend PHP 5 Certification」のSelf Testがあるようです。

いちおうベンダ資格ホルダー(wなのでチャレンジしてみました。

結果は「6/8」という微妙な得点。。。

まあ思ったよりは良かったのでチャレンジしてみようかな。(こういう作戦か?)

<ちょい脱線>

ベンダ資格は「資格なんか何の役に立たない」とバカにされる向きもありますが、あると割と便利です。特に初対面の相手には対して簡潔にスキルが証明できるという点ではそれなりに効果があると思います。

(こう言うと「資格なんかあてにならない。資格は持っているが使えないヤツを知っている。」と反論してくる人がいるのですが、これは資格があろうと無かろうと同じ事で、相手が資格を持っていても盲信しなければ良い事だと思います。)

もちろんスキルを身につける事が大事なのは間違いないですが、スキルを身につける取っかかりとして資格を取るというのもありなのでは。

試験やセミナー等でお金がかかるというのは確かに何とかして欲しいところではありますが。:-p

</ちょい脱線>

ちなみにSelf Testでは正解が表示されませんので、解答を知りたい方はZend PHP5 Certification self-test – David Coallierでどうぞ。

CakePHPをWebサイトの一部で使う

この記事の所要時間: 053

以前依頼を受けた案件でこんなのがありました。

  • 現在稼働中のサイトに機能(ページ)追加
  • 実装方法はお任せ(既存のライブラリ等は使用しなくて良い)
  • 但し、Smartyは必須
  • あとSmartyテンプレート、PHPの内部エンコーディングはSJIS

仕様自体はそれほど複雑で無いのでDBライブラリ(PEAR::DBとか)+Smaryでゴリゴリ書いても良いのですが、せっかくの機会なのでCakePHPを使ってみる事にしました。

DocumentRoot以下には既に稼働中のページがあるので、イメージとしてはそこにPageControllerを置き、そこからCakePHPを呼び出すといった感じです。

(CakePHP自体を追加ディレクトリに入れるという力業もあるのですが、ファイル構成が複雑になって、メンテナンスされる方が困りそうなのと個人的にもDocumentRoot以下にライブラリがあるのは抵抗があるのでそれはやめました。。。)

構成としてはこんな感じです。

[cake] <-- CakePHPを置く
  + [app]
  + [cake]
  + [vendors]
[DocumentRoot]
  + [new]
    + index.php
    + add.php
    + update.php
    + delete.php
  + [new2]
    + index.php
    + add.php
    + update.php
    + delete.php

DocumentRoot以下では単純にCakeのappを呼びます。

<?php
$_GET&#91;'url'&#93; = '/new/index';
// cake/をinclude_pathに入れたら
include('app/webroot/index.php');
// cake/をinclude_pathに入れないなら
//include('../../cake/app/webroot/index.php');
?>

$_GET['url']に渡すパスを変えれば任意のコントローラ・アクションが呼べます。

この方法ならブラウザから見るパスとCake用のパスを異なるものにできるので小さな案件ならわりと使えるシーンはあるのではないかと思います。(突然「URLを変えてくれ」とかありません?)

Smarty関係も小ネタがあるので、それはまた書きたいと思います。

# 昨今のWebフレームワークはFrontControllerが主流ですが、PageControllerももうちょっと見直されても良いかなと思ったり。

RecursiveDirectoryIteratorで再帰なしでファイル走査

  • 2007-02-25 (日)
  • PHP
この記事の所要時間: 038

あるディレクトリ内のファイルを走査して各ファイルに処理を行うなんて時に今までは再帰を使って実装していました。

PHP5な環境ならRecursiveDirectoryIteratorを使ってこんな感じで作ってました。

<?php
function gc(RecursiveDirectoryIterator $itr, $term) {
  foreach ($itr as $elem) {
    if ($itr->hasChildren()) {
      gc($itr->getChildren(), $term);
    } else {
      if ($elem->isFile() && $elem->getMTime() < $term) {
//        unlink($elem->getPathname());
        echo $elem->getPathname() . PHP_EOL;
      }
    }
  }
}

$term = strtotime('-1 day');
gc(new RecursiveDirectoryIterator('/tmp'), $term);
?>

これRecursiveIteratorIterator(凄い名前。。。)を使えば再帰なしで書けるんですね。

<?php
function gc($path, $term) {
  $itr = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
  foreach ($itr as $elem) {
    if ($elem->isFile() && $elem->getMTime() < $term) {
//        unlink($elem->getPathname());
      echo $elem->getPathname() . PHP_EOL;
    }
  }
}

$term = strtotime('-1 day');
gc('/tmp', $term);
?>

うーん便利。

GoogleCodeSearchで見るとPHPUnitやらPHP_CodeSnifferで使われていました。

SPLはもうちょっと情報が欲しいですね。

参考:PHPのSPLを使用してディレクトリを操作する – PHPプロ!ニュース

ホーム > PHP

検索
フィード
メタ情報

Return to page top