開発メモ,主に補足by子連れ親父プログラマー

ラベル php の投稿を表示しています。 すべての投稿を表示
ラベル php の投稿を表示しています。 すべての投稿を表示

2012-09-14

mysqli(MySQL 改良版拡張モジュール)を使う場合のサンプルいろいろ

mysqli(MySQL 改良版拡張モジュール)を使う場合のサンプルいろいろ


いやあよもやね、mysql_real_escape_string とかが deprecated になる日がくるとは思わんかったね。
まずいでしょう、これは。まじで。

error_reporting(0);

にしとかないとあっという間にサーバーのログがパンクするんじゃないか・・・。

ま、それはともかく、mysqli に書き換えないといけない。
いや、そもそも mysqli でいいのか? PDO っつーのもあるぞ、って話なんだが。

俺は mysqli でいく。

まずは接続。

// データベース接続
$dbh = new mysqli("localhost", "username", "password", "db_name");
if ($dbh->connect_error) {
    die('Db Connect Error: '.$dbh->connect_errno.':'.$dbh->connect_error);
}
$dbh->set_charset('utf8');

id= で一個だけのデータをセレクトする。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $id);
    $id = '1';
    $sth->execute();
    $result = $sth->get_result();

    while ($row = $result->fetch_assoc()) {
        echo $row['id']."\n";
        echo $row['name']."\n";
    }
    $sth->close();
}

同じsqlをセレクトする値を変えて複数回実行する。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $id);
    $id_array = array('1', '2');
    foreach($id_array as $id) {
        $sth->execute();
        $result = $sth->get_result();

        while ($row = $result->fetch_assoc()) {
            foreach($row as $k => $v) {
                echo "$k $v";
            }
        }
    }
    $sth->close();
}

クエリの結果の行数を取得してなんかする。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->execute();
    $sth->store_result();
    if ($sth->num_rows == '3') {
        $sth->bind_result($id, $name);
        while ($sth->fetch()) {
            echo "$id, $name\n";
        }
        printf("Number of rows: %d.\n", $sth->num_rows);
    }
    $sth->free_result();
    $sth->close();
}


クエリでINを使う。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE id IN (?, ?)";
if (! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $list = array('1','3');
    $sth->bind_param('ii', $list[0], $list[1]);
    $sth->execute();
    $sth->store_result();
    $sth->bind_result($id, $name);
    while ($sth->fetch()) {
        echo "$id, $name\n";
    }
    $sth->free_result();
    $sth->close();
}

使えねーなこりゃ。

次、LIKE検索。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE name LIKE ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $param);
    $myname = "理";
    $param = "%".$myname."%";
    $sth->execute();
    $sth->store_result();
    $sth->bind_result($id, $name);
    while ($sth->fetch()) {
        echo "$id, $name\n";
    }
    $sth->free_result();
    $sth->close();
}

一行だけ更新。

$sth = $dbh->stmt_init();
$sql = "UPDATE users SET name = ? WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myid);
    $myname = "里香";
    $myid = "3";
    $sth->execute();
    printf("%d Row affected.\n", $sth->affected_rows);
    $sth->close();
}

ループで複数行更新。

$sth = $dbh->stmt_init();
$sql = "UPDATE users SET name = ? WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myid);
    $id_array = array('8'=>'佐藤', '2'=>'山田');
    foreach($id_array as $myid => $myname) {
        $sth->execute();
    }
    printf("%d Row affected.\n", $sth->affected_rows);
    $sth->close();
}

削除。

$sth = $dbh->stmt_init();
$sql = "DELETE FROM users WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $id);
    $id = "2";
    $sth->execute();
    printf("%d Row affected.\n", $sth->affected_rows);
    $sth->close();
}

インサートとインサートしたIDの取得。

$sth = $dbh->stmt_init();
$sql = "INSERT INTO users (name, email) VALUES (?, ?)";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myemail);
    $myname = "歌代";
    $myemail = "xxxx@xxxx.xxx";
    $sth->execute();

    // INSERTED_ID の取得
    printf("%d Inserted.\n", $dbh->insert_id);// こっち
    printf("%d Inserted.\n", $sth->insert_id);
    $sth->close();
}

インサート処理でエラー処理を付ける。

$sth = $dbh->stmt_init();
$sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('iss', $myid, $myname, $myemail);
    $myid = "2";
    $myname = "歌代";
    $myemail = "xxxx@xxxx.xxx";
    if ( ! $sth->execute()) {
        die('failed'.$sth->error);
    }
    $sth->close();
}

トランザクションで失敗したらロールバック。

$dbh->autocommit(FALSE);
$sth = $dbh->stmt_init();
$sql = "INSERT INTO myusers (name, email) VALUES (?, ?)";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myemail);
    $myname = "夢";
    $myemail = "xxxx@xxxx.xxx";
    if ( ! $sth->execute()) {
        $dbh->rollback();
    } else {
        $dbh->commit();
    }
    $sth->close();
}

うーん、、こんなとこかー。


2012-08-27

CodeIgniterで list() っつー名前のメソッド名を作るとエラーになる。

CodeIgniterで list() っつー名前のメソッド名を作るとエラーになる。

こんな感じで、

public function index()
 {
  $this->load->view('welcome_message');
 }

 public function list()
 {
  echo "ok";
 }

list() メソッドを追加すると・・・、

Parse error: syntax error, unexpected T_LIST, expecting T_STRING in /Users/xxx/Sites/codeigniter/application/controllers/welcome.php on line 25


なんでやねん!

ドラゴンクエストX 目覚めし五つの種族 オンライン(通常版)

2012-08-19

OS X Mountaion Lion で MacPorts を使って開発環境を整える(postgres編)


OS X Mountaion Lion で MacPorts を使って開発環境を整える(postgres編)


まずは、

$ sudo port install postgresql90

とすると終わり際に、

To use the postgresql server, install the postgresql90-server port

と出るので、引き続き、

$ sudo port install postgresql90-server

を実行。終わり際に、

###########################################################
# A startup item has been generated that will aid in
# starting postgresql90-server with launchd. It is disabled
# by default. Execute the following command to start it,
# and to cause it to launch at startup:
#
# sudo port load postgresql90-server
###########################################################

と、

To create a database instance, after install do
 sudo mkdir -p /opt/local/var/db/postgresql90/defaultdb
 sudo chown postgres:postgres /opt/local/var/db/postgresql90/defaultdb
 sudo su postgres -c '/opt/local/lib/postgresql90/bin/initdb -D /opt/local/var/db/postgresql90/defaultdb'

To tweak your DBMS, consider increasing kern.sysv.shmmax by adding an increased kern.sysv.shmmax .. to /etc/sysctl.conf

が出る。
順番にやっていく。

$ sudo port load postgresql90-server
$ sudo mkdir -p /opt/local/var/db/postgresql90/defaultdb
$ sudo chown postgres:postgres /opt/local/var/db/postgresql90/defaultdb
$ sudo su postgres -c '/opt/local/lib/postgresql90/bin/initdb -D /opt/local/var/db/postgresql90/defaultdb'

ここで、だーっと出る。

The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale ja_JP.UTF-8.
The default database encoding has accordingly been set to UTF8.
initdb: could not find suitable text search configuration for locale ja_JP.UTF-8
The default text search configuration will be set to "simple".

fixing permissions on existing directory /opt/local/var/db/postgresql90/defaultdb ... ok
creating subdirectories ... ok
selecting default max_connections ... 20
selecting default shared_buffers ... 2400kB
creating configuration files ... ok
creating template1 database in /opt/local/var/db/postgresql90/defaultdb/base/1 ... ok
initializing pg_authid ... ok
initializing dependencies ... ok
creating system views ... ok
loading system objects' descriptions ... ok
creating conversions ... ok
creating dictionaries ... ok
setting privileges on built-in objects ... ok
creating information schema ... ok
loading PL/pgSQL server-side language ... ok
vacuuming database template1 ... ok
copying template1 to template0 ... ok
copying template1 to postgres ... ok

WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the -A option the
next time you run initdb.

Success. You can now start the database server using:

    /opt/local/lib/postgresql90/bin/postgres -D /opt/local/var/db/postgresql90/defaultdb
or
    /opt/local/lib/postgresql90/bin/pg_ctl -D /opt/local/var/db/postgresql90/defaultdb -l logfile start

最後のやつをやってみる。

$ /opt/local/lib/postgresql90/bin/postgres -D /opt/local/var/db/postgresql90/defaultdb

エラーになる。

postgres cannot access the server configuration file "/opt/local/var/db/postgresql90/defaultdb/postgresql.conf": Permission denied

パーミッションの問題か。
sudo su postgres して再トライ。

$ sudo su postgres
$ /opt/local/lib/postgresql90/bin/postgres -D /opt/local/var/db/postgresql90/defaultdb
LOG:  database system was shut down at 2012-08-11 04:10:00 JST
LOG:  autovacuum launcher started
LOG:  database system is ready to accept connections

動いたっぽい。

$ /opt/local/lib/postgresql90/bin/psql -l -U postgres                              List of databases
   Name    |  Owner   | Encoding | Collation | Ctype |   Access privileges  
-----------+----------+----------+-----------+-------+-----------------------
 postgres  | postgres | UTF8     | C         | C     |
 template0 | postgres | UTF8     | C         | C     | =c/postgres          +
           |          |          |           |       | postgres=CTc/postgres
 template1 | postgres | UTF8     | C         | C     | =c/postgres          +
           |          |          |           |       | postgres=CTc/postgres
(3 rows)

よさそうだ。
パスを通しておく。

$ vim .profile

export PATH=/opt/local/lib/postgresql90/bin:$PATH

テスト用DBを作っておく。

$ createdb sample_db -U postgres -E UTF-8

psqlして、

$ psql sample_db -U postgres

テーブルを作る。

sample_db=# CREATE TABLE users (
sample_db(# id SERIAL PRIMARY KEY,
sample_db(# name varchar(255)
sample_db(# );
NOTICE:  CREATE TABLE will create implicit sequence "users_id_seq" for serial column "users.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "users_pkey" for table "users"
CREATE TABLE

確認。

sample_db=# \d
              List of relations
 Schema |     Name     |   Type   |  Owner  
--------+--------------+----------+----------
 public | users        | table    | postgres
 public | users_id_seq | sequence | postgres
(2 rows)

sample_db=# \d users
                                 Table "public.users"
 Column |          Type          |                     Modifiers                     
--------+------------------------+----------------------------------------------------
 id     | integer                | not null default nextval('users_id_seq'::regclass)
 name   | character varying(255) |
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)

データを入れておく。

sample_db=# INSERT INTO users (name) VALUES ('yamada');
INSERT 0 1

セレクトして確認。

sample_db=# select * from users;
 id |  name 
----+--------
  1 | yamada
(1 row)

再びportでインストール

$ sudo port install php5-postgresql

アパッチ再起動

$ sudo /opt/local/apache2/bin/apachectl restart

infophp()で確認。

簡単なphpを書いて接続テスト。

<?php
$conn = pg_pconnect("dbname=sample_db user=postgres");
if (!$conn) {
  echo "An error occured.\n";
  exit;
}

$result = pg_query($conn, "SELECT * FROM users");
if (!$result) {
  echo "An error occured.\n";
  exit;
}

while ($row = pg_fetch_row($result)) {
  echo "Id: $row[0]  Name: $row[1]";
  echo "<br />\n";
}

表示される。

Id: 1 Name: yamada

よさそうだ。

2012-08-10

OS X Mountaion Lion で MacPorts を使って開発環境を整える(apache, PHP編)

OS X Mountaion Lion で MacPorts を使って開発環境を整える(apache, PHP編)


という訳で無事 Mountaion Lion のクリーンインストールも完了し、開発環境を再構築しようってことで四苦八苦。
まずはMacPortsのこのページ


http://www.macports.org/install.php


から Mountaion Lion のリンクをクリックしてダウンロード。
MacPorts をインストール。
それから Apple の開発者ページ


https://developer.apple.com/downloads/index.action


へ行って Command Line Tools (OS X Mountain Lion) for Xcode をダウンロードしてインストール。
(実際にはここでログインが必要で、なんだっけな、iCloudのアカウントで入ってメールアドレスのVerifyがあって、そんでやっとダウンロードできる)


そんで、まずはアパッチ。


$ sudo port install apache2

終わり際に


###########################################################
# A startup item has been generated that will aid in
# starting apache2 with launchd. It is disabled
# by default. Execute the following command to start it,
# and to cause it to launch at startup:
#
# sudo port load apache2
###########################################################

とかって出るので、以下を実行。


$ sudo port load apache2
$ port installed

として確認してみると、アパッチと一緒に


openssl @1.0.1c_0 (active)
 perl5 @5.12.4_0+perl5_12 (active)
 perl5.12 @5.12.4_1 (active)
 sqlite3 @3.7.13_0 (active)

この辺もインストールされているのが分かる。
で、次はphp。


$ sudo port install php5 +apache2 +pear

こちらも終わり間際で


To customize php, copy
/opt/local/etc/php5/php.ini-development (if this is a development server) or
/opt/local/etc/php5/php.ini-production (if this is a production server) to
/opt/local/etc/php5/php.ini and then make changes.

と表示されるので、


$ sudo cp /opt/local/etc/php5/php.ini-development /opt/local/etc/php5/php.ini

とする。

ついでにphp.iniを編集して、


$ sudo vim /opt/local/etc/php5/php.ini

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = Asia/Tokyo 

とタイムゾーンの設定を入れておく。
引き続き、apacheのhttpd.conf を編集。


$ sudo vim /opt/local/apache2/conf/httpd.conf

として、


# Virtual hosts
Include conf/extra/httpd-vhosts.conf

この部分のコメントアウトを外す。
さらに、


AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz

となっているあたりに以下を追加。


AddType application/x-httpd-php .php

引き続き


$ sudo vim /opt/local/apache2/conf/extra/httpd-vhosts.conf

を編集。下の方を全部消して、以下のように変更


<VirtualHost *:80>
   DocumentRoot "/opt/local/apache2/htdocs"
   ServerName localhost
 </VirtualHost>
          
 <VirtualHost *:80>
     DocumentRoot "/Users/xxxx/htdocs"
     ServerName vhost
     <Directory "/Users/xxxx/htdocs">
       order deny,allow
       allow from All
       Options All
       AllowOverride All
     </Directory>
</VirtualHost>

自分のユーザーフォルダーの直下にhtdocsを作っておく。


$ mkdir /Users/xxxx/htdocs

その中に、適当な index.html を入れておく。
ついでに


<?php
phpinfo();

とだけ書いてあるinfo.phpファイルを作って入れておく。

あと、


$ sudo vim /private/etc/hosts

として


127.0.0.1   vhost

を追加。


$ sudo /opt/local/apache2/bin/apachectl restart

でアパッチ再起動。ブラウザで見てみる。


http://vhost/
http://vhost/info.php

OKなら mbstring とかも入れる。


$ sudo port install php5-mbstring
$ sudo /opt/local/apache2/bin/apachectl restart

とかやって。phpinfo() でちゃんと反映されているか確認する。
ここまでで一週間くらいかかった。(笑)
先は長いって。



2012-07-08

一つ目を選択したら二つ目のセレクトの値が変化するようにしたいんだが、ソースの文字コードがEUCでjqueryのload()を使って読み込むと文字化けしてしまう。


一つ目を選択したら二つ目のセレクトの値が変化するようにしたいんだが、ソースの文字コードがEUCでjqueryのload()を使って読み込むと文字化けしてしまう。



文字コードがEUCなのは昔のソースなのでしょうがない。
いまさら直しようがない。
jquery の load() だと化けるってのもしょうがないだろう。調べるだけ無駄だ。
ということで、php側で一旦データを全部読み込んで、それをJSON形式のデータにして、直接javascriptのスクリプト部分に埋め込むことにしよう。

データのイメージ:
一つ目のセレクトがこんな感じであって、選択すると、

<select name="category_id">
    <option value="0">爬虫類</option>
    <option value="1">鳥類</option>
    <option value="2">昆虫option>
</select>
こんなようなデータが読み込まれて二つ目のセレクトが生成される。(鳥類だったら"1"のデータがセレクトになる)

var per_array ='{
    "0":{
        "1":"トカゲ",
        "2":"ワニ",
        "3":"ザリガニ",
    },
    "1":{
        "4":"スズメ",
        "5":"ハト",
        "6":"トンボ"
    },
    "2":{
        "7":"コオロギ",
        "8":"バッタ"
    }
}';

データベースにはこんな感じで入っている。

SELECT * FROM ikimono;
 id | category_id |   name   
----+-------------+----------
  1 |           0 | トカゲ
  2 |           0 | ワニ
  3 |           0 | ザリガニ
  4 |           1 | スズメ
  5 |           1 | ハト
  6 |           1 | トンボ
  7 |           2 | コオロギ
  8 |           2 | バッタ
(8 rows)

まず、データベースから値を読み込んで、JSON形式のデータを作成する。

$query = "SELECT id, name, category_id FROM ikimono";
$result = pg_query($pg, $query);
$n = pg_numrows($result);
if ($n){
    for ($i=0;$i<$n;$i++){
        $id = pg_result($result, $i, 0);
        $name = pg_result($result, $i, 1);
        $category_id = pg_result($result, $i, 2);
        // ここで一個一個配列に入れておいて
        $select_array[$category_id] .= '"'.$id.'":"'.$name.'",';
    }
}
// ここでカテゴリー別に {} で囲む
foreach($select_array as $k => $v) {
    $v = preg_replace('/,$/', '', $v);
    $ikimono_data .= '"' . $k . '":{' . $v . '},';
}
$ikimono_data = preg_replace('/,$/', '', $ikimono_data);// 最後のカンマを削除

で、ページ表示部分のjavascript部分をこんな感じで。
配列の定義のところに直接phpでデータを埋め込む。


<script>
$(document).ready(function(){
    $('select[name=category_id]').change(function(){
        var category_id = $(this).val();
        var ikimono_array ='{<?php echo $ikimono_data; ?>}';
        var obj = jQuery.parseJSON(ikimono_array)[category_id];
        var ikimono_select = '';
        for (key in obj) {
            ikimono_select += '<option value="'+key+'">' + obj[key] + "</option>";
        }
        $('select[name=ikimono_id]').empty().append(ikimono_select);
    }).change();
});
</script>

二個目のセレクトは空っぽで用意しておく。

<select name="ikimono_id">
</select>

もっと他にいい方法がありそうな予感が大いににするが、とりあえずこんなところで。

2011-08-05

外部のサイトに置いたphpからのjsonデータをjqueryで読み込む

簡単にできると思って実装してしまって、実際に外部のサーバーに設置してから、ありゃ動かないわ、
あ〜そうだった、Javascript って外部のサーバーのファイル読めないんだっけ、と気づいていつもあきらめていたんだが。
これを ajax の crossdomain と言うらしい。
検索すると jquery のプラグインとかで沢山ヒットするが、jquery のドキュメントを見ると1.5以降 jquery 単体でも実現できるようになっている。
ということで試してみた。
まず javascript 側は、
$.ajax({
    url: "http://xxxxxxxxxxxxxx/get_json.php",
    data: "name=123",
    dataType: 'JSONP',
    async: false,
    crossDomain: true,
    success: function(data){
        console.log(data);
    }
こんな感じ。dataType を JSONP にするのがミソだ。データは必ず get で送られるので
type: "POST"
とか書いても意味はない。
で、php 側は
echo $_GET['callback'].'({"error":"no data"})';
こんな感じ。
普通の json 形式データを()で囲んで、その前に
$_GET['callback']
をくっつける。

2011-08-01

JSON形式のつづき。DBからselectしたデータが連想配列になってるのでそのまま処理したい編。

なにはともあれ、DBからselectしたデータがある訳です。
で、selectしたデータのid列の値がキーになっているとなにかと便利な訳ですよ。
後から参照する時にidだけ分かれば一発ですから。
ということでデータが今phpでこんなになってるとします。
Array
(
    [4] => Array
        (
            [id] => 4
            [year] => 2012
            [created] => 2011-07-26 19:37:37.985709
        )

    [7] => Array
        (
            [id] => 7
            [year] => 2011
            [created] => 2011-07-27 16:43:11.697075
        )
)
こいつをJSON形式で出力したいってことです。
最終的にこんな形になればいいので、
{
    "4": {
        "id": "4",
        "year": "2012",
        "created": "2011-07-26 19:37:37.985709"
    },
    "7": {
        "id": "7",
        "year": "2011",
        "created": "2011-07-27 16:43:11.697075"
    }
}
昨日のファンクションを以下のように修正。
$json = "{".toJson($data)."}";

function toJson($data) {   
    $json = array();
    foreach ($data as $string => $value) {
        $key = "\"$string\":";

        if (is_array($value)) {
            $json[] = sprintf("%s {%s}", $key, toJson($value));
        } else {
            $json[] = sprintf("%s \"%s\"", $key, addslashes($value));
        }
    }
    return implode(", ", $json);
}
こうすると、例えば返って来た json を javascript で例えば data とかいうオブジェクトに入れたとしたら、
data[4].year
とかで処理できる訳です。
    var tbl='';
    for (key in data) {
        tbl += '<tr><td>'+data[key].year+'</td></tr>";
    }
みたいにね。

2011-07-29

JSON形式の過去に一体何があったのか?

それにしても分からないのはJSON形式だ。
今phpで配列からJSON形式のデータを作ろうってことになったら、普通にphp5.2以上を使っている人たちは、
普通に json_encode を使うんだろう。悩む必要はない。
それ以外のちょっと前の環境の人だって、 pear が使えればそれでやっちゃってるんだろう。
だから世の中の人達は本当に正しいJSON形式なんてものに興味も関心もないに違いない。
それが自動的に作ってくれるんだから。
で、pearもねぇ、phpはいまだに4.3という、絶望のどん底のような環境にいる私は仕方なく自分でJSON形式を作ろうとすることになるんだが、
一昔前までは、
{ とか [ とか "" とか '' とか結構いい加減でもJSONとして動いていたらしいフシがある。
jQuery のプラグインです、なんつって配布されてるものでも jQuery1.2 くらいの時代のものだと、 key を""で囲っていなかったりして、これを最新の jQuery で動かすとエラーになる。
つまり、その時代に結構いい加減に作られていたJSON形式の一体どこがいけないのか、自分で分かってなきゃ作れないってことなんだ。
ということでJSONとはこれだ! というのがこのページらしいんだが。。。
http://json.org/
さっぱりわからん(笑)。
おそらくしかしこれは、配列は [] で囲め、 [] の中にはなにがしかの要素が入る。
オブジェクトは {} で囲め、その中にはキーと値をコロンでつないだものが入る、複数入れるならカンマで句切れ。
っていうようなことなんだろう。
ということでこちらの
http://d.hatena.ne.jp/kovaio/20060610
2006年の古い記事の関数を書き換えてみました。
$json = "[".toJson($data)."]";

function toJson($data) {   
 $json = array();
 foreach ($data as $string => $value) {
  $key = (is_numeric($string) ? "" : "\"$string\":");

  if (is_array($value)) {
   $json[] = sprintf("%s {%s}", $key, toJson($value));
  } else {
   $json[] = sprintf("%s \"%s\"", $key, addslashes($value));
  }
 }
 return implode(", ", $json);
}
JSON形式に何があったのか知らないが、こんだけ絶望的に変わってるのに、なんで誰も憤慨していないのかが不思議だ。

2011-07-08

本当に phpunit なんて要るのか? pear なんて大っきらい編

本当に要る。ということなので、インストールを試みる。

PHPUnit Manual
第3章 PHPUnit のインストール
http://www.phpunit.de/manual/3.6/ja/installation.html

とりあえずこちらに書いてある通り
以下の3つをチャンネルに追加。

pear channel-discover pear.phpunit.de
pear channel-discover components.ez.no
pear channel-discover pear.symfony-project.com
sudo でないとエラーになったのでこうなった。
$ sudo pear channel-discover pear.phpunit.de
Adding Channel "pear.phpunit.de" succeeded
Discovery of channel "pear.phpunit.de" succeeded

$ sudo pear channel-discover components.ez.no
Adding Channel "components.ez.no" succeeded
Discovery of channel "components.ez.no" succeeded

$ sudo pear channel-discover pear.symfony-project.com
Adding Channel "pear.symfony-project.com" succeeded
Discovery of channel "pear.symfony-project.com" succeeded
自分のチャンネルのリストはこうやってみる。
$ pear list-channels
Registered Channels:
====================
Channel                  Alias   Summary
components.ez.no         ezc     eZ Enterprise components
doc.php.net              phpdocs PHP Documentation Team
pear.php.net             pear    PHP Extension and Application
                                 Repository
pear.phpunit.de          phpunit PHPUnit PEAR Channel
pear.symfony-project.com symfony The symfony project PEAR channel
pecl.php.net             pecl    PHP Extension Community Library
__uri                    __uri   Pseudo-channel for static packages
で、いざ、
$ sudo pear install phpunit/PHPUnit
を実行してみると
Failed to download pear/HTTP_Request2 within preferred state "stable", 
latest release is version 2.0.0RC1, stability "beta", 
use "channel://pear.php.net/HTTP_Request2-2.0.0RC1" to install
というようなエラーが出る。
この use "channel://pear.php.net/HTTP_Request2-2.0.0RC1" to install
という一文が意味がわからなかったのだが、これはつまり、
$ sudo pear install channel://pear.php.net/HTTP_Request2-2.0.0RC1
こうしろってことらしい(笑)。
そうならそう書いてくれよって。

ということで、結局、以下の順に実行して無事終了。
$ sudo pear install channel://pear.php.net/Net_URL2-0.3.1
$ sudo pear install channel://pear.php.net/HTTP_Request2-2.0.0RC1
$ sudo pear install phpunit/PHPUnit
$ phpunit
PHPUnit 3.5.14 by Sebastian Bergmann.

2011-01-25

OSXのMacPortsでphpのバージョンが5.3.5になり、なにもかもおかしくなった件

前の記事を見たら2010/3/18ってなってたからおよそ一年近くほったらかしになっていたことになるが。
1年前に自宅のOSXの環境にMacPortsでphpを入れて、その時は5.3.2だったんだが、
それで昨日そいつを更新しようと思い立ち、
sudo port upgrade installed
とやったが最後、とんでもなく延々と待たされた挙げ句に
 php5 @5.3.2_0+apache2+macosx+mysql5+pear+sockets+sqlite
 php5 @5.3.5_0+apache2 (active)
と2つのphp5が存在している状態になり、もう訳が分からない(涙)。
なんなんすか、これ。
phpのバージョンを5.3.2から5.3.5にするんだったら、
 php5 @5.3.5_0+apache2+macosx+mysql5+pear+sockets+sqlite
こうなるのが普通じゃないか?!
おかしい、これは絶対おかしい。
ということで
sudo port uninstall -f php5 @5.3.5_0+apache2
とやって消してみた。
消してからふと、そういえばmbstringが使えなかったような気がしたナーと思ったので
ついうっかり、
sudo port install php5-mbstring
とやってしまった。すると、
 php5 @5.3.2_0+apache2+macosx+mysql5+pear+sockets+sqlite
 php5 @5.3.5_0+apache2 (active)
げげーっつまたしても5.3.5が復活している!!
さすがにここまでやって気がついた。
恐らくこれは5.3.2と5.3.5とでは、もう、違うってことだ。
これからはもう絶対に
php5 @5.3.5_0+apache2+macosx+mysql5+pear+sockets+sqlite
こういう風にはならないってことだ。
結果、
php5 @5.3.5_0+apache2 (active)
php5-mbstring @5.3.5_0 (active)
php5-mysql @5.3.5_0+mysqlnd (active)
こういう風になる。
ついでに泣けるのがこのmbstring入れる時に出るメッセージ
Your php.ini contains a line that will prevent php5-mbstring
and other PHP extensions from working. To fix this,
edit /opt/local/etc/php5/php.ini and delete this line:

extension_dir = "./"
この行を消せってのは
extension_dir = "./"
のことを指している(笑)。

2010-09-15

jQuery で table をいじるときは tbody を忘れない

まぁ普通に会員の一覧検索みたいな管理画面を作っていて、普通に table でタグが組んであって、それを項目名をクリックしたらその項目名の昇順で並び替えたいとか、そんなような作業ってのは結構あるわけで。
なんかこう、テーブルのそのデータ行の部分だけを Ajax で load するようにしたいなと。
思ったわけです。

で、テーブルは普通に、こんな感じであって、
<table id="result">
  <tr>
    <th>ID</th>
    <th>氏名</th>
    <th>メールアドレス</th>
  </tr>
  <tr>
    <td>1</td>
    <td>山田</td>
    <td>yamada@・・・.jp</td>
  </tr>
  <tr>
    <td>2</td>
    <td>佐藤</td>
    <td>sato@・・・.jp</td>
  </tr>
</table>
このデータ行の部分だけを Ajax で書き換えたいってことなんですが、どうすればいいですかね?
テーブルの2行目以降を一旦 remove() とかで消して、そこに append() すりゃいいんじゃないかとか、いろいろ悩んでみたんだけど、結局、tbodyを使うってことで解決しました。
先のテーブルを以下のように書き直します。
<table id="result">
  <thead>
    <tr>
      <th>ID</th>
      <th>氏名</th>
      <th>メールアドレス</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>山田</td>
      <td>yamada@・・・.jp</td>
    </tr>
    <tr>
      <td>2</td>
      <td>佐藤</td>
      <td>sato@・・・.jp</td>
    </tr>
  </tbody>
</table>
そして、
$(document).ready(function() {
  $('#result tbody').load(
    './result.php',
    $('form').serialize(),
    function(data){}
  );
});
と jQuery で書きます。
result.php にはページ中のフォームの値がGETで渡っていくので、それを適当に処理してクエリーをかけて結果を元に
    <tr>
      <td>1</td>
      <td>山田</td>
      <td>yamada@・・・.jp</td>
    </tr>
    <tr>
      <td>2</td>
      <td>佐藤</td>
      <td>sato@・・・.jp</td>
    </tr>
の部分だけを出力してやればよいです。

2010-09-11

jQuery、Form Plugin を使って、file upload する、ファイルを選択したら自動でその画像が表示されるようにする

まあ、とにかくファイルアップロード系のプラグインってのはあるようでないようで、これというのがない。
もう探し疲れたよ、ほんとに。
ファイルのアップロードのインターフェースはこういうのがいいんだ、っていう部分で人によって意見が違いすぎるんだよな、結局。
だから作者によって考えてることが全然別々で。
ということで、一番シンプルで、機能がなくて、ただアップロードできるだけ、というものを考えてみた。
まず、HTML側はこうする。
<form id="upload" method="post" action="form.php" >
  <input type="file" name="sample" id="sample" />
  <img id="sample_img" src="img/sample.jpg">
</form>
sample.jpg はデフォルトの画像で用意してあって、最初から表示されているんだけど、ファイル参照で別の画像を選べばそれに置き換わる、というようなものを作ろうってことだ。

まず、
http://www.malsup.com/jquery/form/
こちらから Form Plugin を入手して、読み込む。
<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="js/jquery.form.js"></script>
で、ファイル選択したらファイルアップロードをする php に処理を渡せばいいので、
$(document).ready(function() {
  $("input#sample").change(function(){
    var options = { 
      url:       'upload.php',
      dataType:  'xml', 
      success:   processXml 
    }; 
    $('form#upload').ajaxSubmit(options); 
  });
});
こんな感じに書く。中味は全部 form プラグインの記述。dataType で xml を選ぶのが味噌だ。
json にすると動かないので注意。
ajaxSubmit で form の action 先を upload.php に変更して、自動でサブミットしてくれる。

ということなので upload.php を書く。
いや、これはまあ、普通に $_FILES を使ったファイルアップロード処理を php で書けばいいだけなので詳細は略。
普段自分が使ってるやつでやってくれ。
なんやかんや処理した後に、
header('Content-type: text/xml'); 
echo <<<END
<root>
  <status>$status</status>
  <file_path>$file_path</file_path>
  <emes>$emes</emes>
</root>
END;
というように xml で結果を返す。上記では $status に成功したかどうか、$file_path にアップロードした画像を保存したファイルのパス、$emes にエラーメッセージ、を格納しているつもりである。

で、アップロードに成功すると
success:   processXml 
とあるように、processXml に処理が渡るので、processXml をごりごり書く。
function processXml(responseXML) {
  var status = $('status', responseXML).text();
  if (status == 'success') {
    var file_path = $('file_path', responseXML).text(); 
    $('#sample_img').attr('src', file_path);
  } else {
    var emes = $('emes', responseXML).text();
    alert(emes);
  }
}
xml のデータは
$('status', responseXML).text()
というような書き方で取得できる。
ので、jQuery の .attr を使って画像のパスを書き換える。
$('#sample_img').attr('src', file_path);
これでオッケーだ。
あとは好きなようにいじればよしと。

2010-08-11

phpとMySQLで、住所から郵便番号を検索する(逆引き)

大昔に会社のホームページ用に用意した原稿だけど、このまま埋もれてるのも何なのでこちらに載せておきます。

今回は、リアルな住所から郵便番号を検索するプログラムについて考えてみることにします。
例えば「札幌市清田区美しが丘一条N丁目M-1」というような、都道府県の部分だけを除いてあとは全部ひとつつながりの住所があるとする。
これがまあ、15万件くらいですかね、それくらいの件数の住所データを渡されて、「これの郵便番号を出せ」と、そういう指令が出たとする訳です。
さあどうしようかと。
話を簡単にするために、その住所データは以下のようなMySQLデータベースのテーブルに入っているとします。
CREATE TABLE users (
    id int(11) NOT NULL auto_increment,
    name varchar(100),
    address varchar(255),
    zip varchar(7),
    PRIMARY KEY (id)
)DEFAULT CHARSET=utf8;
住所はaddressのカラムで、郵便番号はzipですが、今はzipには何も入っていません。

で、まず考えるのは

日本郵便 http://www.post.japanpost.jp/zipcode/index.html

のページへ行って、郵便番号のデータをもらってくることです。ここに「郵便番号データのダウンロード」というリンクがありますのでクリックして、「全国一括」というやつをダウンロードします。
問題はその中味なのですが、詳しくは

郵便番号データの説明 http://www.post.japanpost.jp/zipcode/dl/readme.html

を読んでもらうことにして、この中で使えそうなのは8番目の市区町村名と9番目の町域名、そしてももちろん3番目の郵便番号(7桁)になりそうです。
その3つだけをインポートしてもよいのですが、それも面倒なので、せっかくのCSV形式のファイルですからそのままMySQLのLOAD DATAを使って一気に入れることにします。
最初に、以下のようなテーブルを作っておきます。
CREATE TABLE zips (
    zkd_code VARCHAR(255),
    kyu_zip VARCHAR(255),
    zip VARCHAR(255),
    ken_yomi VARCHAR(255),
    siku_yomi VARCHAR(255),
    cyou_yomi VARCHAR(255),
    ken VARCHAR(255),
    siku VARCHAR(255),
    cyou VARCHAR(255),
    fg_a INT,
    fg_b INT,
    fg_c INT,
    fg_d INT,
    fg_e INT,
    fg_f INT
);
次に、上記日本郵便のサイトでダウンロードした郵便番号データを適当なファイル名で(ここではyubin.csv)、サーバーに保存して、MySQLのコンソール画面から、
mysql> LOAD DATA LOCAL INFILE "/home/myhome/yubin.csv" INTO TABLE zips FIELDS TERMINATED BY ',' ENCLOSED BY '"';
と、やります。
これ一発で郵便番号データがzipsテーブルに入ります。

以上でデータは揃いましたので、usersテーブルの
+-------+--------------------------------------------------+
| id    | address                                          |
+-------+--------------------------------------------------+
|     1 | 札幌市清田区美しが丘一条N丁目M-1             |
+-------+--------------------------------------------------+
というデータと、zipsテーブルの
+---------+--------------------+--------------------+
| zip     | siku               | cyou               |
+---------+--------------------+--------------------+
| 0040811 | 札幌市清田区       | 美しが丘一条       |
| 0040813 | 札幌市清田区       | 美しが丘三条       |
| 0040812 | 札幌市清田区       | 美しが丘二条       |
| 0040815 | 札幌市清田区       | 美しが丘五条       |
| 0040814 | 札幌市清田区       | 美しが丘四条       |
+---------+--------------------+--------------------+
というようなデータを見比べて、郵便番号を照合するプログラムを組めばよいわけです。

zipsの方は市区町村名と町域名が別々のカラムになっていますので、 CONCAT を使って合体させて処理します。
一方リアルの住所には番地とか何丁目とかいうのが最後の方についていますので、それをどんどん削除していって、それが、 CONCAT で合体した市区町村名と町域名にマッチしたらその時の郵便番号を取得すればよい、という訳です。
文字を後ろから削除するには LEFT を使います。
SELECT 
    zip, siku, cyou 
FROM 
    zips
WHERE 
    CONCAT(siku, cyou) = LEFT('札幌市清田区美しが丘一条N丁目M-1', 10)
このクエリーはヒットしませんが、 LEFT の最後の数字を1ずつ増やして消していけば、いつかはヒットするはずです。

ということで、まずは対象となる住所データとその文字列長さを users テーブルから取得します。
$query = "SELECT address, CHAR_LENGTH(address) as len FROM users";
$result = mysql_query($query);
if ($result) {
    $n = mysql_num_rows($result);
    if ($n){
        while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
            for($i=$row['len'];$i>0;$i--){
                // ここに郵便番号テーブルとの照合を入れる
            }
        }
    }
}
forループを使って、最初に数えた文字列長さから始めて1文字すつ削っていこうという作戦です。
そして forループの中に以下の照合処理を入れます。
$query2 = "SELECT zip FROM zips WHERE CONCAT(siku, cyou) = LEFT('$address', $i)";
$result2 = mysql_query($query2);
if ($result2) {
    $n2 = mysql_num_rows($result2);
    if ($n2){
        while ($row2 = mysql_fetch_array($result2, MYSQL_ASSOC)) {
            echo $row2['zip'] . "\t";
        }
        break;
    }
}
breakを使って、マッチした時点で forループを抜けます。

で、これでテストしてみるとすぐに分かりますが、処理が大変に遅いです。私の試算では15万件のデータを処理するのに、18時間かかると出ました(笑)。
これはおそらく CONCAT でカラムを合体させてるのが原因でしょう。
ということで、市区町村名と町域名をバラバラにしておく意味はないので、カラムとして合体させます。
ALTER TABLE zips ADD COLUMN sikucyou VARCHAR(52);
UPDATE zips SET sikucyou=CONCAT(siku, cyou);
最初に新しいカラムを追加して、UPDATE をかけています。
ついでにインデックスも追加しておきます。
ALTER TABLE zips ADD INDEX (sikucyou);
PHPソース内のSQLの方も修正します。
SQLの CHAR_LENGTH もめためたに遅いのでPHP側での処理に変更します。
$query = "SELECT id, address FROM users";
$result = mysql_query($query);
if ($result) {
    $n = mysql_num_rows($result);
    if ($n){
        while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
            $address = mysql_real_escape_string($row['address']);
            $len = mb_strlen($row['address']);
            $zip = '';
            for($i=$len;$i>0;$i--){
                $query2 = "SELECT zip FROM zips WHERE sikucyou = LEFT('$address', $i)";
                $result2 = mysql_query($query2);
                if ($result2) {
                    $n2 = mysql_num_rows($result2);
                    if ($n2){
                        while ($row2 = mysql_fetch_array($result2, MYSQL_ASSOC)) {
                            $zip = $row2['zip'];
                        }
                        break;
                    }
                }
            }
            $query3 = "UPDATE users SET zip='$zip' where id='{$row['id']}'";
            $result3 = mysql_query($query3);
        }
    }
}

実行してみましょう。私の環境では15万件の住所の照合は、2分で終わりました。
問題はこの方法でやった郵便番号が本当に全部合っているのか、ということですが、全体の約14%は郵便番号を照合することができません。
リアルの住所自体には大字(おおあざ)などの表記のゆれがあるのと、それと、この方法では町域が「以下に掲載がない場合」の判別ができないからです。
照合できなかった住所でGoogle郵便番号検索をしてみましょう。
これはちゃんとヒットしますが、
郵便番号 渋谷区桜丘町
これはヒットしません。
郵便番号 千代田区丸の内
Google先生でも無理なんです。

2010-07-26

phpでカンマ区切りのcsvを出力する

とりあえず、データの中に改行は含まれない、という前提にしてしまえば比較的楽なのではないかと。 いうことで、暫定版ですが。。
function add_comma($str) {
    if (preg_match('/"/', $str)) {
        $str = preg_replace('/"/', '""', $str);
    }
    if (preg_match('/[,"]/', $str)) {
        $str = '"' . $str . '"';
    }
    return $str;
}

2010-06-25

smartyの{section}関数に渡す配列は0から順番になってないと表示されない

smartyの{section}関数に渡す配列は0から順番になってないと表示されない 今見た感じバージョンはSmarty 2.6.18だけど。 以下のような{section}を使ったループ表示で、
{section name=key loop=$message}
 {$message[key]|escape}<br />
{/section}
php側を、
$message = array('123','456','abc');

$o_smarty -> assign("message", $message);
とすると、ちゃんと表示されるが、
$message[3] = '123';
$message[7] = '456';
$message[] = 'abc';

$o_smarty -> assign("emes", $emes);
とすると、何も表示されない。 悩まず、{foreach}に変更すべし。

2010-03-18

MacPortsでOSXにphp5.3を入れる

OSXに、php5.3を入れようということで、
port installed
で見たところ、apache2とかはもうインストール済みになってるみたいだったので、もう一回以下のコマンドでphpのインストールに挑戦。
sudo port -d install php5 +apache2 +macox +mysql5 +pear +postgres +sockets +sqlite
最後に出た以下のメッセージを完全に見逃す
To customize php, copy
/opt/local/etc/php5/php.ini-development (if this is a development server) or
/opt/local/etc/php5/php.ini-production (if this is a production server) to
/opt/local/etc/php5/php.ini and then make changes.

If this is your first install, you need to activate PHP in your web server.

To enable PHP in Apache, run
  cd /opt/local/apache2/modules
  /opt/local/apache2/bin/apxs -a -e -n "php5" libphp5.so
こうして入れたシステム一式は、OSXの
/opt/local
の下に入るようです。
PHPのバージョン5.3.2になっている。
$ php -v
PHP 5.3.2 (cli) (built: Mar 18 2010 03:49:53) 
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
pearも入っている。
$ pear -V
PEAR Version: 1.9.0
PHP Version: 5.3.2
Zend Engine Version: 2.3.0
でアパッチを起動したいということで、
sudo launchctl load -w /Library/LaunchDaemons/org.macports.apache2.plist 
とりあえずこうするだけでapache動いた。
が、phpが動いていない。 そこで、以下を実行。
sudo cp php.ini-development php.ini
sudo /opt/local/apache2/bin/apxs -a -e -n "php5" libphp5.so
アパッチのhttpd.confに
以下を追加。
Include conf/extra/mod_php.conf
以下を付け足し。

    DirectoryIndex index.html index.php

で、
sudo /opt/local/apache2/bin/apachectl restart
したら、phpもオッケーに。
なったがな、しかし。だな。

2010-03-16

XAMPPのインストールと設定

Windowsのphpのバージョンを5.3.2にアップしようとしたらば、apacheまで動かなくなったので、もうあきらめて新規にXAMPPでインストールすることに。

apache friends

ここから XAMPP Windows版 1.7.3 をダウンロードしてインストール。

最初に環境変数に書かれている既存のMySQLとかphpのパスを消去してからやった方がよかったみたいですね。
既存のそれらのプログラムとごっちゃになってしまって、その修正に丸一日かかってしまった。
そもそもMySQLがサービスとして動いていたようで、XAMPPはそっちを見に行ってしまった模様。
マニュアルには最初に「セキュリティ」からMySQLのrootのパスワードを設定せよ、と書いてあるが、既存のMySQLを見に行ってるので既にパスワードがある状態になっている。
こりゃおかしい、ということで既存のMySQLのサービスを無効にして、環境変数の方は
C:\xampp\mysql\bin;
に書き換え、これでようやく正常に戻る。
改めて「セキュリティ」から進んで、rootのパスワードを設定。
あとphpMyAdminのDB接続設定をするために C:\xampp\phpMyAdmin\config.inc.php にrootのパスワードを設定
/* Authentication type and info */
$cfg['Servers'][$i]['auth_type']            = 'config';
$cfg['Servers'][$i]['user']                 = 'root';
$cfg['Servers'][$i]['password']             = 'password';
$cfg['Servers'][$i]['AllowNoPassword']      = true;
それからMySQLの文字コード関係設定。C:\xampp\mysql\bin\my.ini を開いて一番下に、
[mysqld]
skip-character-set-client-handshake
default-character-set = utf8
character-set-server = utf8
collation-server = utf8_unicode_ci
init-connect = SET NAMES utf8

[mysql]
default-character-set=utf8
を追加。

次にapacheの設定。
C:\xampp\apache\conf\httpd.conf を開いて、Directory の
<Directory />
    Options FollowSymLinks
    AllowOverride none
    Order deny,allow
    Deny from all
</Directory>
を以下に変更
<Directory />
    Options FollowSymLinks
    AllowOverride all
    Order deny,allow
    Deny from all
</Directory>
ついでにバーチャルホストの設定。C:\xampp\apache\conf\extra\httpd-vhosts.conf を開いて、
以下のコメントアウトを外す
NameVirtualHost *:80
一番下に以下を追加
<VirtualHost *:80>
    ServerAdmin someone@somedomain
    DocumentRoot "D:/project"
    ServerName project
    ErrorLog "logs/project-error.log"
</VirtualHost>
<Directory "D:/project">
    Options FollowSymLinks
    AllowOverride all
    Order deny,allow
    Allow from all
</Directory>
ここで、最後の Allow from が all になってないと見られないので注意。
さらに、C:\Windows\System32\drivers\etc\hosts を開いて、
127.0.0.1       localhost
127.0.0.1       project
を追加。
あと php.ini のこのヘンを修正
[mbstring]
mbstring.language = Japanese
mbstring.internal_encoding = UTF-8
mbstring.http_input = pass
mbstring.http_output = pass
mbstring.encoding_translation = Off
mbstring.detect_order = UTF-8,SJIS,EUC-JP,JIS,ASCII
mbstring.substitute_character = none;
mbstring.func_overload = 0
最後にpearも既存のphpのパスが悪さしてへんてこりんな設定になっていたので、C:\xampp\php に go-pear を置いて、再インストール
php go-pear.php
これはコマンドプロンプトを管理者モードで実行しないとだめ。
あ、そうそう、あと、こうやってインストールした状態では、apacheの mod_rewrite は最初から使えるようになってるし、phpの short_open_tag はOffになってるぞ。

このブログを検索

Powered by Blogger.

ラベル

php (17) jQuery (13) OSX (10) MySQL (8) Javascript (7) Postgres (7) port (7) apache (6) Java (3) Smarty (2) html (2) pear (2) FCKEditor (1) XAMPP (1) css (1) git (1) perl (1) ruby (1)

Facebookバナー