[ロリポップ]MySQLサーバのIPアドレスを調べる方法

ロリポップのMySQLサーバへSSHトンネリング経由で接続したかったのですが、サーバのIPアドレスが分からなかったので調べてみました。
調査時点で、下記のIPになってました。

mysql501.phy.lolipop.jp : 157.7.105.174
mysql502.phy.lolipop.jp : 157.7.106.56
mysql503.phy.lolipop.jp : 157.7.106.57
mysql504.phy.lolipop.jp : 157.7.105.175
mysql505.phy.lolipop.jp : 157.7.106.58
mysql506.phy.lolipop.jp : 157.7.106.59
mysql507.phy.lolipop.jp : 157.7.105.161
mysql508.phy.lolipop.jp : 157.7.106.60
mysql509.phy.lolipop.jp : 157.7.106.61
mysql510.phy.lolipop.jp : 157.7.106.53
mysql511.phy.lolipop.jp : 157.7.106.62
mysql512.phy.lolipop.jp : 157.7.106.63
mysql513.phy.lolipop.jp : 157.7.106.54
mysql514.phy.lolipop.jp : 157.7.106.64
mysql515.phy.lolipop.jp : 157.7.106.65
mysql516.phy.lolipop.jp : 157.7.105.158
mysql517.phy.lolipop.jp : 157.7.106.66
mysql518.phy.lolipop.jp : 157.7.106.67
mysql519.phy.lolipop.jp : 157.7.105.159
mysql520.phy.lolipop.jp : 157.7.106.68
mysql521.phy.lolipop.jp : 157.7.106.69
mysql522.phy.lolipop.jp : 157.7.105.160
mysql523.phy.lolipop.jp : 157.7.106.70
mysql524.phy.lolipop.jp : 157.7.106.71
mysql525.phy.lolipop.jp : 157.7.105.162
mysql526.phy.lolipop.jp : 157.7.106.72
mysql527.phy.lolipop.jp : 157.7.106.73
mysql528.phy.lolipop.jp : 157.7.105.163
mysql529.phy.lolipop.jp : 157.7.106.74
mysql530.phy.lolipop.jp : 157.7.106.75
mysql531.phy.lolipop.jp : 157.7.105.164
mysql532.phy.lolipop.jp : 157.7.106.76
mysql533.phy.lolipop.jp : 157.7.106.77
mysql534.phy.lolipop.jp : 157.7.105.165
mysql535.phy.lolipop.jp : 157.7.106.78
mysql536.phy.lolipop.jp : 157.7.106.79
mysql537.phy.lolipop.jp : 157.7.106.55
mysql538.phy.lolipop.jp : 157.7.106.80
mysql539.phy.lolipop.jp : 157.7.106.81
mysql540.phy.lolipop.jp : 157.7.105.157
mysql541.phy.lolipop.jp : 157.7.106.82
mysql542.phy.lolipop.jp : 210.157.22.120
mysql549.phy.lolipop.jp : 157.7.104.133
mysql550.phy.lolipop.jp : 157.7.104.134
mysql551.phy.lolipop.jp : 157.7.104.135
mysql552.phy.lolipop.jp : 157.7.104.136
mysql553.phy.lolipop.jp : 157.7.104.137
mysql554.phy.lolipop.jp : 157.7.104.138
mysql555.phy.lolipop.jp : 157.7.104.139
mysql556.phy.lolipop.jp : 157.7.104.140
mysql557.phy.lolipop.jp : 157.7.104.141
mysql558.phy.lolipop.jp : 157.7.104.142
mysql559.phy.lolipop.jp : 157.7.104.143
mysql560.phy.lolipop.jp : 157.7.104.144
mysql561.phy.lolipop.jp : 157.7.104.145
mysql562.phy.lolipop.jp : 157.7.104.146
mysql563.phy.lolipop.jp : 157.7.104.147
mysql564.phy.lolipop.jp : 157.7.104.148
mysql565.phy.lolipop.jp : 157.7.104.149
mysql566.phy.lolipop.jp : 157.7.104.150
mysql567.phy.lolipop.jp : 157.7.104.151
mysql568.phy.lolipop.jp : 157.7.104.152
mysql569.phy.lolipop.jp : 157.7.104.153
mysql570.phy.lolipop.jp : 157.7.104.154
mysql571.phy.lolipop.jp : 157.7.104.155
mysql572.phy.lolipop.jp : 157.7.104.156
mysql573.phy.lolipop.jp : 157.7.104.157
mysql574.phy.lolipop.jp : 157.7.104.158
mysql575.phy.lolipop.jp : 157.7.104.159
mysql576.phy.lolipop.jp : 157.7.104.160
mysql577.phy.lolipop.jp : 157.7.104.161
mysql578.phy.lolipop.jp : 157.7.104.162
mysql579.phy.lolipop.jp : 157.7.104.163
mysql580.phy.lolipop.jp : 157.7.104.164
mysql581.phy.lolipop.jp : 157.7.104.165
mysql582.phy.lolipop.jp : 157.7.104.166
mysql583.phy.lolipop.jp : 157.7.104.167
mysql584.phy.lolipop.jp : 157.7.104.168
mysql585.phy.lolipop.jp : 157.7.104.169
mysql586.phy.lolipop.jp : 157.7.104.170
mysql587.phy.lolipop.jp : 157.7.104.171
mysql588.phy.lolipop.jp : 157.7.104.172
mysql589.phy.lolipop.jp : 157.7.104.173
mysql590.phy.lolipop.jp : 157.7.104.174
mysql591.phy.lolipop.jp : 157.7.104.175
mysql592.phy.lolipop.jp : 157.7.104.176
mysql593.phy.lolipop.jp : 157.7.104.177
mysql594.phy.lolipop.jp : 157.7.104.178
mysql595.phy.lolipop.jp : 157.7.104.179
mysql596.phy.lolipop.jp : 157.7.104.180
mysql597.phy.lolipop.jp : 157.7.104.181
mysql598.phy.lolipop.jp : 157.7.104.182
mysql599.phy.lolipop.jp : 157.7.104.183
mysql600.phy.lolipop.jp : 157.7.104.184
mysql601.phy.lolipop.jp : 210.157.9.114
mysql602.phy.lolipop.jp : 157.7.104.185
mysql603.phy.lolipop.jp : 157.7.104.186
mysql001.phy.lolipop.lan : 172.19.44.112
mysql002.phy.lolipop.lan : 172.19.44.101
mysql003.phy.lolipop.lan : 172.19.44.113
mysql004.phy.lolipop.lan : 172.19.44.103
mysql005.phy.lolipop.lan : 172.19.44.114
mysql006.phy.lolipop.lan : 172.19.44.105
mysql007.phy.lolipop.lan : 172.19.44.115
mysql008.phy.lolipop.lan : 172.19.44.107
mysql009.phy.lolipop.lan : 172.19.44.116
mysql010.phy.lolipop.lan : 172.19.44.109
mysql011.phy.lolipop.lan : 172.19.44.117
mysql012.phy.lolipop.lan : 172.19.44.111
mysql013.phy.lolipop.lan : 172.19.44.100
mysql014.phy.lolipop.lan : 172.19.44.102
mysql015.phy.lolipop.lan : 172.19.44.104
mysql016.phy.lolipop.lan : 172.19.44.106
mysql017.phy.lolipop.lan : 172.19.44.108
mysql018.phy.lolipop.lan : 172.19.44.110
mysql019.phy.lolipop.lan : 172.19.44.118
mysql020.phy.lolipop.lan : 172.19.44.119
mysql021.phy.lolipop.lan : 172.19.44.120
mysql022.phy.lolipop.lan : 172.19.44.121
mysql023.phy.lolipop.lan : 172.19.44.122
mysql024.phy.lolipop.lan : 172.19.44.123
mysql025.phy.lolipop.lan : 172.19.44.124
mysql026.phy.lolipop.lan : 172.19.44.125
mysql027.phy.lolipop.lan : 172.19.44.126
mysql028.phy.lolipop.lan : 172.19.44.127
mysql101.phy.lolipop.lan : 172.19.44.63
mysql102.phy.lolipop.lan : 172.19.44.64
mysql103.phy.lolipop.lan : 172.19.44.65
mysql104.phy.lolipop.lan : 172.19.44.66
mysql105.phy.lolipop.lan : 172.19.45.151
mysql106.phy.lolipop.lan : 172.19.45.152
mysql107.phy.lolipop.lan : 172.19.46.21
mysql108.phy.lolipop.lan : 172.19.46.22
mysql109.phy.lolipop.lan : 172.19.46.25
mysql110.phy.lolipop.lan : 172.19.46.26
mysql111.phy.lolipop.lan : 172.19.46.28
mysql112.phy.lolipop.lan : 172.19.46.29


*.lolipop.lanのほうはプライベートIPですが、*.lolipop.jpはグローバルのIPが振られていました。
WhoISで確認すると、当たり前ですが”descr:GMO Pepabo, Inc.”となっており、GMOグループで管理しているIPでした。


確認に使ったのは下記のスクリプトです。
存在しているMySQLサーバのホスト名一覧は、ロリポップが用意しているPHPMyAdminのログイン画面で確認できます。

<?php
echo "test\n";
 
$hostArray = array();
for ( $i = 501; $i <= 603; $i++ ) {
    $hostArray[] = sprintf( "mysql%03d.phy.lolipop.jp", $i );
}
for ( $i = 1; $i <= 28; $i++ ) {
    $hostArray[] = sprintf( "mysql%03d.phy.lolipop.lan", $i );
}
for ( $i = 101; $i <= 112; $i++ ) {
    $hostArray[] = sprintf( "mysql%03d.phy.lolipop.lan", $i );
}
 
 
foreach( $hostArray as $hostName ) {
    $ipAddr   = gethostbyname( $hostName );
    echo "{$hostName} : {$ipAddr}\n";
}

小さなレジンアクセサリのパーツ作りに役立つ型を購入(3Dシリコンモールド5個セット)

アクセサリのパーツを作りたかったのですが、手軽にいろいろな形のパーツを型取りできるものがないか探していました。そんな中、amazonで見つけたのがこの商品でした。

5個セットになっていて、まとめて届く為いろいろ試せそうだったので試しに購入しました。また、セットで5つも入っているのに価格が手頃だったの点もよいです。


※クリックで大きな画像が表示されます。

アクセサリーのパーツ用としてレジンで作ってみた

この商品、もともとはネイル用のシリコンモールドらしいですが、今回はアクセサリーのパーツ作り用として使用しています。なかなかショップに行っても、小さい部品用のシリコンモールドってなかったので助かります。

セットの内容は、サクラ、雪、金魚、ハロウィン、星と月の5種類があり、ハロウィンとサクラは単品販売がないらしいです。ハロウィンやサクラの型がほしいならこのセットで購入するしかないっぽいです。
(写真だと、下の2つです)

小さな型なので綺麗にできるかどうか心配だったのですが、作ってみると、とても綺麗に形ができました。ハロウィンのカボチャは顔の凹凸まで出ていて、オバケもとても可愛く仕上がります。サクラは、レジンの量を調整することで中心部に穴が開くようになっています。穴を開けたくない場合は、少しだけレジンの量を多く入れれば塞がります。

雪の型だけはちょっと残念…

少しだけ残念だったのが雪の形でした。
あまりにも型が小さくてレジンが溢れてしまい、形を作ったときにはみ出しができてしまいます。大きめの形は、慎重にやれば綺麗にできますが、一番小さい型はなかなか難しいです。

全ての型でいろんなバリエーションが作れるので、価格の割には楽しめてよかったです

下記の画像クリックでamazonに飛びます。
amazonの商品ページのほうが1円玉との大きさ比較画像もあるのでサイズ感が分かりやすいです。

3Dシリコンモールド 【 福袋5種類 】金魚 月 星 桜 雪 結晶 お化け レジン パーツ アクセサリー 素材

追記:この記事を書くために改めて検索してみたら、同じものがamazonより安く楽天で売ってました…。
最新の価格を比較して安いほうで購入してください。


3Dシリコンモールド 【 福袋5種類 】金魚 月 星 桜 雪 結晶 お化け ネイルアート レジン パーツ 型 アクセサリー 素材 シリコンモールド シリコン モールド UVクラフトレジン UVレジン レジン液 封入 材料

[ロリポップ]cronからphpを実行しようとして”php: command not found”エラーが出るときの対処法

ロリポップのレンタルサーバではcronによるシェルスクリプトの定期実行が可能なのですが、スクリプトからphpを実行してみたところ、以下のエラーが発生しました。

test.sh line 2: php: command not found



このとき実行したスクリプトは下記のとおりです。

#!/bin/bash
php -v >  /path/to/log/result.txt 2>&1




このような場合は、以下のような形にすればOKっぽいです。

#!/bin/sh
/usr/local/php5.6/bin/php -v >  /path/to/log/result.txt 2>&1



ポイントは2点でです。

1点目は、phpにパスが通ってないので、フルパスでの指定が必要なようです。
使いたいPHPのバージョンによって、下記のパスを指定します。

/usr/local/php5.3/bin/php
/usr/local/php5.5/bin/php
/usr/local/php5.6/bin/php



ちなみに確認してみたところ、各PHPバージョンのマイナーバージョンとビルドは下記のとおりでした。

PHP 5.3.29 (cli) (built: Sep  1 2014 16:01:39)
PHP 5.5.25 (cli) (built: May 18 2015 13:14:35)
PHP 5.6.13 (cli) (built: Sep 15 2015 18:22:33)



2点目は、1行目のシェル指定です。
#!/bin/bashを指定してダメな場合は、#!/bin/shで試してみてください。

[DataGridView]改行を含むセル値を、Excelに貼付できるようにする方法

DataGridViewにテキストを表示させているとき、Ctrl-C,VでデータをExcelへコピー&ペーストさせたい場合があります。

通常であれば特にプログラムを書かなくてもコピペは可能なのですが、コピー元となるDataGridViewのセル値に改行が含まれる場合は上手くいきません。

例えば、以下のようなデータがあった場合、Excelへコピペをすると…


Excel上では、下記のようにコピー元にあった改行文字の単位で別のセルに値が入ってしまいます。


このような場合は、下記の対処方法をとると解決できます。


※余談ですが、DataGridViewのセル内で改行を許可するには、下記のようにWrapModeプロパティを変更すればOKです

dataGridView1.Columns[0].DefaultCellStyle.WrapMode = DataGridViewTriState.True;



対処方法

対処方法ですが、Excelでは改行を含むデータをセルに含めたい場合は「”」で括ればOKです。
試しに、下記の文字列をコピーして、Excelに貼り付けてみると、期待する結果になることがわかります。

"1aaaaa
1aa
1aaa"
"2bbbbb
2bbb"



ですので、上記のようなテキストがクリップボードに入ればよいということになります。

ボタンを用意して、下記のコードを書いてみてください。

private void button1_Click( object sender, EventArgs e ) {
 
    StringBuilder builder = new StringBuilder();
    foreach( DataGridViewCell curCell in dataGridView1.SelectedCells ) {
        builder.Append( "\"" + curCell.Value + "\"" + Environment.NewLine );
    }
 
    Clipboard.SetText( builder.ToString() );
}



DataGridViewのコピーしたいセルを選択した状態で上記のコードを走らせると、選択範囲のテキストがクリップボードにコピーされます。

コピーしたテキストをExcelにペーストすると下記のように改行を含めて正しくコピペが行えました。




Xamarin Studioを使いたい人が、最初に読むべきチュートリアル資料

新しい開発環境を試してみたい場合、簡単なチュートリアルを参考にしながら、試しに1本プログラムを作ってみると、手の動かし方など基本的な部分を理解するのに役立ちます。

先日、Xamarin Studioを評価してみたですが、下記のサンプルプログラムがわかりやすかったため、メモしておきます。

Xamarin Studioの参考ページ一覧

Xamarin Studioを最初にセットアップする方法
https://github.com/ytabuchi/XamarinHOL/blob/master/XamarinHandsOn_Prepare_JXUG.pdf

ハンズオンのドキュメント
https://github.com/ytabuchi/XamarinHOL
この中にある、XamarinHandsOn_Android.pdfなどをダウンロードしながら、黙々と作業を行う

コピペ用のサンプル
https://github.com/ytabuchi/XamarinHOL/wiki
コードを書く場面で、手打ちしたくない場合は、上記のページからコピペ可能


参考書籍
いわゆる”ペゾルド本”というのが、現状最も役立つリファレンスっぽいです。
英語なのですが、無料でダウンロードして読むことができます。
https://developer.xamarin.com/guides/xamarin-forms/creating-mobile-apps-xamarin-forms/


Xamarin Studioの評価結果

で、実際にプロジェクトを1つ作って評価してみた結果ですが…

Xamarin Studio自体は、WindowsプログラマでVisual Studioでの開発経験があれば、非常に利用しやすい(直感的に作業が理解しやすい)環境でした。

一方、UI(画面レイアウト)の作成にはまだ難があるようです。
Xamarin Studioは、UIの作成方法が、”Xamarinネイティブ”、”Xamarin.Forms”と2つあるのですが、現時点では”Xamarinネイティブ”のほうで作ったほうが良さそうです。

Xamarin.Formsのほうは、Android, iOSとも同じコードでUI画面を作れるメリットがあるのですが、GUI上で画面レイアウトを作成することができず、*.xamlファイルをエディタで編集してレイアウト作りしなければならない点が(現状では)致命的な欠陥です。

この点さえ解消されれば、Xamarin.Formsのがコード自体は書きやすいため、UIエディタ待ちな感じでした。

[Xamarin] *.xaml.csに書いたInitializeComponent()メソッドでエラーが出る場合


Xamarinでプログラムを作成しているとき、InitializeComponentメソッドを呼び出している箇所で、メソッドが見つからないという意味の赤い波線が表示される場合があります。

具体的には、以下のようなコードです。

public partial class MainPage :ContentPage {
 
    public MainPage() {
        InitializeComponent();
    }
    ...
}



このような場合の対処法は、下記のとおりです。
1.Mainpage.xampを開く。
2.適当な場所にスペースを入れて、上書き保存する。
3.ちょっと待つと、赤波線が消える

*.xampファイルのタイムスタンプを書き換えるとコードの再解析(自動再構築?)が走り、メソッドの解決ができるようです。

[Xamarin]エラー:96659D653BDE0FAEDB818170891F2BB0.zip is not a valid zip fileが出るときにすべきこと

このエラーが出たときにすべきことは、下記のとおり

AppData\Local\Xamarin\zips\96659D653BDE0FAEDB818170891F2BB0.zip is not a valid zip file



・プロジェクトを閉じる。
・該当のzipファイルを削除する。
・新しくプロジェクトを作る。
・ビルドする。

この手順で、ちょっと時間はかかるが、ビルドできるはずです。

3つ目の”新しくプロジェクトを作る”で、場合によってはクリーンしてリビルドでもOKかもしれません。

cURLで、エラー”Initializing NSS with certpath: none”出る理由を、ソースから調べてみた

Linux上で実行するPHPのスクリプト内で、curlを使用したプログラムを書いていたのですが、テスト中に下記のエラーが出力されました。

Initializing NSS with certpath: none
Unable to initialize NSS



メッセージ通りcertファイルがあるPATHがわからないせいで、初期化に失敗したのだろうとは思っていたのですが、このエラーが具体的にどの条件で出力されるか気になったので、curlのソースを確認してみました。

該当のメッセージでgrepしてみると、エラー出力をしているのは”curl/lib/nss.c”でした。関数の中身は以下のような感じになってます。

static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir)
{
#ifdef HAVE_NSS_INITCONTEXT
  NSSInitParameters initparams;
 
  if(nss_context != NULL)
    return CURLE_OK;
 
  memset((void *) &initparams, '\0', sizeof(initparams));
  initparams.length = sizeof(initparams);
#else /* HAVE_NSS_INITCONTEXT */
  SECStatus rv;
 
  if(NSS_IsInitialized())
    return CURLE_OK;
#endif
 
  if(cert_dir) {
    const bool use_sql = NSS_VersionCheck("3.12.0");
    char *certpath = aprintf("%s%s", use_sql ? "sql:" : "", cert_dir);
    if(!certpath)
      return CURLE_OUT_OF_MEMORY;
 
    infof(data, "Initializing NSS with certpath: %s\n", certpath);
#ifdef HAVE_NSS_INITCONTEXT
    nss_context = NSS_InitContext(certpath, "", "", "", &initparams,
            NSS_INIT_READONLY | NSS_INIT_PK11RELOAD);
    free(certpath);
 
    if(nss_context != NULL)
      return CURLE_OK;
#else /* HAVE_NSS_INITCONTEXT */
    rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY);
    free(certpath);
 
    if(rv == SECSuccess)
      return CURLE_OK;
#endif
 
    infof(data, "Unable to initialize NSS database\n");
  }
 
  infof(data, "Initializing NSS with certpath: none\n");
#ifdef HAVE_NSS_INITCONTEXT
  nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY
         | NSS_INIT_NOCERTDB   | NSS_INIT_NOMODDB       | NSS_INIT_FORCEOPEN
         | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
  if(nss_context != NULL)
    return CURLE_OK;
#else /* HAVE_NSS_INITCONTEXT */
  if(NSS_NoDB_Init(NULL) == SECSuccess)
    return CURLE_OK;
#endif
 
  infof(data, "Unable to initialize NSS\n");
  return CURLE_SSL_CACERT_BADFILE;
}



上記のロジックを見ると、cert_dirが0のときエラー”Initializing NSS with certpath: none”が出力されます。それでは関数nss_init_core()を誰がコールしているかというと、同じファイルにあるnss_init()から呼ばれています。

nss_init()の中身は以下の通りです。

static CURLcode nss_init(struct SessionHandle *data)
{
  char *cert_dir;
  struct_stat st;
  CURLcode rv;
 
  if(initialized)
    return CURLE_OK;
 
  /* First we check if $SSL_DIR points to a valid dir */
  cert_dir = getenv("SSL_DIR");
  if(cert_dir) {
    if((stat(cert_dir, &st) != 0) ||
        (!S_ISDIR(st.st_mode))) {
      cert_dir = NULL;
    }
  }
 
  /* Now we check if the default location is a valid dir */
  if(!cert_dir) {
    if((stat(SSL_DIR, &st) == 0) &&
        (S_ISDIR(st.st_mode))) {
      cert_dir = (char *)SSL_DIR;
    }
  }
 
  rv = nss_init_core(data, cert_dir);
  if(rv)
    return rv;
 
  if(num_enabled_ciphers() == 0)
    NSS_SetDomesticPolicy();
 
  initialized = 1;
  return CURLE_OK;
}


このコードを見ると、下記のルールでcert_dirを決定しています。
・環境変数SSL_DIRで指定されたディレクトリが存在するかチェックし、存在すればこれをcert_dirとみなす。
・存在しない場合や、SSL_DIRが未定義の場合は、定数SSL_DIRで指定されたディレクトリが存在するかチェックする。
・どちらもない場合は、cert_dirにNULLをセットする。

SSL_DIRは、こちらも同じファイルで定義されており、/etc/pki/nssdbを決めうちで指定しています。

#define SSL_DIR "/etc/pki/nssdb"




というわけで、環境変数SSL_DIR、および/etc/pki/nssdbを検索し、これがなければエラー”Initializing NSS with certpath: none”が出力されるようです。

※本件とは直接関係ないですがこのコード、NULLが0番地であることを仮定したコードになっていて、ちょっと良くないですね…


[Google APIs Client Libraries]認証処理でOAuth2 access tokenの保存エラーが出たときの対処法

Google APIs Client Librariesを使用して、Googleのサーバへ対してOAuth2認証を行う際、SDKは認証のアクセストークンを/tmpフォルダに保存しようとします。

レンタルサーバなどで/tmpフォルダに権限がない場合、トークンの保存処理でエラーになってしまいます。

cURL Problem with the SSL CA cert (path? access rights?)



このような場合は、下記の処理でトークンの保存先を変更できます。

$config = new \Google_Config();
$config->setClassConfig('Google_Cache_File', 'directory', '/path/to/cache/dir');
 
$client = new \Google_Client($config);



[Google Client Library]Google通信のデバッグログを画面に出力する方法

GoogleAnalytics APIなど、Google Client LibraryのSDKを使用したプログラムを作る際、認証処理など、通信でエラーになる場合があります。
このような場合、デバッグログを出力させると問題の切り分けが容易になります。

以下のコードのように、Google_Config::setLoggerClass()の指定やGoogle_IO_Curlクラスのログレベル変更を行うことで、デバッグログの出力が可能となります。

// 動作のロギングクラスを指定する
$config = new \Google_Config();
$config->setLoggerClass('Google_Logger_File');
 
$client = new \Google_Client($config);
 
// cURLのログ出力をVERBOSEにする
$client->setClassConfig('Google_IO_Curl', 'options', array( CURLOPT_VERBOSE => TRUE));



上記変更を行ったうえでプログラムを実行すると、下記のようなログが出力されます。

[04/May/2016:23:14:07 +0900] DEBUG: File cache miss {
    "key": "...",
    "file": "/tmp/Google_Client/..."
}
[04/May/2016:23:14:07 +0900] DEBUG: OAuth2 access token expired
[04/May/2016:23:14:07 +0900] INFO: OAuth2 access token refresh with Signed JWT assertion grants.
[04/May/2016:23:14:07 +0900] DEBUG: cURL request {
    "url": "https://accounts.google.com/o/oauth2/token",
    "method": "POST",
    "headers": {
        "content-type": "application/x-www-form-urlencoded",
        "content-length": 761
    },
    "body": "grant_type=assertion&assertion_type=..."
}
* About to connect() to accounts.google.com port 443 (#0)
*   Trying 216.58.197.173... * connected
* Connected to accounts.google.com (216.58.197.173) port 443 (#0)
* Initializing NSS with certpath: none
* Unable to initialize NSS
* NSS error -8023
* Closing connection #0
* Problem with the SSL CA cert (path? access rights?)
[04/May/2016:23:14:07 +0900] ERROR: cURL Problem with the SSL CA cert (path? access rights?)



ログを見るとNSS error -8023と出ており、このエラーコードで調べると下記がエラーを引き起こした原因のようです。

A PKCS #11 module returned CKR_DEVICE_ERROR, 
indicating that a problem has occurred with the token or slot


[GoogleAnalytics API]Googleサーバへの接続タイムアウトを指定するには

Google Client APIを使用して認証を行う際、google提供のSDK内ではGoogle_IO_Curlというクラスを利用しています。
この中で、接続タイムアウト時間を変更するには、下記のコードでオプションを指定します。

接続するまでのタイムアウトを指定する場合

$client = new \Google_Client();
$client->setClassConfig('Google_IO_Curl', 'options', array(CURLOPT_CONNECTTIMEOUT=>10));



応答データが返ってくるまでのタイムアウトを指定する場合

$client = new \Google_Client();
$client->setClassConfig('Google_IO_Curl', 'options', array(CURLOPT_TIMEOUT=>10));




上記の2つは両方同時に指定することも可能です。

[FuelPHP]task作成時、指定可能なクラス名・ファイル名の組み合わせとは?

FuelPHPでtaskを作ったとき、実行が可能なファイル名とクラス名の組合わせです。
良く忘れるので忘備録代わりに置いておきます。

特に大文字小文字と、アンダーバーの組み合わせで、何がOKなのかよく忘れてしまいます…

まずは、テスト結果から。

クラス名
ファイル名
コマンド
実行
説明
Test
test.php
php oil refine test
OK
これが基本形
Test
test.php
php oil refine Test
OK
コマンドは大文字小文字関係ない
Test
test.php
php oil refine tESt
OK
コマンドは大文字小文字関係ない
TeSt
test.php
php oil refine test
OK
クラス名の途中に大文字が入ってもOK
(CamelCaseはOK)
Test
Test.php
php oil refine test
NG
ファイル名に大文字が入るのはNG(*1)
Test
Test.php
php oil refine Test
NG
ファイル名に合わせて、コマンドを大文字にしてもNG(*1)
Test_Task
test_task.php
php oil refine test_task
OK
アンダーバーでつなぐのはOK
Test_Task
Test_Task.php
php oil refine test_task
NG
アンダーバーを入れても、ファイル名に大文字が入るのはNG
Test_Task
test/task.php
php oil refine test_task
NG
(FuelPHP1.8以下では)タスクはサブフォルダに置けない仕様
*1の部分は、エラー「Uncaught exception Oil\Exception: Task “test01” does not exist. Did you mean “Test01”?」が出力されます。

クラス名をCamelCaseしても動作するようですが、FuelPHPのコーディング標準には準拠していないので、やめておいた方が良いです。
http://fuelphp.jp/docs/1.7/general/coding_standards.html

あと、Windowsではファイル名の大文字・小文字を見ていないようでです。
なので、Test_Task.phpといったファイル名にすると、Windowsだと実行可能だけど、Linux上にコピーすると動作しないという、気づきづらいバグに悩まされてしまいます。

というわけで、まとめると以下のような感じです。
・クラス名は先頭大文字で、複数単語で構成される場合は、アンダーバーでつなぐ。
・ファイル名は全て小文字にする。
・php oil refineで実行するときのコマンド名は、大文字小文字の区別がない。



[GoogleAnalytics API]エラー:(403) User does not have any Google Analytics account. が出るときの対処法

GoogleAnalytics APIを使用したプログラムで、APIをコールすると、”User does not have any Google Analytics account”エラーが出る場合があります。

data
PHP Fatal error:  Uncaught exception 'Google_Service_Exception' with message 
'Error calling GET https://www.googleapis.com/analytics/v3/management/accounts: 
(403) User does not have any Google Analytics account.' 
in google-api-php-client\src\Google\Http\REST.php:110
 
Stack trace:
#0 google-api-php-client\src\Google\Http\REST.php(62): 
   Google_Http_REST::decodeHttpResponse(Object(Google_Http_Request), Object(Google_Client))
#1 [internal function]: Google_Http_REST::doExecute(Object(Google_Client), Object(Google_Http_Request))
#2 google-api-php-client\src\Google\Task\Runner.php(174): call_user_func_array(Array, Array)
#3 google-api-php-client\src\Google\Http\REST.php(46): Google_Task_Runner->run()
#4 google-api-php-client\src\Google\Client.php(593): 
   Google_Http_REST::exec in google-api-php-client\src\Google\Http\REST.php on line 110



これは、APIのサービスアカウントを作成した際に、用意したユーザアカウントがGoogle Analyticsに登録されてないのが原因です。


GoogleAnalytics APIを利用したプログラムを作る際、最初に下記のような形で認証を行うはずです。
まずは、ここで使用するservice_account_emailを覚えておきます。

$service_account_email = 'user@projectName.iam.gserviceaccount.com';
$key_file_location = 'xxx.p12';
 
// 生成されたclient_secrets.p12 キーファイルをロードする
$key = file_get_contents($key_file_location);
 
// GooleAPIの認証を行う
$scopes = array(Google_Service_Analytics::ANALYTICS_READONLY);
$cred = new Google_Auth_AssertionCredentials( $service_account_email, $scopes, $key );




Google Analyticsの管理画面より、ユーザ管理をクリックします。



先ほどプログラムで指定していたユーザのを追加します。


ユーザ一覧に追加されていればOKです。


この状態で、再度プログラムを実行すれば403の認証エラーは解消されます。

もし、この状態でも認証エラーが出る場合は、下記の点を確認してみてください。
・Google Developer ConsoleでGoogleAnalytics APIを有効にしているか
・Google Developer Console認証情報(アカウント)を作っているか
・認証情報をOAuth APIにしているか
・認証のしかたをp12形式にしているか


[GoogleAnalytics API] エラー:This library must be installed via composer or by downloading the full package. が出るときの対処法

GoogleAnalytics APIを利用したプログラムを実行すると、”This library must be installed via composer or by downloading the full package.”というエラーが出る場合があります。

PHP Fatal error:  Uncaught exception 'Exception' with message 
'This library must be installed via composer or by downloading the full package. 
See the instructions at https://github.com/google/google-api-php-client#installation.' 
in google-api-php-client\src\Google\autoload.php:14
 
Stack trace:
#0 sample.php(10): require_once()
#1 sample.php(106): getService()
#2 {main}
  thrown in google-api-php-client\src\Google\autoload.php on line 14


これは、ダウンロードしたSDKに必要なファイルがない場合に発生します。


Google Analyticsのサイトでは、https://github.com/google/google-api-php-client#installationからSDKをダウンロードするよう記載がありますが、このページのドキュメントを見ると、下記の注意が記載されています。

NOTE: If you arrived here from  developers.google.com , 
you should be using the v1-branch of this repo




というわけで、githubからzipパッケージをダウンロードする場合は、v1-masterのブランチである下記のURLからダウンロードを行う必要があります。
https://github.com/google/google-api-php-client/tree/v1-master


ページにアクセスしたら、リスト上部の右側にあるDownload ZIPをクリックすると、SDKを入手できます。

[GoogleAnalytics API]Google_Service_Exception(Insufficient Permission)例外が出るときの対応

GoogleAnalyticsでは、APIを使用することでデータのバッチ登録が可能です。

データを検索(取得)は可能だだが、データファイルのインポートするためにREST APIをコールしたとき、Insufficient Permissionエラーが出る場合があります。

Fatal error: Uncaught exception 'Google_Service_Exception' with message 
'Error calling POST https://www.googleapis.com/upload/analytics/v3/management/accounts/...: 
(403) Insufficient Permission' in .../google-api-php-client-1-master/src/Google/Http/REST.php:110



この場合、Google_Auth_AssertionCredentials()へ渡すパラメータが不正な場合があります。
以下のように、ANALYTICS_EDIT権限を付与するとエラーが解消されるかもしれません。

$scopes = array(Google_Service_Analytics::ANALYTICS_READONLY);
$cred = new Google_Auth_AssertionCredentials( $email, $scopes, $key );




$scopes = array(Google_Service_Analytics::ANALYTICS_READONLY, Google_Service_Analytics::ANALYTICS_EDIT);
$cred = new Google_Auth_AssertionCredentials( $email, $scopes, $key );



プログラム中でconst値を使っていない場合は、以下のような変更となります。

$scopes = array('https://www.googleapis.com/auth/analytics.readonly');
$cred = new Google_Auth_AssertionCredentials( $email, $scopes, $key );




$scopes = array('https://www.googleapis.com/auth/analytics.readonly', 
                'https://www.googleapis.com/auth/analytics.edit' );
$cred = new Google_Auth_AssertionCredentials( $email, $scopes, $key );

[FuelPHP]taskとして作ったバッチはサブディレクトリで管理可能?

FuelPHPでは、バッチ処理をtaskのしくみで実行することができます。
インストール直後では、robotsというサンプルスクリプトがおいてあり、下記のコマンドで実行可能です。

php oil refine robots



プログラムは、fuel/app/tasksの下で管理するのですが、タスクの数が多くなるとサブディレクトリを作って管理したくなります。
ですが、FuelPHP1.8以下のバージョンでは、taskをサブディレクトリに格納することはサポートされていないようです。

FuelPHPの掲示板でも以下のようなやり取りがあり、FuelPHPの2.0で改善予定だそうです。

We have quite a lot of app/tasks/ and want to organize them a bit,
 so we need to put oil refine tasks in subfolders,
But we didnt figure out how to call them. We put task class file in 
fuel/tasks/sub/task.php:
...




No, this is currently not supported. Tasks don't follow the rules of 
the autoloader, they are loaded hardcoded. This will be remedied in 2.0.


by http://fuelphp.com/forums/discussion/8297/subfolders-in-oil-refine-is-it-possible


ちなみに、oil refineの処理は下記場所で実装されています。あまり良い対応のしかたではありませんが、Find the taskのコメントがある箇所付近を修正すればカスタマイズできるかも…です。

/fuel/packages/oil/classes/refine.php

class Refine
{
	public static function run($task, $args = array())
	{
		...
 
		// Find the task
		if ( ! $file = \Finder::search('tasks', $task))
		{
			$files = \Finder::instance()->list_files('tasks');
			$possibilities = array();
			foreach($files as $file)
			{
				...





ちなみに、oil refineコマンドで実行可能なタスク(=FuelPHPに認識してされているタスク)は、以下のようにhelpコマンドで確認できます。

php oil refine help

Usage:
    php oil [r|refine] <taskname>
 
Description:
    Tasks are classes that can be run through the the command line or set up as a cron job.
 
Available tasks:
    php oil refine robots
    php oil refine robots:protect
    php oil refine fromdb
    php oil refine fromdb:help
    php oil refine fromdb:scaffold
    php oil refine fromdb:model
    php oil refine fromdb:migration
    php oil refine install
    php oil refine migrate:help
    php oil refine session
    php oil refine session:create
    php oil refine session:remove
    php oil refine session:clear
    php oil refine session:help
 
Documentation:
    http://docs.fuelphp.com/packages/oil/refine.html





はじめてのフレームワークとしてのFuelPHP 改訂版

Nexus5の液晶を割ってしまったので、自力で交換してみた

先日、所有しているNexus5の液晶を割ってしまいました。

ショップで修理を依頼してもよいのですが、液晶部のパーツがamazonで安く売っていたので、今回、自力で交換してみました。


今回、購入したもの

今回の液晶交換で購入したのは、下記の2点です。

1.液晶+フレームのセット


今回購入したのは下記の商品です。amazonで買いました。

(ナイセマウス) Nicermouse LG Google Nexus 5 D820 D821 修理用キット LCD液晶ガラスデジタイザスクリーン+タッチパネル フレーム付き (フロントガラスデジタイザ) 修理工具付き ブラック [並行輸入品]

実は、破損してしまったのは液晶のガラスだけで、フレーム部分は無傷でした。
amazonでは、液晶だけをもっと安く販売されているのですが、液晶とフレームは強力に接着されており液晶だけの交換は非常に難易度が高いです。このため、今回はフレーム付きの商品を購入しました。


商品は購入してから到着まで17日かかりました。
中国から輸送されて来ました。箱は、かなりボロボロです。


ラベルを見ると、中身は”Commercial Sample(製品サンプル)”であると記載されています。
ここの記載によっては、税関で余計な時間が掛かったり、関税がかかってしまう場合もあるので、製品サンプルと書いてくれている気遣いはありがたいです。


段ボール開けてみると、厳重に梱包されており、中身は大丈夫でした。
入っているのは以下のもので、左から順に保護フィルム、液晶、フレーム、工具セットです。


Amazonの評価にも書いてあるとおり、工具セットにドライバが数本入っているのですが、この中に必要なドライバーが一本入っていないという不思議な構成です。


2.Y型ドライバー


交換パーツにはドライバーが入っているのですが、交換に必要なY型ドライバーが何故か入っていません。
このため、下記の商品も一緒に購入しました。

アネックス(ANEX) 特殊精密ドライバー Y型 No.3470A

こちらが、購入したドライバーです。


大きさは1.8mmが丁度よいサイズです。購入するときに間違えないようにしてください。



実際の交換手順


交換手順自体はYoutubeに動画がアップされています。下記の動画を見ながら行えば、だれでも作業できるレベルでした。
英語での説明ですが、動画を見れば何をすればよいかは分かります。


このため、作業中の写真はないです…。


作業中の注意ポイント

実際に交換してみて、作業中に注意したほうが良いポイントを書いておきます。

裏ブタを開けるとき

裏ブタを開けるときオープナー(前述の写真の、青い棒状のモノ)を差し込んでこじ開けていきます。
この際、SIMがある場所にオープナーを差し込んでこじ開けると、この部分だけフレームが細くなっているのでフレームが割れてしまいます。

フレームごと交換してしまうので、割ってしまっても問題ないのですが、気になる人はこの部分は避けてオープナーを入れていくと良いです。


パーツを外す際

カメラなど、各パーツは本体と接着されています。
ゆっくり外せば問題ないのですが、急に力を加えると破損してしまう可能性があるので注意してください。
動画中、”careful”と言っている部分で、ゆっくーーーり外していけば、問題なく作業できます。


電池パックを外す際

電池パックは、他のパーツより特に強力に接着されています。
ここは特に時間をかけてゆっくり外すようにすると良いです。

基盤を外す際

メインの基板を外すときに、SIMを刺したままだと、SIMのスロットが邪魔になって基板が外せません。
あらかじめSIMのスロットを外してから作業するとよいです。

スピーカを外す際

スピーカ(通話時に耳をあてるところ)を外すとき、スピーカ本体とスピーカーを支えるガワパーツの2つあるので、外し忘れに注意してください。外し忘れると移し替えるときに気づくので問題ないかとは思いますが….

一通りパーツを外し終わった後

電源スイッチ、ボリュームスイッチも外す必要があるので忘れないように作業します。

組み立て終わって最初に起動する場合

初回起動時、画面が砂嵐状態になってしまう場合があります。

このような場合は、まず、電源ボタンを10秒ほど長押しして強制シャットダウンします。
次に、電池パックの配線を外したうえで、液晶の配線を一旦はずし、つなぎ直すと正常に映りました。



修理完了後のチェック項目

修理時は、各パーツを外して付け直すので、それぞれ動作確認をする必要があります。
下記のチェックを一通り行っておけばよいかと思います。

wifiがつながるか
wifiをOFFにして、モバイル回線が使えるか
GPSが使えるか
前面カメラが使えるか
背面カメラが使えるか
通話ができるか
bluetoothが使えるか
イヤホン端子が使えるか
内蔵スピーカーが使えるか
ボリュームのUP/DOWNが正常に行えるか
NFC機能が使えるか
画面の自動回転をONにして、自動回転できるか
カメラのフラッシュライトが使えるか





液晶交換後

結構いろいろなパーツをつけ外しするので不安でしたが、無事交換に成功しました。
交換後の端末です。新品同様になりました。


中国のよくわからないメーカから購入したので正直、品質が不安でしたが、フレームのパーツ・液晶とも完璧なクォリティでした。


交換中に、フレーム・液晶ともかなり細かく見比べたのですが、完全に同じ形・クオリティなので、おそらく純正パーツと同じモノでは…?と思います。


液晶購入で変わったところ


手持ちのNexus5は白色だったのですが、今回黒色のNexus5向けの交換パーツを購入しています。
このため、以下の細かな違いがあります。

スピーカー部分の網が白色から黒色に変わりました。

交換後:


交換前:



フレームの側面がツルツルな素材から、マットな素材に変わりました。
上が交換後、下が交換前です。



個人的には、どちらも交換後のほうが良い感じでしたので満足しています。



付属の保護フィルムについて


交換後、また液晶を割ってしまうのが怖かったので、付属のフィルムをつけてみました。
最初見たとき少しチープな感じがしましたが、つけてみるとつけているのが分からないぐらい綺麗でした。
ただし、少し厚みがあるので、フチの部分に段差ができます。




(ナイセマウス) Nicermouse LG Google Nexus 5 D820 D821 修理用キット LCD液晶ガラスデジタイザスクリーン+タッチパネル フレーム付き (フロントガラスデジタイザ) 修理工具付き ブラック [並行輸入品]


アネックス(ANEX) 特殊精密ドライバー Y型 No.3470A

[C#] AsinItemManager : アマゾンの商品情報を最も取得できるライブラリ

C#で、amazonの商品情報を簡単に取得できるライブラリを作成したので公開します。
ファイルは、1ファイルだけで、下記のソースをコピペするだけで動作します。

using System.Xml;
 
//*********************************************************************
/// <summary> amazon商品情報の取得管理クラス 
/// </summary>
//*********************************************************************
class AsinItemManager {
 
    //*********************************************************************
    /// <summary> dan.co.jpのwebapiを使用してアマゾンの書籍情報を取得する
    /// example:
    ///    System.Xml.XmlDocument itemDoc = AsinBookManager.findByAsin("4003320212");
    ///    string asin     = AsinBookManager.getNodeTextDef( itemDoc, "/opt/ASIN",     "" );
    ///    string title    = AsinBookManager.getNodeTextDef( itemDoc, "/opt/ItemAttributes/Title",     "" );
    ///    string author   = AsinBookManager.getNodeTextDef( itemDoc, "/opt/ItemAttributes/Author",     "" );
    ///    string binding  = AsinBookManager.getNodeTextDef( itemDoc, "/opt/ItemAttributes/Binding",    "" );
    ///    string label    = AsinBookManager.getNodeTextDef( itemDoc, "/opt/ItemAttributes/Label",      "" );
    ///    string newPrice = AsinBookManager.getNodeTextDef( itemDoc, "/opt/ItemAttributes/ListPrice/FormattedPrice", "" );
    ///    string imageUrl = AsinBookManager.getNodeTextDef( itemDoc, "/opt/ImageSets/SwatchImage/URL", "" );
    ///    string linkUrl  = AsinBookManager.getNodeTextDef( itemDoc, "/opt/DetailPageURL", "" );
    /// </summary>
    /// 
    /// <param name="asin">	検索キー(ASIN)</param>
    /// <returns>書籍情報</returns>
    //*********************************************************************-
    public static XmlDocument findByAsin( string asin ) {
        string url = "http://api.dan.co.jp/asin/" + asin + ".xml";
        using ( XmlReader reader = XmlReader.Create( url ) ) {
            XmlDocument doc = new XmlDocument();
            doc.Load( reader );
            return doc;
        }
    }
 
    //*********************************************************************
    /// <summary> XmlDocumentから指定されたノードの情報を取得する
    /// </summary>
    /// <param name="doc">          取得対象のxml情報</param>
    /// <param name="xpath">        取得するノードのxpath式</param>
    /// <param name="defaultValue"> 指定したノードが無かったときの値</param>
    /// <returns>ノードのテキスト(InnerXml)</returns>
    //*********************************************************************
    public static string findNodeTextDef( XmlDocument doc, string xpath, string defaultValue ) {
        XmlNode node = doc.SelectSingleNode( xpath );
        if ( node == null ) {
            return defaultValue;
        } else {
            return node.InnerText;
        }
    }
 
    //*********************************************************************
    /// <summary> プライマリの商品画像一式を取得する
    /// example:
    ///     // System.Xml.XmlDocument itemDoc = AsinBookManager.findByAsin("4003320212");
    ///     System.Xml.XmlDocument itemDoc = AsinBookManager.findByAsin("4774166138");
    ///     System.Xml.XmlNode primaryImageSet = AsinBookManager.getPrimaryImageSet( itemDoc );
    ///     string imageUrl =  primaryImageSet.SelectSingleNode( "MediumImage/URL" ).InnerText;
    /// </summary>
    /// <param name="doc">取得対象のxml情報</param>
    /// <returns>ノードのテキスト(InnerXml)</returns>
    //*********************************************************************
    public static XmlNode getPrimaryImageSet( XmlDocument doc ) {
        System.Xml.XmlNode primaryImageSet = null;
 
        // ImageSets/ImageSetがある場合は、primaryなImageSetを取得する
        System.Xml.XmlNodeList imageNodeList = doc.SelectNodes( "/opt/ImageSets/ImageSet" );
        foreach ( System.Xml.XmlNode curNode in imageNodeList ) {
            string category = curNode.SelectSingleNode( "Category" ).InnerText;
            if ( category != "primary" ) {
                continue;
            }
 
            primaryImageSet = curNode;
            break;
        }
 
        // ImageSets/ImageSetがなかった場合は、ImageSetsをprimaryとみなす
        if ( primaryImageSet == null ) {
            primaryImageSet = doc.SelectSingleNode( "/opt/ImageSets" );
        }
 
        return primaryImageSet;
    }
}




使い方は、各メソッドのコメントにも記載してありますが、以下のような感じです。

string keyAsin = "4774166138"; // "4003320212"
 
var itemDoc = AsinBookManager.findByAsin( keyAsin );
string asin     = AsinBookManager.findNodeTextDef( itemDoc, "/opt/ASIN",     "" );
string title    = AsinBookManager.findNodeTextDef( itemDoc, "/opt/ItemAttributes/Title",      "" );
string author   = AsinBookManager.findNodeTextDef( itemDoc, "/opt/ItemAttributes/Author",     "" );
string binding  = AsinBookManager.findNodeTextDef( itemDoc, "/opt/ItemAttributes/Binding",    "" );
string label    = AsinBookManager.findNodeTextDef( itemDoc, "/opt/ItemAttributes/Label",      "" );
string pubDate  = AsinBookManager.findNodeTextDef( itemDoc, "/opt/ItemAttributes/PublicationDate", "" );
string newPrice = AsinBookManager.findNodeTextDef( itemDoc, "/opt/ItemAttributes/ListPrice/FormattedPrice", "" );
string linkUrl  = AsinBookManager.findNodeTextDef( itemDoc, "/opt/DetailPageURL", "" );
 
System.Xml.XmlNode primaryImageSet = AsinBookManager.getPrimaryImageSet( itemDoc );
string imageUrl = primaryImageSet.SelectSingleNode( "MediumImage/URL" ).InnerText;




本ライブラリは、内部的にhttp://api.dan.co.jp/のAPIを使用しています。
Ajax – AWS Caching Proxy w/ Authentication Support

テストで使う場合はこのままでよいですが、本格的に使用するにはサーバ側も自前で用意してください。
サーバ側は、上記のリンク先にソースがあります。



ダウンロード:
AsinBookManager.zip

C#ではじめるWebサービスプログラミング

[WordPress]SiteGuard WP Pluginを使用時、管理画面に入れなくなった時にすること

WordPressで、管理画面のセキュリティを向上するためのプラグインにSiteGuard WP Pluginというものがあります。WordPressの管理画面に入るためのURLはデフォルトだとwp-adminなのですが、SiteGuard WP Pluginを使うとこのURLを変更することが可能です。

この機能は、通常は便利なのですが、バックアップから他のサイトにWordPressを移動させた際に、この機能のせいでDBからデータを戻しても、管理画面にログインできなくなってしまう場合があります。

管理画面へのログインURLをwp-adminに戻す方法

このような場合は、WordPressのDBを直接変更することで、ログインできるようになります。
SiteGuard WP Pluginの設定データは、wp_optionsテーブルの’siteguard_config’に入っているので、下記のような感じでデータを書き換えると、管理画面のURL書き換え先を変更できます。

まず、現状の設定値を確認します。

SELECT * FROM wp_options WHERE option_name = 'siteguard_config';



検索結果から、下記の部分を書き換えます。

"renamelogin_path";s:9:"foo-admin"
↓
"renamelogin_path";s:8:"wp-admin"



SQLで変更する場合は下記のupdate文で更新できます。

UPDATE   wp2_options
SET      option_value = REPLACE(option_value, "s:9:""foo-admin""", "s:8:""wp-admin""")
WHERE    option_name  = 'siteguard_config';



ドメインも同時に変更した場合に変更すべきoption_valueは?

ドメインを変更した場合は、上記に加えて、他にwp_optionsのoption_nameがsiteurlとhomeのレコードも変更が必要な可能性があります。
例えば、XAMPPなどを利用して自分のPCにコピーを作る場合は、”http://localhost”に書き換えるとよいです。こちらもSQLで更新したい場合は、下記のupdate文を実行します。

UPDATE wp2_options SET option_value = 'http://localhost' WHERE option_name = 'siteurl';
UPDATE wp2_options SET option_value = 'http://localhost' WHERE option_name = 'home';


[PHP]PHP5におけるgotoの利用について

PHPでは、PHP5.3以降でgoto文をサポートしました。

echo "prog1\n";
goto END_LABEL;
 
echo "prog2\n";
 
END_LABEL:
echo "prog3\n";



手元のPHP5.3.3環境で上記プログラムの実行すると、結果は以下のようになり、確かにprog2の処理がgotoで飛ばされています。

$ php -v
PHP 5.3.3 (cli) (built: Feb  9 2016 10:36:17)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
 
$ php test01.php
prog1
prog3




但し、PHPのgotoはC言語などと異なり、下記の制限があります。

対象となるラベルは同じファイル上の同じコンテキストになければなりません。 
関数やメソッドの外へgotoで移動することはできない。(逆も同じ)
 
ループやif, switchの中にもgotoで入れません。
ループやif,switchから抜け出すことは可能です。



よく使われるシチュエーションとしては、多段のループ、条件式から一度に抜け出したいときが多いです。