2012年3月22日木曜日

[PHP] 配列の最初と最後のキーまたは値を調べる

あんまり機会としては少ないとは思うが、稀に配列の最初の要素のキー(添字)や値を調べたくなる時がある。
添字配列であれば最初は0から始まりcount($array)-1で終わっているはずだが、
連想配列としてキーが文字列だったり、添字配列に連想配列が混ざったりしていると、
なかなかそうはいかない。

そういう時は配列のポインタを利用する。

配列は実は内部に現在どこの要素を指しているかを調べるポインタを持っているのだ。
foreachなんかはそのポインタを1個1個移動させて中の要素を取り出している。

故にポインタを最初の位置と最後の位置に移動させて、キーや値を取得すれば良い。

//link・・・http://mio-koduki.blogspot.com/2012/03/javascriptphp_12.html
//第一引数・・・最初のキーを取得したい配列
//返り値・・・最初のキー
function get_first_key($array)
{
    reset($array);
    return key($array);
}

//第一引数・・・最初の値を取得したい配列
//返り値・・・最初の値
function get_first_value($array)
{
    return reset($array);
}

//第一引数・・・最後のキーを取得したい配列
//返り値・・・最後のキー
function get_last_key($array)
{
    end($array);
    return key($array);
}

//第一引数・・・最後の値を取得したい配列
//返り値・・・最後の値
function get_last_value($array)
{
    return end($array);
}

array_shiftやarray_popでも最初、最後の値を取得することはできるが、
これらは元になった配列から値が削除されてしまうため、予期しない挙動になることがある。

しかし、これらを利用して

//最初のキー
$keys=array_keys($array);
array_shift($keys);

//最初の値
$values=array_values($array);
array_shift($values);

//最後のキー
$keys=array_keys($array);
array_pop($keys);

//最後の値
$values=array_values($array);
array_pop($values);

ということも出来なくはない。
これらを組み合わせて

//最初のキー
$keys=array_keys($array);
reset($keys);

//最初の値
$values=array_values($array);
reset($values);

//最後のキー
$keys=array_keys($array);
end($keys);

//最後の値
$values=array_values($array);
end($values);

でもOK
また下記のように一行でもかけるが、
E_NOTICEレベルエラーが出るので、
E_NOTICEレベルエラーを表示してるとちょっと邪魔臭い。

//最初のキー
reset(array_keys($array));

//最初の値
reset(array_values($array));

//最後のキー
end(array_keys($array));

//最後の値
end(array_values($array));

こんな感じ。

2012年3月16日金曜日

[PHP][JavaScript] Ajaxからの通信(アクセス)かどうか判定する

ファイルへのアクセスがAjaxから要求されたものなのか、
それともブラウザからアドレスを直接叩くなどしてAjaxを通さずにアクセスしてきたのか調べたい時がある。

jQueryやprototypeなどのライブラリを使ってAjax通信をするときは実はヘッダに
「X-Requested-With:XMLHttpRequest」
というデータをつけて送ってくる。
なので、ヘッダ情報に「X-Requested-With」があるかどうかを調べて
それが「XMLHttpRequest」という値になってるかをチェックすればいい。

//link・・・http://mio-koduki.blogspot.com/2012/03/phpjavascript-ajax.html
//返り値・・・Ajaxからの通信であればtrue、そうでなければfalse
function is_ajax()
{
    return isset($_SERVER['HTTP_X_REQUESTED_WITH'])&&strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])=='xmlhttprequest';
}

という感じになる。

では、jQueryなどのライブラリを使わずに自力でXMLHttpRequest(IEであればActiveXObject」)を使ってAjaxを行なっている場合はどうすればいいか?
その時は自分でヘッダに「X-Requested-With:XMLHttpRequest」をつけてしまえばいい。

//link・・・http://mio-koduki.blogspot.com/2012/03/phpjavascript-ajax.html

//Ajax通信用のオブジェクト作成関数
function create_ajax_object()
{
    //IE以外
    if(window.XMLHttpRequest)
    {
        try
        {
            return new XMLHttpRequest();
        }
        catch(e)
        {
            return null;
        }
    }
    //IE
    else if(window.ActiveXObject)
    {
        try
        {
            //IE6以降
            return new ActiveXObject('Msxml2.XMLHTTP');
        }
        catch(e)
        {
            try
            {
                //IE5
                return new ActiveXObject('Microsoft.XMLHTTP');
            }
            catch(e)
            {
                return null;
            }
        }
    }
    else
    {
        return null;
    }
}

//Ajax通信関数
function ajax_access(method,path,data,callback,async_flag)
{
    //パスが設定されていなければnullを返す
    if(path==null)
    {
        return null;
    }

    //Ajax通信用のオブジェクト作成
    var ajax_object=create_ajax_object();

    //Ajax通信用のオブジェクト作成に失敗したらnullを返す
    if(ajax_object==null)
    {
        return null;
    }

    //メソッドに何も設定されていなければGETを設定する
    method=method||'GET';
    //同期フラグに何も設定されていなければtrueを設定する
    async_flag=async_flag||true;

    //通信回線を開く
    ajax_object.open(method,path,async_flag);

    //content-typeを指定する
    //これがないとPOSTのデータを飛ばすときなどに失敗することがある
    ajax_object.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8');

    //「X-Requested-With:XMLHttpRequest」をヘッダにつける
    ajax_object.setRequestHeader('X-Requested-With','XMLHttpRequest');

    //通信時のイベント
    ajax_object.onreadystatechange=function()
    {
        //readyStateが4の時通信終了
        //statusが200の時通信成功
        if(ajax_object.readyState==4&&ajax_object.status==200)
        {
            //コールバック関数が設定されていれば呼び出す
            if(callback!=null)
            {
                //コールバック関数に結果を返す
                callback(ajax_object.responseText);
            }
        }
    }

    //データ送信
    ajax_object.send(data);
}

//Ajax通信を行う
ajax_access('post','test.php',encodeURIComponent('test')+'='+encodeURIComponent('テスト'),function(data){alert(data);},true);

とこんな感じになる。
久しぶりにJavaScriptでAjax書いた気がする。
ポイントはopenメソッドを呼び出した後に
setRequestHeaderを呼び出して「X-Requested-With:XMLHttpRequest」をヘッダにつけるところ

2012年3月12日月曜日

[JavaScript][PHP] オブジェクトからクエリストリングを作る

以前クエリストリング(URLパラメータ、GETパラメータって呼ばれたりもする)から
オブジェクトを作るということをやったので今度はその逆で
オブジェクトからクエリストリングを作るのをやってみたいと思う。
ちなみに、クエリストリングのキーの部分が数字のみになる場合のプレフィックスはつけてない。

//link・・・http://mio-koduki.blogspot.com/2012/03/javascriptphp_12.html
//第一引数・・・クエリストリングにしたいオブジェクト
//返り値・・・オブジェクトからビルドされたクエリストリング
function build_query(data)
{
    if(is_array(data)||is_object(data))
    {
        var params=[];
        for(var i in data)
        {
            if(is_array(data[i])||is_object(data[i]))
            {
                params.push(build(i,data[i]));
            }
            else if(is_can_format_value(data[i]))
            {
                params.push(url_encode(i)+"="+url_encode(format_value(data[i])));
            }
        }
        return params.join('&');
    }
    else
    {
        throw new Error('Parameter 1 expected to be Array or Object.');
    }

    function build(key,data)
    {
        var params=[];
        for(var i in data)
        {
            if(is_array(data[i])||is_object(data[i]))
            {
                params.push(build(key+'['+i+']',data[i]));
            }
            else if(is_can_format_value(data[i]))
            {
                params.push(url_encode(key+'['+i+']')+'='+url_encode(format_value(data[i])));
            }
        }
        return params.join('&');
    }

    function is_can_format_value(value)
    {
        return typeof value=='boolean'||typeof value=='string'||typeof value=='number';
    }

    function format_value(value)
    {
        if(value===true)
        {
            return 1;
        }
        else if(value===false)
        {
            return 0;
        }
        else
        {
            return value;
        }
    }

    function url_encode(string)
    {
        return encodeURIComponent((string+'')).replace(/!/g,'%21').replace(/'/g,'%27').replace(/\(/g,'%28').replace(/\)/g,'%29').replace(/\*/g,'%2A').replace(/%20/g,'+');
    }

    function is_array(data)
    {
        return data instanceof Array;
    }

    function is_object(data)
    {
        return data!=null&&typeof data=='object'&&Object.prototype.toString.call(data)!='[object Array]';
    }
}

と、こんな感じになると思う。
ちなみにPHPではhttp_build_queryが同じような挙動をする関数。

[PHP] 配列からCSVを作成しダウンロード

DBなどからデータを引き出し、CSV形式でダウンロードさせることは往々にしてある。
ロジック自体はそこまで複雑じゃないので毎回書いてもいいが、
数が多いと面倒なので関数にしてしまう。

//link・・・http://mio-koduki.blogspot.com/2012/03/php-csv.html
//第一引数・・・ダウンロードするときのファイル名
//第二引数・・・CSVにする配列(array(array(data1,data2),array(data1,data2))の2次元配列形式)
//第三引数・・・ヘッダを付ける場合は配列を渡す(array(header1,header2)の配列形式)
//返り値・・・なし
function csv_download($file_name,$data,$header=null)
{
    //ヘッダを付ける場合はarray(header1,header2)の形式で渡す
    if($header!=null)
    {
        array_unshift($data,$header);
    }

    //PHPのテンポラリに読み書きの準備をする
    $fp=fopen('php://temp','r+');
    foreach($data as $v)
    {
        //テンポラリにCSV形式で書き込む
        fputcsv($fp,$v,',','"');
    }
    //ファイルポインタを一番先頭に戻す
    rewind($fp);
    //ファイルポインタの今の位置から全てを読み込み文字列へ代入
    $csv=stream_get_contents($fp);
    //SJISに変える
    $csv=mb_convert_encoding($csv,'SJIS',mb_internal_encoding());
    //ファイルポインタを閉じる
    fclose($fp);
    //渡されたファイル名の拡張子やパスを切り落とす
    $file_name=basename($file_name);

    //ダウンロードヘッダ定義
    header('Content-Disposition:attachment; filename="'.$file_name.'.csv"');
    header('Content-Type:application/octet-stream');
    header('Content-Length:'.strlen($csv));
    echo $csv;
    exit;
}

という風に実装してみた。
tempの代わりにoutputストリームに書き込む方法もあるが、
こちらは出力バッファ系もごちゃごちゃいじる必要があるので、
一手間増えるためあまり好きじゃない。

ポイントとなる関数
  • fputcsv ・・・ 行を CSV 形式にフォーマットし、ファイルポインタに書き込む
  • stream_get_contents ・・・ 残りのストリームを文字列に読み込む

2012年3月9日金曜日

[JavaScript][PHP] クエリストリングからオブジェクトを作る

クエリストリングやURLパラメータ、GETパラメータと様々な呼び方があるが、
要するにURLの?以降にあるtest_key=test_valueの形式の文字列のことである。

PHPはわざわざ自前でパースしなくても、$_GETの中に配列として展開された形式で入ってるし、
parse_str関数を使えば好きなタイミングでパースすることが出るが、
JavaScriptにはそのような仕組みはなく、自力でパースしないといけない。

クエリストリングには配列として変換する仕組みがある。
test_key[]=test_value1&test_key[]=test_value2&test_key[test]=test_value3
というクエリストリングだと

array(1) {
  ["test_key"]=>
  array(3) {
    [0]=>
    string(11) "test_value1"
    [1]=>
    string(11) "test_value2"
    ["test"]=>
    string(11) "test_value3"
  }
}

という配列が生成される。

何サイトかJavaScriptでクエリストリングをオブジェクトに変える処理を見てみたが、
ほとんどのサイトでこの配列形式をサポートしてなかった。

なのでちょっと作ってみた。

//link・・・http://mio-koduki.blogspot.com/2012/03/javascriptphp.html
//第一引数・・・パースしたいクエリストリング。省略した場合は現在のURLのクエリストリング
//返り値・・・クエリストリングからパースされたオブジェクト
function parse_query(query)
{
    if(query==null)
    {
        query=(location.search+'').replace(/^[?&]/,'');
    }
    var separator1='=';
    var separator2='&';
    var data={};
    query=(query+'').replace(/^( |%20)*/g,'').split(separator2);
    for(var i=0;i<query.length;i++)
    {
        var tmp=query[i].split(separator1);
        if(tmp.length<2)
        {
            tmp=[tmp,''];
        }
        var key=url_decode(tmp.shift());
        if(key.length<=0)
        {
            continue;
        }
        data=merge(data,make_array(key,url_decode(tmp.join(separator1))));
    }
    return data;

    function url_decode(string)
    {
        return decodeURIComponent((string+'').replace(/\+/g,'%20'));
    }

    function make_array(key,value)
    {
        var tmp_key=[];
        var bracket_key='';
        var bracket=false;
        key=key.replace(/^ */g,'');
        if(key.length>0&&key.charAt(0)=='[')
        {
            key='';
        }
        for(var i=0;i<key.length;i++)
        {
            var char=key.charAt(i);
            if(!bracket&&char=='[')
            {
                if(bracket_key!='')
                {
                    tmp_key.push(bracket_key);
                    bracket_key='';
                }
                bracket=true;
                bracket_key+=char;
            }
            else if(char==']')
            {
                if(bracket)
                {
                    if(tmp_key==1)
                    {
                        tmp_key.push(bracket_key);
                    }
                    else
                    {
                        tmp_key.push(bracket_key.substr(1));
                    }
                    bracket_key='';
                    bracket=false;
                    if(key.charAt(i+1)!='[')
                    {
                        break;
                    }
                }
                else
                {
                    bracket_key+=char;
                }
            }
            else if(char=='\0')
            {
                break;
            }
            else
            {
                bracket_key+=char;
            }
        }

        if(bracket)
        {
            if(tmp_key.length==1)
            {
                tmp_key[tmp_key.length-1]+=bracket_key;
            }
        }
        else if(bracket_key!='')
        {
            tmp_key.push(bracket_key);
        }
        var count=0;
        if(tmp_key.length>0)
        {
            for(var i=0;i<tmp_key[0].length;i++)
            {
                var char=tmp_key[0].charAt(i);
                if(char==' '||char=='.'||char=='[')
                {
                    tmp_key[0]=tmp_key[0].substr(0,i)+'_'+tmp_key[0].substr(i+1);
                }
                //下の1文を変えれば$_GET準拠、現在はparse_str準拠
                //if(char=='['&&++count>0)
                if(char=='['&&count++>0)
                {
                    break;
                }
            }
        }
        for(var i=tmp_key.length-1;i>=0;i--)
        {
            var tmp_data={};
            tmp_data[tmp_key[i]]=value;
            value=tmp_data;
        }
        return value;
    }

    function merge(base,extend)
    {
        if(!is_object(base)||!is_object(extend))
        {
            return {};
        }
        extend=numbering(base,extend);
        for(var i in extend)
        {
            if(is_object(base[i])&&is_object(extend[i]))
            {
                base[i]=merge(base[i],extend[i]);
            }
            else
            {
                base[i]=extend[i];
            }
        }
        return base;
    }

    function numbering(base,extend)
    {
        var tmp={};
        for(var i in extend)
        {
            var index=(i.match(/^\s?$/)?max_index(base):i);
            if(is_object(extend[i]))
            {
                tmp[index]=numbering(is_object(base)?base[i]:null,extend[i]);
            }
            else
            {
                tmp[index]=extend[i];
            }
        }
        return tmp;
    }

    function max_index(data)
    {
        var max=0;
        if(!is_object(data))
        {
            return max;
        }
        for(var i in data)
        {
            if((i+'').match(/^(0|[1-9][0-9]*)$/)&&i>=max)
            {
                max=i-0+1;
            }
        }
        return max;
    }

    function is_object(data)
    {
        return data!=null&&typeof data=='object'&&Object.prototype.toString.call(data)!='[object Array]';
    }
}

とこんな感じ。
配列形式をサポートすると非常に面倒くさくなってくる。
第一引数にクエリストリングを渡す形式だが、省略すると現在のURLのクエリストリングをパースする。

余談だが、PHPのマニュアルには$_GETや$_POSTと同じ仕組でparse_strを動かしてあると書いているが、「a[b[c=d」というクエリの時に結果が違ってる。
地味に気になるw
この関数もどっちに準拠すべきか迷ったので、一応どっちにも切り替えられるようにしている。

この関数の逆の挙動をする関数も作ってみた。

[PHP][PDO][SQL] IN句のbindを簡単に作成する。

PHPからPDOでIN句を含むSQLを発行するとき
IN(?,?,?,?)やIN(:in_1,:in_2,:in_3)
と検索する個数分「?」なり「:in_1」なりを書かないといけない。
個数が固定であれば直書きでもいいが、checkboxから飛んできたデータなど、
不定であることが多い。
もちろんforやforeachなどループで回せば書くことはできるが少々面倒臭い。
そういう時は

//idに対してこういうデータで検索条件の値が飛んでくるとする
$data=array(2,7,14,23);

$sql='';
$where=array();

$sql.="SELECT * FROM table WHERE flag=?";
$where[]=$flag;
$sql.=" AND id IN(".substr(str_repeat(',?',count($data)),1).")";
$where=array_merge($where,$data);

$st=$pdo->prepare($sql);
$st->execute($where);

とするとループを書くことなくスマート?に記述することができる。
ざっくり解説すると
SQLを組み立てるところは、まず、データの個数をcount関数で取得し、
str_repeat関数で「,?」をデータの個数分作成する。
その状態だと「,?,?,?,?」と先頭に1個「,」が多い状態なので
それをsubstr関数で先頭1文字を切り出し、「?,?,?,?」の状態にする。
実データの方はarray_mergeを使うことでflagの検索条件の値を保持したまま
1つの配列にまとめている。
なお、$dataに何も入ってないとSQLエラーになる(IN()というふうに空のIN句になってしまうので)。
そのため、事前に$dataが空じゃないかのチェックをするなりしておいたほうがいいだろう。

ポイントとなる関数
  • str_repeat ・・・ 文字列を反復する
  • substr ・・・ 文字列の一部分を返す
  • array_merge ・・・ ひとつまたは複数の配列をマージする

2012年3月7日水曜日

[PHP][Excel][PHPExcel] セルに0埋め(0詰め)した値をセットする

PHPExcelにあるメソッドのsetCellValueやsetCellValueByColumnAndRowで0埋め(0詰め)で検索してきていた人がいたのでせっかくなのでコードを紹介してみる。

ちなみに普通にアプリケーションのExcelでもセルの書式設定から表示形式を文字列にしないと、「01」などと入力しても「1」に勝手に書き換えられてしまう。

PHPExcelも考え方は同じでセルの表示形式を文字列にしつつ値を入れるという方法になる。

//ClassesをおいたパスからPHPExcel.phpをインクルードする
require_once('Classes/PHPExcel.php');
//書き込むExcelのパス
define('EXCEL','data/test.xls');

//Excelのクラスを作る
$excel=new PHPExcel();
//アクティブなシートを取得する
$sheet=$excel->getActiveSheet();
//シートに名前をつける
$sheet->setTitle('sheet title');
//R1C1参照で値を入れる
$sheet->setCellValueExplicitByColumnAndRow(0,1,'01',PHPExcel_Cell_DataType::TYPE_STRING);
//A1参照で値を入れる
$sheet->setCellValueExplicit('A2','01',PHPExcel_Cell_DataType::TYPE_STRING);
//.xls形式で書き込むためのクラスを作る
//.xlsx形式を書き込む場合は'Excel5'の部分を'Excel2007'に変える →$writer=PHPExcel_IOFactory::createWriter($excel,'Excel2007');
$writer=PHPExcel_IOFactory::createWriter($excel,'Excel5');
//ファイルに書き込む
$writer->save(EXCEL);

とすることによって

ABC
101
2002
3

のようなデータを書き込むことが可能になる。

2012年3月6日火曜日

[PHP][Excel][PHPExcel] PHPExcelのgetNestingLevelエラー問題

PHPExcelを使ってExcelを読み込みたい時がある。
しかし、一定の条件によって
Fatal error: Call to a member function getNestingLevel() on a non-object in ~~~パス/Classes/PHPExcel/Reader/Excel5.php on line 842 
というエラーが表示され、先に進まないことがある。

どうも、これ自体はPHPExcelのバグのようでまだ修正されてないようだ。
ただ、パッチを作ってくれている人がいるようで、そのパッチを当てれば動くようになる。

PHPExcel - Patches

上記サイトのID:9136のところにそのパッチのダウンロードリンクがある。
そのパッチをExcel5.phpに当ててやれば、getNestingLevel問題は解決する。

[PHP][Excel][PHPExcel] PHPExcelの導入2

前回はPHPExcelからデータを読み込む方法をやったので、
今回はExcelにデータを書き込む方法をやってみる。
まずは単純なデータの書き込み。

//ClassesをおいたパスからPHPExcel.phpをインクルードする
require_once('Classes/PHPExcel.php');
//書き込むExcelのパス
define('EXCEL','data/test.xls');

//Excelのクラスを作る
$excel=new PHPExcel();
//アクティブなシートを取得する
$sheet=$excel->getActiveSheet();
//シートに名前をつける
$sheet->setTitle('sheet title');
//R1C1参照で値を入れる
$sheet->setCellValueByColumnAndRow(0,1,'test');
//A1参照で値を入れる
$sheet->setCellValue('B1','test2');
//数字を入れる
$sheet->setCellValue('A2','3');
//計算式を入れる
$sheet->setCellValue('B2','=A2*3');
//.xls形式で書き込むためのクラスを作る
//.xlsx形式を書き込む場合は'Excel5'の部分を'Excel2007'に変える →$writer=PHPExcel_IOFactory::createWriter($excel,'Excel2007');
$writer=PHPExcel_IOFactory::createWriter($excel,'Excel5');
//ファイルに書き込む
$writer->save(EXCEL);

とすると

ABC
1testtest2
239
3

というExcelファイルが作られる。

またスタイルの設定なども変えることが可能。

//ClassesをおいたパスからPHPExcel.phpをインクルードする
require_once('Classes/PHPExcel.php');
//書き込むExcelのパス
define('EXCEL','data/test.xls');

//Excelのクラスを作る
$excel=new PHPExcel();
//アクティブなシートを取得する
$sheet=$excel->getActiveSheet();
//シートに名前をつける
$sheet->setTitle('sheet title');
//A1参照で値を入れる
$sheet->setCellValue('A1','test1');
//フォントサイズ
$sheet->getDefaultStyle()->getFont()->setSize(10);
//フォント
$sheet->getDefaultStyle()->getFont()->setName('メイリオ');
//セルの下に罫線を引く
$sheet->getStyle('A1')->getBorders()->getBottom()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);
//センター寄せ
$sheet->getStyle('A1')->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
//文字色
$sheet->getStyle('A1')->getFont()->getColor()->setARGB('FFFF3333');
//背景色
$sheet->getStyleByColumnAndRow(0,1)->getFill()->setFillType(PHPExcel_Style_Fill::FILL_SOLID);
$sheet->getStyleByColumnAndRow(0,1)->getFill()->getStartColor()->setARGB('FFCCCCFF');

//.xls形式で書き込むためのクラスを作る
//.xlsx形式を書き込む場合は'Excel5'の部分を'Excel2007'に変える →$writer=PHPExcel_IOFactory::createWriter($excel,'Excel2007');
$writer=PHPExcel_IOFactory::createWriter($excel,'Excel5');
//ファイルに書き込む
$writer->save(EXCEL);

ABC
1test
2
3

という結果になる。

2012年3月2日金曜日

[PHP][Excel][PHPExcel] PHPExcelの導入

PHPからExcelを扱うというのは、意外と顧客からの要望に上がることが多い。

また、仕様書やデータ定義、DB定義などもExcelで書かれていると、
簡単にコピペでプログラムに持ってくることはできない。
だからといっていちいち手作業でDB定義書からCREATE文を作るなどというのも結構手間だ。
そういう時にPHPからExcelを読み取って、自動的にCREATE文を作るなりすると効率がいい。

まずは、PHPExcelの本家サイトからライブラリを落としてこよう。
落としてきたZIPを解凍すると、中にClassesフォルダがある。
この中に必要なクラスが定義されているファイルが入っているので、
これを必要な場所にフォルダごとおいておこう。

後はコードを書くだけ。
試しにExcel内の最初のシートの中身を配列に代入するプログラムを書いてみる。

ABC
1123
2456
3789

上記のような構造のExcelファイルを読み込むとする。

//ClassesをおいたパスからPHPExcel.phpをインクルードする
require_once('Classes/PHPExcel.php');
//読み込むExcelのパス
define('EXCEL','data/test.xls');

//.xls形式を読むという設定でクラスと作る
//.xlsx形式を読み込む場合は'Excel5'の部分を'Excel2007'に変える →$reader=PHPExcel_IOFactory::createReader('Excel2007');
$reader=PHPExcel_IOFactory::createReader('Excel5');
//読み込むExcelのパスを指定する
$excel=$reader->load(EXCEL);
//最初のシートを選ぶ
$sheet=$excel->setActiveSheetIndex(0);

$data=array();
//行イテレータ取得
foreach($sheet->getRowIterator() as $row)
{
    $tmp=array();
    //列イテレータ取得
    foreach($row->getCellIterator() as $cell)
    {
        //セルの値取得
        $tmp[]=$cell->getValue();
    }
    $data[]=$tmp;
}

//整形して出力
echo '<pre><code>';
var_dump($data);
echo '</code></pre>';

出力結果はこんな感じになる。
array(3) {
  [0]=>
  array(3) {
    [0]=>
    float(1)
    [1]=>
    float(2)
    [2]=>
    float(3)
  }
  [1]=>
  array(3) {
    [0]=>
    float(4)
    [1]=>
    float(5)
    [2]=>
    float(6)
  }
  [2]=>
  array(3) {
    [0]=>
    float(7)
    [1]=>
    float(8)
    [2]=>
    float(9)
  }
}

しかし、このままのソースだと例えば

ABC
113
25
378

のような虫食い状態のデータの時に下記のような結果になる。

array(3) {
  [0]=>
  array(2) {
    [0]=>
    float(1)
    [1]=>
    float(3)
  }
  [1]=>
  array(1) {
    [0]=>
    float(5)
  }
  [2]=>
  array(2) {
    [0]=>
    float(7)
    [1]=>
    float(8)
  }
}

上記プログラムだと空の場合は読み飛ばしてしまうため、
配列の添字が詰められてしまう。
データが開いてる時は配列も開けておきたい場合は

//ClassesをおいたパスからPHPExcel.phpをインクルードする
require_once('Classes/PHPExcel.php');
//読み込むExcelのパス
define('EXCEL','data/test.xls');

//.xls形式を読むという設定でクラスと作る
//.xlsx形式を読み込む場合は'Excel5'の部分を'Excel2007'に変える →$reader=PHPExcel_IOFactory::createReader('Excel2007');
$reader=PHPExcel_IOFactory::createReader('Excel5');
//読み込むExcelのパスを指定する
$excel=$reader->load(EXCEL);
//最初のシートを選ぶ
$sheet=$excel->setActiveSheetIndex(0);

$data=array();
//列の最大値を取得
for($i=1;$i<=$sheet->getHighestRow();$i++)
{
    $tmp=array();
    //行の最大値を取得
    //A、B、Cの文字列形式で帰ってくるので数字に変換する
    $max_column=PHPExcel_Cell::columnIndexFromString($sheet->getHighestColumn());
    for($j=0;$j<$max_column;$j++)
    {
        //列のインデックスと、行のインデックスからセルの値を取得
        $tmp[]=$sheet->getCellByColumnAndRow($j,$i)->getValue();
    }
    $data[]=$tmp;
}

//整形して出力
echo '<pre><code>';
var_dump($data);
echo '</code></pre>';

とすると出力結果は

array(3) {
  [0]=>
  array(3) {
    [0]=>
    float(1)
    [1]=>
    NULL
    [2]=>
    float(3)
  }
  [1]=>
  array(3) {
    [0]=>
    NULL
    [1]=>
    float(5)
    [2]=>
    NULL
  }
  [2]=>
  array(3) {
    [0]=>
    float(7)
    [1]=>
    float(8)
    [2]=>
    NULL
  }
}

となる。

次へ