trim()が原因で文字化けする

POSTデータをチェックする前に無駄な前後のスペースなどは取り除きたい場合。CakePHPで言うとbeforeValidateなんかで処理するケース。PHP: trim - Manualを使おうとすると文字化けする。

*1

<?php
// _は半角スペース_は全角スペース
$str = trim($str, '_\t\r\n\0\x0B_');

これは文字化けする。unicodeならいけるのかとUTF8で試すがダメみたい。なのでPHP: preg_replace - Manualで対応。UTF8でしか使えません。

<?php
// _は半角スペース_は全角スペース
$pattern = array('/^[_\t\r\n\0\x0B_]+/u', '/[_\t\r\n\0\x0B_]+$/u');
$str = preg_replace($pattern, '', $str);

*1:\円マークが表示されている場合はバックスラッシュと読みかえて下さい。

CakePHPはじめました

ことの始まり

これまでSmartyPEARPHPアプリを作ってきたのだけれど、独学なのでなんともMVCに自信が持てない。時間もかかってるので非効率な感じがずっとしていた。

小さいプログラムからプログラミングを始めて、データベースを扱うようになって、コードが多少複雑になり、見返すと自分のコードがスパゲティーなのにギョッとした。そこで、ふと目にしたMVCという考え方をなんとなく覚えて、できたアプリのソースがどんどん大きくなってきて、特にControllerがなぜか肥大化していることに、またギョッとする。
どうしようかと思いながらふっとフレームワークってなんだろうと思って始めてみたのがきっかけ。

なぜCakePHP?

Zend Framework SymfonyどうやらCakePHP以外で有名なのはこのあたり。CakePHPにした理由は2つ。

  1. 小規模な物を早く、簡単に作りたい。
  2. 記事が多そう。

現時点ではほんとに小規模でごく数人が使うだけのアプリを想定していること。PHPのことを検索してるとCakePHPの話題がよく目に付いたので、なんとなく参考になるものが多そうな気がした。

マニュアルって大事

結局データベース設計からやり直したけど、結果的には早く、バグの少ないものを作れている気がする。以下マニュアル読んで調べてみるまで分からなかったこと。

APPディレクトリ以外は変更を加えてはいけない。

コンソールからインストールした場合は不要ですが、まずapp_controllerとapp_modelをcoreディレクトリからコピーしてしまうこと。なんか問題が起きた時に複雑になってしまいました。

基本データの取得はFind

readとfind取得の方法があって混乱しました。以下のような記事を発見したのですが、

そう 実はおおもとのDBアクセスは findAll() なんです。。。
read にいたっては
read() にきたものをバラして find() に投げて
find() はそれをさらに解析して findall に投げるわけです。
いや だったら使い分けないで全部 findAll で細かく指定するよ。。。
ちなみに findcount() も findAll() してます。
findAllBy とか findBy 系はどうなんだろう。
他のとこで camelclass をばらしてるのでチョット動き違うかもしれません。

cakephp find findall read の違い ← Neo Inspiration

readを使うとfindの引数を細かく指定したりしないことが多く、ソースがすっきりするので、場合によって使い分けるとよいのかも。参照した記事がバージョンが古い記事なので、そういう意味でも公式のマニュアルの内容も確認しながら上記を参考にすると良いのかな、と。

今日はとりあえずここまで。

MDB2でデータを表示するのにページを分割して表示する

つまりは

前へ ...5 6 7 8 9 10 11... 次へ 6/24ページ目

みたいなものを表示したかったので作ってみた。上記であれば5または11をクリックするとナビゲーションの表示が切り替わる。「前へ」や「次へ」は単純に次のページなり前のページなりへ遷移。現在のページ情報はGETで送信しています。一部抜粋*1です。

まずは設定。

<?php
# conf.php
# ページナビゲーションの最大表示ページ数を設定

define('PAGE_LIMIT', 5);
?>

データベースの操作。
総数と1ページ分のデータを取得するのに2回もクエリを発行するのが、なんかこれでよいのか?と疑問。誰かつっこみください。

<?php
# model.php
# データベースへのアクセス
# データ取得時に該当データの総件数と1ページ分のデータを取得

require_once('MDB2.php');

// エラーハンドラ
function my_error() {
  print('DBアクセスエラーが発生しました.管理者にお問い合わせ下さい.');
  exit;
}

class model {
  protected $db;	// DBクラスオブジェクト
  
  // コンストラクタ
  function __construct() {
    $con =& MDB2::connect(DSN);	// DBへ接続

    if (PEAR::isError($con)) die($con->getMessage());
    $con->setErrorHandling(PEAR_ERROR_CALLBACK, 'var_dump');
    $this->db = $con;
  }
  // データ一覧の取得
  function get_datas($page, $limit) {
    $result = array('page' => $page);
    $res = $this->db->query('SELECT * FROM table ORDER BY update DESC');
    $result['numrows'] = $res->numRows(); 
    $this->db->setLimit($limit, ($page-1)*$limit);
    $res = $this->db->query('SELECT * FROM table ORDER BY update DESC');
    $result['rows'] = $res->fetchAll(MDB2_FETCHMODE_ASSOC);
    $res->free();
    return $result;
  }
  
  // データ一覧の取得(プリペアドステートメントを使用)
  function get_datas_by_id($id, $page, $limit) {
    $result = array('page' => $page);
    $types = array('text');
    $data = array('id' => $id);
    $sth = $this->db->prepare('SELECT * FROM table WHERE id = :id ORDER BY update DESC', $types);
    $res = $sth->execute($data);
    $result['numrows'] = $res->numRows();
    $this->db->setLimit($limit, ($page-1)*$limit);
    $sth = $this->db->prepare('SELECT * FROM postdata WHERE id = :id ORDER BY update DESC', $types);
    $res = $sth->execute($data);
    $result['rows'] = $res->fetchAll(MDB2_FETCHMODE_ASSOC);
    $sth->free();
    $res->free();
    return $result;
  }
}
?>

表示にはSmartyを使用しています。

<!--
  page_navi.tpl
  ページナビゲーション
-->
<div id="page_navi">
{if $navi.before}<a href="{$uri}&page={$navi.page-1}">前へ</a>{/if}
{if $navi.page_before}...<a href="{$uri}&page={$navi.block*$smarty.const.PAGE_LIMIT}">{$navi.block*$smarty.const.PAGE_LIMIT}</a>{/if}
{section name=i start=1 loop=$navi.loop+1}
  {if $navi.position === $smarty.section.i.index}
    {$navi.block*$smarty.const.PAGE_LIMIT+$smarty.section.i.index}
  {else}
    <a href="{$uri}&page={$navi.block*$smarty.const.PAGE_LIMIT+$smarty.section.i.index}">{$navi.block*$smarty.const.PAGE_LIMIT+$smarty.section.i.index}</a>
  {/if}
{/section}
{if $navi.page_next}<a href="{$uri}&page={$navi.block*$smarty.const.PAGE_LIMIT+6}">{$navi.block*$smarty.const.PAGE_LIMIT+6}</a>...{/if}
{if $navi.next}<a href="{$uri}&page={$navi.page+1}">次へ</a>{/if}
{$navi.page}/{$navi.num_page}ページ目
</div>

*1:コントローラー部分とSmartyへのアサイン処理とかは省略しました。

PHPでYahooにログイン出来なくなった

以前の記事PHPからYahooへログインするスクリプトを書いたのですが、ログインできなくなってました。
原因は不正ログイン騒動*1で何らかの対応をしたことが原因のよう。
なにが原因かよくわからなかったんですが、ググったサイト*2を参考に改良したところ、ログインに成功しました。
よりいっそうコードが汚くなったので、ちっとは綺麗にしてから次回ソースを公開しようと思います。

*1:[http://getnews.jp/archives/117084:title=「Yahoo!で不正ログイン多数報告?」の件についてYahoo! JAPANにきいてみた - ガジェット通信]

*2:[http://pg-memo.blogspot.com/2011/05/yahoo.html:title=WEBプログラマの雑記帳: プログラムからYahooにログインする際に、不正アクセス防止機能により文字認証画面が出た時の対処法]

URL上にファイルが存在するかチェックする

どこを参考にしたか忘れました。
単純で簡単そうだったのでこの方法にしました。

<?php
$header = get_headers($url);
if ($header[0] != "HTTP/1.1 404 Not Found") {
 echo "そのファイルは存在します。<br />";
} else {
 echo "そのファイルは存在しません。<br />";
}
?>

WEB上のファイルをダウンロードする

画像をダウンロードするのに必要になりました。
画像以外のダウンロードが出来るか分かりません。
PHP逆引きレシピを主に参考にしてます。

<?php
function downloadWebFile($url, $filename, $dir)
{
 # 保存するファイル名を設定します。
 $fileName = $dir.$filename;

 $fileData = file_get_contents($url);
  
 # ファイルを追記モードで開きます。
 $fp = fopen($fileName, 'ab');

 # ファイルをロックします(排他的ロック)。
 flock($fp, LOCK_EX);

 # ファイルの中身を空にします。
 ftruncate($fp, 0);

 # データを書き込みます。
 fwrite($fp, $fileData);

 # ファイルを閉じます。
 fclose($fp);
}
?>