Kinopyo Blog

プログラマとしてRuby, Rails, iPhone, iPad,Macなどなどと向き合う日々のログポース

Posts Tagged ‘PHP’

さくらVPSのパフォーマンスチューニング

2011年11月11日

さくらVPSでWordpressブログをやってます。そんなにアクセスがないはずなのに、さくらVPSに移行した当時は結構サーバが落ちました。topコマンドで見るとスワップが結構70%まで上がったりして、手動でapache再起動とかもやりました。。ネットでいろいろググッてそれなりにパフォーマンスチューニングした結果やっと安定して、同じサーバに2つのWordpressブログと一つのRailsアプリケーションを実行しています。

参考までにさくらVPSは一番安いの512MB、月1000円のプランで、平日だとこのブログの一日PVは大体500〜600です。

チューニングしてから結構時間が経ったのでだいぶ忘れました。Evernoteでのメモを貼ります。

効果が高い順で。

WordPress

W3 Total Cache プラグインが一押しです。インストールも設定も簡単ですし、一番効果があります。

ページのキャッシュ、cssやjavascriptのマージと圧縮など、結構やってくれます。実際これだけ入れても感じるほど早くなるはずです。

Apache

httpd.confを編集します。いろいろ修正してみましたが、結果下記の設定にしました。この辺はググったら結構出ますが、それぞれの環境に合わせて試行錯誤したほうがオススメです。


<IfModule prefork.c>

StartServers       5

MinSpareServers    5

MaxSpareServers   10

ServerLimit      64

MaxClients       64

MaxRequestsPerChild  50

MaxMemFree 2000

</IfModule>

PHP

/etc/php.ini

zlib.output_compression = On

APC

pecl install APC

さくらVPSに移行後wordpressで画像アップロードした時にサムネイルが生成されない件

2011年09月21日

WordPressで画像アップロード時にいつも3サイズのサムネイルが生成されたのですが、さくらVPSに移行後それができなくなったのです。

調べてみたらPHPの画像処理のライブラリであるgdがインストールしてないことがわかりました。

CentOSなのでyumを使ってインストールすれば解決です。


sudo yum install php-gd

sudo service httpd restart

PHP プロセス数を指定して実行するマルチスレッド処理(curl_multi)

2011年08月09日

curl_multi系を使って、プロセス数を指定して実行するマルチスレッド処理です。

urlは配列で受け取って、もし指定したプロセス数より多い場合は分割して実行するようになってます。

このサンプルコードではこのブログの幾つかのurlに対してtitleを取得しました。

<?php
/**
* 指定したプロセス数で並列処理を実行する
*
* @param array $url_list URLの配列
* @param boolean $url_as_key 結果配列を返すときに、urlをキーにする
* @param int $timeout タイムアウト秒数 0だと無制限
* @return array 結果配列
*/
function execute($url_list, $url_as_key = false, $timeout=0) {
    // set your process number
    $process = 5;

    $is_over_process = false;
    if ($process < count($url_list)) {
        // chunk url list / process number*
        $url_chunk = array_chunk($url_list, $process);
        $is_over_process = true;
    }

    $ret = array();
        
    if ($is_over_process && !empty($url_chunk)) {

        foreach ($url_chunk as $key => $url_list) {
            echo "chunk start:{$key}\n";
            
            $res = fetch_multi_url($url_list, $url_as_key, $timeout);
            if (!empty($res)) {
                $ret = array_merge($ret, $res);
            } else {
                continue;
            }

        }
    } else if (!$is_over_process && !empty($url_list)){
        $ret = fetch_multi_url($url_list, $url_as_key, $timeout);
    } else {
        echo "url invalid::";
    }
    
    return $ret;
    
}

/**
* curl_multi_execの並列処理
* ほぼboilerplate
*
* @param array $url_list URLの配列
* @param boolean $url_as_key 結果配列を返すときに、urlをキーにする
* @param int $timeout タイムアウト秒数 0だと無制限
* @return array 結果配列
*/
function fetch_multi_url($url_list, $url_as_key, $timeout) {
    $mh = curl_multi_init();
    foreach ($url_list as $i => $url) {
        $ch[$i] = curl_init($url);
        curl_setopt($ch[$i],CURLOPT_RETURNTRANSFER,1);

        //タイムアウト
        if ($timeout){
            curl_setopt($ch[$i],CURLOPT_TIMEOUT,$timeout);
        }

        curl_multi_add_handle($mh,$ch[$i]);
    }

    //URLを取得
    //すべて取得するまでループ
    $active = null;
    do {
        $mrc = curl_multi_exec($mh,$active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

    while ($active and $mrc == CURLM_OK) {
        if (curl_multi_select($mh) != -1) {
            do {
                $mrc = curl_multi_exec($mh,$active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }

    if ($mrc != CURLM_OK) {
        echo '読み込みエラーが発生しました:'.$mrc;
    }

    //ソースコードを取得
    $res = array();
    foreach ($url_list as $i => $url) {
        if (($err = curl_error($ch[$i])) == '') {
            // url_as_keyがtrueの場合、urlをキーとして格納
            if ($url_as_key) {
                $res[$url] = curl_multi_getcontent($ch[$i]);
            // そうでない場合は、ただ配列に入れる
            } else {
                $res[$i] = curl_multi_getcontent($ch[$i]);
            }
        } else {
            echo '取得に失敗しました:'.$url_list[$i].'<br />';
        }
        curl_multi_remove_handle($mh,$ch[$i]);
        curl_close($ch[$i]);
    }
    curl_multi_close($mh);

    return $res;
}


// 並列実行したいurl list
$url_list = array(
    "http://www.kinopyo.com/blog/ipad-2-not-charging-when-connected-to-pc-usb",
    "http://www.kinopyo.com/blog/the-first-app-i-installed-to-ipad2",
    "http://www.kinopyo.com/blog/chrome-warn-before-quitting-with-command-q-in-mac",
    "http://www.kinopyo.com/blog/reply-to-all-always-in-gmail",
    "http://www.kinopyo.com/blog/lion-fullscreen-shortcut-key-conflict-with-evernote-client",
    "http://www.kinopyo.com/blog/how-to-set-gesture-for-chrome-to-swipe-back-and-forth-in-lion",
    "http://www.kinopyo.com/blog/3-free-ebooks-for-study-coffeescript"
);

// start time
$start_time = microtime(true);

// execute
$res = execute($url_list, true);

// execute time
$time = microtime(true) - $start_time;

// play with the result
// here I just get the page title
$titles = array();
foreach ($res as $url => $html) {
    preg_match('{<title>(.*)</title>}',$html, $match_title);
    $titles[$url] = $match_title[1];
}

echo "Result:\n";
echo "time:{$time} sec\n";
print_r($titles);

参考:PHPでマルチスレッド(バックグラウンド処理)を実現する方法

PHP:必要桁数まで0埋めする

2011年07月22日
// 47 -> 047
sprintf("%03d", 47); 

// or

// parameter: string $input , int $pad_length,
str_pad(47, 3, '0', STR_PAD_LEFT);

str-padについては:http://php.net/manual/en/function.str-pad.php

複数URLのFacebookいいね(Like)された数を取得する方法

2011年07月20日

今はほとんどのサイトがLike(いいね!)ボタンを置いてますが、Likeボタンがどれだけクリックされたか、その数は気になりますね。

Facebook独自のFQL(文法はSQLに似ている)を使って複数URLのLikeされた数が取れます。Facebook公式ドキュメントはこちらにあります。

下記はPHPでのサンプルコードです。

<?php
// see fql docs:
// http://developers.facebook.com/docs/reference/fql/link_stat/

$url_list = array(
'http://example.com/url1.html',
'http://example.com/url2.html',
);

$url_string = '("'. implode('","', $url_list). '")';

// just like sql syntax
$fql = "select url, like_count from link_stat where url in".$url_string;
// remember to encode it
$url = "https://api.facebook.com/method/fql.query?format=json&query=".urlencode($fql);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
curl_close($ch);

$json = json_decode($data, true);
$ret = array();
print_r($json);

// result example
/*
Array
(
[0] => Array
(
[url] => http://example.com/url1.html
[like_count] => 10
)

[1] => Array
(
[url] => http://example.com/url2.html
[like_count] => 20
)

)
*/

PHP:環境構築時に使う接続テストコードまとめ MySQL/Oracle/Memcache/Memcached

2011年06月03日

環境構築時に接続テスト用のコードをまとめました。

Facebookからのメール:you may be allowing authentication data to be passed to 3rd parties

2011年05月20日

Our automated systems have detected that you may be inadvertently allowing authentication data to be passed to 3rd parties.

ここ最近Facebookから”認証情報をサードパーティに渡している可能性がある、48時間内に修正しろ”みたいなメールが届いた人が多いと思います。

メール本文です:

Dear Developer of xxxxxx {your app name} xxxxx,

Our automated systems have detected that you may be inadvertently allowing authentication data to be passed to 3rd parties. Allowing user ids and access tokens to be passed to 3rd parties, even inadvertently, could allow these 3rd parties to access the data the user made available to your site. This violates our policies and undermines user trust in your site and Facebook Platform.

In every case that we have examined, this information is passed via the HTTP Referer Header by the user’s browser. This can happen when using our legacy authentication system and including <iframe>, <img> or <script> content from 3rd parties in the page that receives authentication data from Facebook. Our legacy mechanism passes authentication information in the URL query string which, if handled incorrectly, can be passed to 3rd parties by the browser. Our current OAuth 2.0 authentication system, released over a year ago, passes this information in the URL fragment, which is not passed to 3rd parties by the browser.

Please ensure that you are not allowing this data to be passed immediately. Accessing your site as a test user while running a HTTP proxy/monitor like Charles or Fiddler is the best way to determine if you are allowing this information to be passed. If you discover the issue, you can do one of two things:

1. Migrate your site to use our OAuth 2.0 authentication system. We are requiring all apps and sites to update to this mechanism by Sept. 1, 2011. Migrating now will address this issue and ensure that you are one of the first to meet the deadline. For more details, please see our Authentication Guide.

2. Create and use an interstitial page to remove the authentication data before redirecting to your page with 3rd party content. This approach is used by many of our largest developers today (although they are all migrating to OAuth 2.0 shortly). This is a simple and straightforwardchange that should have minimal impact on your site. For more details on this approach, see our Legacy Connect Auth doc.

Because of the importance of ensuring user trust and privacy, we are asking you to complete one of the above steps in the next 48 hours. If you fail to do so, your site may be subject to one of the enforcement actions outlined in our policies.

If you have any questions or believe you have received this message in error, please contact us.

Facebook Developer Relations

多分認証システムが古い(OAuth 2.0を使ってない)かつ、該当のページに<iframe>などのタグがあるのを条件としてFacebook側が検知してたと思います、別にサードパーティにどうこうした訳ではなく。

ソースの確認

まずユーザ認証のところのソースを確認しましょう。

古い認証システムを使う場合これに似てるはずです。

ソースはFacebookのLegacy Connect Auth – Facebook開発者より。

 $api_key = "YOUR_API_KEY";
 $interstitial_page = "YOUR_SECURE_URL"; //URL with no 3rd party apps

 $url='http://www.facebook.com/login.php?api_key=' . $app_id
   . '&session_version=3&next=' . urlencode($interstitial_page)
   . '&v=1.0&return_session=1&fbconnect=1'
   . '&cancel_url=' . urlencode($interstitial_page);

 echo "Welcome to the Old Auth flow";
 echo "<p>";

 echo("<a href='" . $url . "'>"
   . "<img src='http://static.ak.facebook.com/images/"
   . "devsite/facebook_login.gif'></a>");

この場合nextパラメータで指定したページに行く時、URLパラメータでuidとかaccess_tokenが渡されますので、危ないと。

OAuth 2.0を使ったログインURLはこうです。


https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&scope=email,read_stream

改修

メールで書かれた通り一つはOAuth2.0に移行する、一つは「中間ページ」挟んでユーザ情報をセッションに保持して隠蔽する。

OAuthについてはAuthentication – Facebook開発者で詳しく説明しています。

後者の場合はLegacy Connect Auth – Facebook開発者の最後でサンプルコードがあるので参考できます。

Cakephp XML to Array

2011年05月15日

CakephpでXMLを配列に変換するのはとても簡単です。

下記のファンクションを使えば一発でできます。

uses('Xml');
$xml = new Xml("/path/to/xml");
$xml_array = Set::reverse($xml);

PHP Fatal error: Call to undefined function mcrypt_decrypt()

2011年04月22日

こちらを参照してください。

PHPを–with-mcrypt[=DIR]再コンパイルする必要があるそうです。またlibmcryptも2.5.6以上のバージョンを求められます。

一般的にもしfunction is undefinedになったら、何かライブラリをコンパイルする必要があるということになります。

sudo apt-get install php5-mcrypt

works on ubuntu.

sudo port install php5-mcrypt

works on mac.

http_build_queryを使うときの注意点:URLパラメータ区切りがエスケープされる(XAMPP要注意)

2011年04月15日

障害

http_build_queryを使ってurlのパラメータを作るときにパラメータがこうなりました。

foo=bar&amp;baz=boo&amp;hoge=hoge

上記のように複数のパラメータがある時に;であるべきのところが&となってしまいました。

これはPHPの設定ファイル、php.iniのarg_separator.outputで制御されてて、XAMPPの場合インストールされたデフォルトでは、「&」となるそうです。

解決方法

なので、php.iniの「arg_separator.output」の値を「&」から「&」に変更する

参考

http_build_queryを使うときの注意点(PHP)

http://www.multiburst.net/sometime-php/2008/12/http_build_query/