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

2022年12月6日火曜日

プリペアードステートメントにIN()を使いたい時

以前からSQLのINをプリペアードステートメントで使うたびに躓くのだけど、これを機会に正攻法を確認しておく。

1)プリペアードステートメントを以下のように作成する

$params = array('aaa', 'bbb');
$in_param = implode(',', array_fill(0, count($params), '?');
$sql = '...WHERE IN('.$in_param.')';
$stmt = $mysqli->prepare($sql);

2)バインドする

$stmt->bind_param(str_repeat('s', count($params)), ...$params);

ポイントは、PHP5.6から使えるようになった「... による可変個引数関数」

参照「[PHP] mysqliのbind paramに可変する引数を渡す」

2020年5月13日水曜日

PHPで$_POSTが消える

PHPで組まれたあるシステムのPOSTした先のページで、特定条件でエラーが発生するという報告を受けました。

発生しているエラー内容は、本来数値であるべきところに数値以外の値が入力されたというような内容です。
エラーが発生しない場合もあるので、単純な書き間違いではないことは明らかです。

エラーが発生している箇所に係るPOST変数の内容を確認したところ、emptyでした。
数値が入っているべき変数がemptyだったために発生しているということです。

特定条件でemptyになるということは、どこかで上書きしてるとか…。
しかしながら、そのような箇所は存在しませんでした。

次に、エラーが発生しているページで$_POSTをvar_dumpしてみました。
確かに、当該POST変数がなくなっています。
$_POSTのダンプをみていたら、配列の次元が1000になっていることに気づきました。
「これは怪しい」
調べたところ、POST変数の数に上限を設定できる機能があり、php.ini等に設定できるようです。
このディレクティブを使うと、ハッシュの衝突を悪用したサービス不能攻撃を受ける可能性を軽減できます。
とのことで、phpinfo()で現在の値を確認すると、1000ということで、どうやら間違いなさそう。

当該ディレクティブ本来の意義から、最低限の設定値を見積もって再設定したところ、無事すべてのPOST変数を受信できました。

2017年9月5日火曜日

PHPからGeographicLibを使う

回転楕円体面上(地球表面上)の正多角形を見て、SWIGを使ってPHPからGeographicLibを使おうと企図。
動作環境:エックスサーバ

SWIGのインストール

~/tmpにダウンロード・展開して、~/localにインストールする。

$ cd tmp
$ wget http://prdownloads.sourceforge.net/swig/swig-3.0.12.tar.gz
$ tar xzf swig-3.0.12.tar.gz
$ cd swig-3.0.12
$ ./configure --prefix=$HOME/local
$ make
$ make install

~/local/binにPATHを通すために、.bashrcに以下を追加

export PATH=/home/user/local/bin:$PATH
export CPLUS_INCLUDE_PATH=/home/user/local/include:$CPLUS_INCLUDE_PATH

※userは、それぞれの環境に合わせて変更

.bashrcを再読込
$ source ~/.bashrc

コマンドの実行を確認
$ swig -version

SWIG Version 3.0.12
(省略)

GeographicLibのインストール

$ wget https://sourceforge.net/projects/geographiclib/files/distrib/GeographicLib-1.48.tar.gz
$ tar xzf GeographicLib-1.48.tar.gz
$ cd GeographicLib-1.48
$ ./configure --prefix=$HOME/local
$ make
$ make install

PHPの実行モジュール作成

インターフェースファイル [myproject.i] を準備したら、以下を実行
$ swig -c++ -php myproject.i
php7の場合:$ swig -c++ -php7 myproject.i

さらに、C++のプログラム [myproject.cpp] を準備して、以下を実行
$ g++ -fpic -c myproject.cpp
$ g++ `/opt/php-7.0.18/bin/php-config --includes` -fpic -c myproject_wrap.cxx
$ g++ -shared *.o -o myproject.so -Wl,-rpath=/home/user/local/lib -L/home/user/local/lib -lGeographic

実行モジュール [myproject.so] ができていることを確認。

php.ini に、実行モジュールの配置場所を設定
extension = /home/user/local/lib/php/7.0.18/extensions/myproject.so

使用例

require_once "Geodesic.php";
$lat2 = new_doublep();
$lon2 = new_doublep();
$poly=[];
for($i=0; $i<$nVertices; $i++) {
    $dir = 90 - $i*360 / $nVertices;
    Geodesic::circle($center[0], $center[1], $radius, $dir, $lat2, $lon2);
    $poly[] = sprintf("%f %f", doublep_value($lon2), doublep_value($lat2));
}

2015年3月17日火曜日

PHPのIF文で、はまる

PHPのIF文の2つの異なる構文、即ち、

if ( 「論理式」 ) {
  「真の処理」
}else {
  「偽の処理」
}

および

if ( 「論理式」 ) :
  「真の処理」
else :
  「偽の処理」
endif;

において、下の構文の処理内に、上の構文をいれた以下のようなコードを書いたところ、文法エラーが発生した。

if ( 「論理式1」 ) :
  if ( 「論理式2」 ) {
    「処理1」
  }
else :
  「処理2」
endif;

Parse error: syntax error, unexpected ':' in XXX(else : の行)
あれ、どこかで括弧閉じ忘れたかな、としばらく右往左往・・・

以下の様に変更すると、エラーは発生しない。

if ( 「論理式1」 ) :
  if ( 「論理式2」 ) :
    「処理1」
  endif;
else :
  「処理2」
endif;

以下のリンク先ページにある、「注意同じブロック内で別の構文を混ぜて使うことはできません。」というのは、このことなのか。

http://php.net/manual/ja/control-structures.alternative-syntax.php

ただし、これを以下の様にしてもエラーは発生しなかった。

if ( 「論理式1」 ) :
  if ( 「論理式2」 ) {
    「処理」
  }else {
    「空文」
  }
endif;

組み合わせによって、エラーが発生したりしなかったりするようだ。まあ、そもそも別の構文を混ぜちゃだめだと言われれば仕方ないけど、通っちゃう場合もあるというのは混乱を招くよね。

2015年3月13日金曜日

Opauthでソーシャルログインを実装する

サイトで会員登録するのは自分でも抵抗があるので、ソーシャルログインを実装してみた。

Opauthは、PHPにおける認証処理を標準化してくれるフレームワークだ。

https://github.com/opauth/opauth

上記サイトから、ZIPをダウンロードして展開したら、

1) libをサイトのドキュメントルートにコピーする。
2) 認証用として例えばauthというディレクトリをドキュメントルートに作成する。
3) authの中には、展開したexampleフォルダの中の、index.php、callback.php、.htaccessおよびopauth.conf.php.defaultをコピーする。
4) opauth.conf.php.defaultをopauth.conf.phpにリネームする。
5) opauth.conf.phpの'path' => '/'を'path' => '/auth/'に書き換える。
6) opauth.conf.phpの'security_salt'にランダムな文字列を設定する。
7) opauth.conf.phpのStrategyを、例に従って記述する。

Strategyに記載するパラメーターは、各プロバイダのサイトでアプリケーション登録をすると得られる。

プロバイダのアプリケーション登録ページの例
Facebook
Twitter
Google+

プロバイダ毎のStrategyは以下からダウンロードして、lib/Opauth/Strategyの下にプロバイダ名のディレクトリを作成してコピーする。

https://github.com/opauth/opauth/wiki/List-of-strategies

例えば、Facebookであれば、

lib/Opauth/Strategy/Facebookの下に、FacebookStrategy.phpをコピーする。

Twitterの場合は、Vendor/tmhOAuthディレクトリもコピーする。

認証手続きを開始するには、opauth.conf.phpで設定した'path/(プロバイダ名)'にアクセスする。

私の場合は、Strategyが見つからないというエラーが出たので、opauth.conf.phpのStrategyに以下のパラメータを追加した。

例えば、Facebookであれば、
'Facebook' => array(
            'app_id' => '(app_id)',
            'app_secret' => '(app_secret)',
            'strategy_url_name' => 'Facebook'
)

Googleの場合は、Googleサイト側でError: redirect_uri_mismatchという画面が出てしまい、認証画面が表示されなかったので、以下のパラメータも追加してみた。

'Google' => array(
            'client_id' => '(client_id)',
            'client_secret' => '(client_secret)',
            'redirect_uri' => 'http://(ドメイン)/auth/Google/oauth2callback',
            'strategy_url_name' => 'Google'
)

なお、Googleは、https://console.developers.google.com/project でリダイレクトURLを設定する必要がある。

しかし、結局Googleで認証ができない。

試行錯誤しているうちに、なぜか認証できるようになった。

Googleは、GoogleApps for Workでも感じたが、設定しても反映するまでの遅延が大きいようなので、設定したらある程度時間を置く必要があるのかもしれない。24時間以上かかる場合もある?

サーバー間の同期に時間がかかっているのだろうか。設定方法に自信がない場合は、設定が終了するまでになんともいえない無為な時間が流れるのは、かなり困る。

アプリの設定に表示されないのも、そのせいかと思っていたら、アカウント権限という別ページで管理するようだorz

2015年3月4日水曜日

[PHPによるHTTP 認証]$_SERVER["PHP_AUTH_USER"]が取得できない

PHPによるHTTP 認証[php.net]ができることを知って、早速試してみたのだが、認証画面が繰り返し表示されてしまう。

$_SERVER["PHP_AUTH_USER"]が取得できていないようだ。

Webを検索してみると、さくらのレンタルサーバーだとPHPがCGIとして動作するため、うまく動作しないとの情報が。

よく見ると、php.netにも以下の情報がありました。

There are .htaccess which actually works for us (cPanel + phpsuexec) unless others failed. Perhaps it may help someone.

# PHP (CGI mode) HTTP Authorization with ModRewrite:
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]

Then you need small piece of php code to parse this line and then everything will work like with mod_php:

if (isset($_SERVER['HTTP_AUTHORIZATION']))
{
$ha = base64_decode( substr($_SERVER['HTTP_AUTHORIZATION'],6) );
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', $ha);
unset $ha;
}
でも、ロリポップではやはり動作しない。まあ、PHPで認証すればいいと言われればその通りだ。