忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2025/01/16 06:06 |
ZF2.0 Zend\Barcode
名前のとおり簡単にバーコードを作っちゃうよというパッケージです。
使い方は超簡単。
<?php
	require_once('../../../../channel/vendor/autoload.php');
	
	// バーコード種別
	$barcode = 'ean13';
	
	// レンダラ
	$renderer = 'image';
	
	// バーコードオプション
	$barcodeOption = array('text'=>'1234567890');
	
	// レンダラオプション
	$rendererOption = array();
	
	// 表示実行
	Zend\Barcode\Barcode::factory($barcode, $renderer, $barcodeOption, $rendererOption, false)->render();
これだけでバーコードが表示されます。
簡単すぎる。

オプションを指定する場合はバーコードオプション、レンダラオプションに配列で突っ込みます。
	// バーコードオプション
	$barcodeOption = array('text'=>'1234567890', 'barThinWidth'=>2, 'barHeight'=>100, 'foreColor'=>'#FF0000', 'backgroundColor'=>'#00FF00');
	// レンダラオプション
	$rendererOption = array('imageType	'=>'jpg', 'height'=>200, 'width'=>500, 'topOffset'=>50, 'leftOffset'=>100, 'moduleSize'=>100);
バーコードのサイズや色、画像自体のサイズといった諸々を指定可能です。
出力形式は画像以外にPDF、SVGが選択可能です。

日本で流通しているバーコードはJAN(EAN13)で数値のみを最大12桁格納可能です。
他にもCode128やITFなど十数種類のバーコードに対応しているので、色々試してみるのもいいでしょう。
バーコードなんて作って何に使うのかはわかりませんがね。

そして日本では一番需要が高いと思われるQRコードには残念ながら対応していません。
早いところの対応が待たれるところです。
PR


2012/11/23 23:59 | Comments(0) | PHP
PHP5.3 三項演算子と文字列演算子を同時に使用
<?php
	$loop = array(1,2,3);
	$return = array();
	foreach($loop as $val){
		$return[$val] = $val === 2 ? '10' : $val . '円';
	}
配列のそれぞれの値に'円'を付けます、ただし2円のやつだけ10円に値上げします。

//期待した値
array(3) {
  [1]=>
  string(4) "1円"
  [2]=>
  string(2) "10円"
  [3]=>
  string(4) "3円"
}
//実際の結果
array(3) {
  [1]=>
  string(4) "1円"
  [2]=>
  string(2) "10"
  [3]=>
  string(4) "3円"
}
あれ。

文字列演算子のほうが優先順位が高いため、
  $val === 2 ? '10' : ( $val . '円' );
と解釈されてしまいました。

  $val === ( 2 ? '10' : $val ) . '円';
とすれば解決です。

まあ、この場合はまだわかりやすいのですが、三項演算子を複数並べたり、マイナーな演算子を使ってたりすると予想外の結果になることがあります。
特にPHPの場合、a ? b : c ? d : eが( a ? b : c ) ? d : eとかなり直感に反する解釈をされてしまいます。
複雑な計算を行う場合は優先順位にかかわらず括弧をつけましょう。
というか、計算式はわかりやすく書けというそれだけの話。
私なら{}の中は、
	if($val === 2){
		$return[$val] = 10;
	}
	$return[$val] .='円';
と書きます。
というかこんな作り自体やめて、'円'は表示するところでくっつけますね。

あと関係ないけどforeach使えるところでfor使うやつは滅びてくれないかな。


2012/11/09 23:59 | Comments(0) | PHP
ZF2.0 Zend\Math\BigInteger
PHPの数値型、いわゆるintは有効数字が10桁とか20桁とかしかありません。
それを超えるとfloat型になり、精度が適当に切り詰められてしまいます。

もっと桁の大きな整数を精密な値で扱いたいというときは、任意精度整数を使用します。

ところが困ったことにPHPにはGMPBCMathという2種類の任意精度整数があり、関数名や使用法がばらばらです。
環境によってどちらかが入っていなかったりといったこともありえます。
面倒ですね。
ということで両者を同じように扱えるMath\BigIntegerです。
<?php
	require_once('../../../../channel/vendor/autoload.php');
	
	
	// 有効な任意精度整数アダプタを取得 ※どれも同じ
	$bigInteger = Zend\Math\BigInteger\BigInteger::factory();
	$bigInteger = Zend\Math\BigInteger\BigInteger::getDefaultAdapter();
	$bigInteger = Zend\Math\BigInteger\BigInteger::getAvailableAdapter();
	
	// 四則演算
	$add = $bigInteger->add('987654321098765432109', '123456789012345678901'); //和
	$sub = $bigInteger->sub('987654321098765432109', '123456789012345678901'); //差
	$mul = $bigInteger->mul('987654321098765432109', '123456789012345678901'); //積
	$div = $bigInteger->div('987654321098765432109', '123456789012345678901'); //商
	
	// その他の演算
	$pow = $bigInteger->pow('987654321098765432109', '10'); //累乗
	$sqrt= $bigInteger->sqrt('987654321098765432109'); //平方根
	$abs = $bigInteger->abs('987654321098765432109'); //絶対値
	$mod = $bigInteger->mod('987654321098765432109', '123456789012345678901'); //剰余
	
	// powしてmod
	$powmod = $bigInteger->powmod('987654321098765432109', '10', '123456789012345678901');
	// 比較演算子"<=>"
	$comp = $bigInteger->comp('987654321098765432109', '123456789012345678901');
	// バイナリに
	$bin = $bigInteger->intToBin('987654321098765432109');
	// バイナリからintに戻す
	$int = $bigInteger->binToInt($bin);
	// 任意進数に変換(最大62)
	$oct = $bigInteger->baseConvert('987654321098765432109', 10, 8);
BigInteger::getAvailableAdapter()等で、有効な任意精度整数を取得することができます。
add()の中身がbcadd()かgmp_add()か、あるいは他の見知らぬ関数であったりするかなどといったことを一切考える必要がなく、どんな環境でも簡単に使用できる便利な使用法となります。

なおGMPもBCMathも入っていない場合はさすがに無理なので、アダプタを取得しようとしたところで例外が発生して停止します。


2012/11/05 23:50 | Comments(0) | PHP
ZF2.0 Zend\Math\Rand
Zend\Math\Randは、ランダムな値を取得できるよ、という非常にわかりやすい機能を持つクラスです。

mt_srand()とかと何が違うのかというと、ランダムっぽい処理が色々使えるということでしょうか。

<?php
	require_once('path/to/channel/vendor/autoload.php');
	
	// true/false
	$strBoolean = Zend\Math\Rand::getBoolean();
	
	// int
	$randInt = Zend\Math\Rand::getInteger(0, 1000);
	
	// float
	$randFloat = Zend\Math\Rand::getFloat();
	
	// 文字列 [A-Za-z0-9+/]
	$randString = Zend\Math\Rand::getString(50);
	// 引数の文字だけを使う
	$randString2 = Zend\Math\Rand::getString(50, '0123456789abcdef');
	
	// バイト列
	$strBytes = Zend\Math\Rand::getBytes(50);
見てのとおり、簡単にランダムな値を得ることができました。
getBoolean()はtrueかfalse、getInteger()は整数値、getFloat()は0から1のfloat値、getString()は文字列を返してくれます。
実際はどれも内部でgetBytes()を呼び出しており、その結果を色々整形して返しています。
getBytes()はopenssl_random_pseudo_bytes()mcrypt_create_iv()chr(mt_rand(0, 255))の順で、使用可能な暗号化関数を用いてランダム値を生成します。

これでランダムな文字列が欲しくなったので自力で作ったはいいが実は何処かに脆弱性があった、なんて羽目にならなくて済むでしょう。

ちなみにどうでもいいのですが、Zend\Math\Randは継承するクラスもないのにあえてabstractクラスにすることによって、インスタンスの生成を防いでいるようです。
そんな使い方があったのか。


2012/10/29 23:28 | Comments(0) | PHP
PHP5.3 クロージャ
http://jp.php.net/manual/ja/functions.anonymous.php
> 無名関数はクロージャとも呼ばれ、 関数名を指定せずに関数を作成できるようにするものです。

???

実はクロージャの意味が未だによくわからないんですよね。
よくわからないのでとりあえず使ってみましょう。
例題は大昔に使ったusort()です。
	$product = array(
		array('name'=>'みかん','price'=>'60'),
		array('name'=>'りんご','price'=>'180'),
		array('name'=>'バナナ','price'=>'48'),
		array('name'=>'いちご','price'=>'590'),
		array('name'=>'ぶどう','price'=>'298'),
		array('name'=>'ドリアン','price'=>'5000')
	);
	
	usort($product,
		function($a,$b){
			if($a['price']==48){
				return -1;
			}else if($b['price']==48){
				return 1;
			}else{
				return 0;
			}
		}
	);
あれ、もしかしてcompare()のとこに全部突っ込んだだけ?
このような作りってjQueryでよく見ますね。
jQueryは大概クロージャであるという認識でよいのでしょうか。

というかこれはただの無名関数で、定義された環境がなんちゃらというクロージャの定義を満たしてない気がします。
そこんとこどうなのでしょう。
ということで呼び出す度にカウントを行うお約束なクロージャを作ってみます。

こちらのJavaScript用クロージャを剽窃参考に実装。
	$countPlus = function(){
		$cnt = 1;
		return function() use ($cnt){
			print($cnt++);
		};
	};
	
	$countPlus();
	$countPlus();
	$countPlus();
何も表示されません。
あれれ?
	function dummy(){
		$cnt = 1;
		return function() use ($cnt){
			print($cnt++);
		};
	};
	$countPlus = dummy();
	$countPlus();
	$countPlus();
	$countPlus();
「111」になってしまいます。
あれー?

あとJavaScriptみたいな
	$countPlus = function(){ /* */ }();
という定義直後の実行はできないみたいです。残念。


なお正解は、
	function dummy(){
		$cnt = 1;
		return function() use (&$cnt){
			print($cnt++);
		};
	};
	$countPlus = dummy();
	$countPlus();
	$countPlus();
	$countPlus();
です。
&が一文字増えました。

ちなみに、
	$countPlus = function(){
		static $cnt = 1;
		print($cnt++);
	};
	
	$countPlus();
	$countPlus();
	$countPlus();
こう書いても思ったとおりに動作します。

これは静的変数というもので、クロージャとは更に別物の話みたいです。


2012/10/26 23:59 | Comments(0) | PHP
ZF2.0 Zend\Log
Zend\Logを使うことで、簡単に、統一的なログを出力することができます。
<?php
	require_once('path/to/channel/vendor/autoload.php');

	//ログの出力先 今回はブラウザに出力
	$zendLogWriter = new Zend\Log\Writer\Stream('php://output');
	
	//フィルタ これより低いログは記録されない
	$zendLogFilter = new Zend\Log\Filter\Priority(Zend\Log\Logger::NOTICE);
	$zendLogWriter->addFilter($zendLogFilter);
	
	//フォーマッタ 指定した形式で出力
	$zendLogFormatter = new Zend\Log\Formatter\Simple('%timestamp% %priorityName% (%priority%): %message%<br />', 'Y/m/d H:i:s');
	$zendLogWriter->setFormatter($zendLogFormatter);
	
	//ログ
	$zendLogger = new Zend\Log\Logger();
	$zendLogger->addWriter($zendLogWriter);
	
	//ログを出力
	$zendLogger->debug ('デバッグメッセージ');
	$zendLogger->info  ('情報');
	$zendLogger->notice('注意');
	$zendLogger->warn  ('警告');
	$zendLogger->err   ('エラー');
	$zendLogger->crit  ('致命的エラー');
	$zendLogger->alert ('アラート');
	$zendLogger->emerg ('緊急');
Zend\Log\Writerで、ログの出力先を指定します。
予めデータベースやメール、SyslogやFirebugなんて出力先が用意されています。
またWriterInterfaceを継承して適当なログ出力先を追加することもできます。
今回はファイルに保存するStreamに対してファイル名のかわりに'php://output'を指定し、ブラウザに直接出力しています。

Zend\Log\Filterは、出力するログのフィルタリングを行います。
正規表現などのフィルタがあります。
今回使用したPriorityはログレベルによるフィルタで、NOTICE以上のログが出力されるようになります。
具体的にはDEBUGとINFOが非表示になります。
ログを出力する条件のデフォルトの演算子は'<='で、Zend\Log\Filter\Priorityの第二引数で指定可能です。
'>'とすると逆にDEBUGとINFOだけが表示されるようになります。

フィルタは複数設定することが可能で、「○○という文字が含まれる、CRIT以上のログ」といった条件が設定可能です。

Zend\Log\Formatterは、出力する文字の形式です。
Zend\Log\Formatter\Simpleは直接文字列を指定する形式で、他にはXML形式やPHPのエラー、例外フォーマットなどがあるようです。
フォーマッタを経由することで、統一的なログの形式を保証することが可能になります。

最後にZend\Log\Loggerにログを突っ込むと、これら設定に沿ったログを出力してくれるようになります。
今回はブラウザに、以下のように表示されます。
2012/01/01 10:00:00 NOTICE (5): 注意 
2012/01/01 10:00:00 WARN (4): 警告 
2012/01/01 10:00:00 ERR (3): エラー 
2012/01/01 10:00:00 CRIT (2): 致命的エラー 
2012/01/01 10:00:00 ALERT (1): アラート 
2012/01/01 10:00:00 EMERG (0): 緊急 
Zend\Log\Formatter\Simpleの書式に沿って、Zend\Log\Filter\Priorityのフィルタに従い、Zend\Log\Writer\Streamで指定した出力先に出力されました。
これでもうよくわからないfunction e()とかそういう代物に頼る必要がなくなりますね。


2012/10/19 23:06 | Comments(0) | PHP
ZF2.0 XAMPPにZend FrameworkF2.0をインストール
インストールとか言いましたが、実のところ前回
> php composer.phar install
した時点でZF2のインストールは終わっています。
ただ場所がわかりづらく、Composerを置いたディレクトリの中、vendor/zendframework/zendframework/library/Zendなんてところに本体があったりします。

なんでそんなわけのわからない場所に入ってるのだ、そんな深い場所いちいち読み込むのが面倒だろう、と思いきや、実はComposerにオートローダが用意されています。
<?php
	//オートローダ
	require_once('path/to/composer/vendor/autoload.php');
	
	//インスタンス
	$zendJson = new Zend\Json\Json();
	
	//実行
	$json = $zendJson->encode(array(1=>'hoge', 2=>'fuga'));
	var_dump($json);
最初にvendor/autoload.phpを指定するだけで、あとはComposerを使ってインストールしたファイルであれば何でもオートロードしてくれます。
というかvendor/autoload.phpをrequireした時点で全てのパスをspl_autoload_register()しているだけみたいです。
非常に便利な反面、中で何をやっているかわかりにくくなるのでちょっと気持ち悪いです。

なお、ZF2では全面的に名前空間が取り入れられています。
いやあ便利ですねnamespace。


ZF2の英語マニュアルができているのですが、現状URL的に少々使い勝手に難があります。

ZF1のマニュアルは、あえてバージョン表記を抜かすことで自動的に最新バージョンのURLに移動するのですが、ZF2ではそれが現在効きません。
http://framework.zend.com/manual/en/zend.acl.html
http://framework.zend.com/manual/1.12/en/zend.acl.html

http://framework.zend.com/manual/en/modules/zend.authentication.intro.html
http://framework.zend.com/manual/2.0/en/modules/zend.authentication.intro.html
ちょっと困りますね。


2012/10/15 23:06 | Comments(0) | PHP
ZF2.0 XAMPPにComposerをインストール
Zend Framework2.0が出ていました。
使ってみた人の記事を見つけたのですが、何語で話してるのかさっぱりわかりません。
仕方ないので自分でやってみます。

ZendFramework1はpear installでインストールできたのですが、ZF2では今のところそういうことはできないみたいです。
インストールガイドにはPyrusとかComposerとか意味の分からないことが書いてあってどうにもなりません。
いや、開発とかバージョン管理とかどうでもいいんで、あとWindowsなのでwgetとかcurlなんて洒落たものはないんです。

今回はComposerを使ってインストールしてみます。
というかComposerをインストールしてみます。
Composerは、JSONで設定ファイルを書けば適当にダウンロードしてくれてオートローダも書いてくれるよ、というもののようです。
Pearでよくね?と思いますが、Pearに対応していないはずのGithubとかの他所のサイトでも、設定ファイルを書けばよきにはからってくれるよ、というのがComposerの売りのようです。
とりあえず適当な場所にComposerをダウンロード。

> php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));"

どうなるかというと、単にcomposer.pharというpharファイルがひとつダウンロードされるだけです。
そのくらい普通にブラウザでダウンロードさせてくれよ。

このファイルがある時点でComposerのインストールは完了しているので、次はこれを使ってZF2をインストールします。
composer.pharと同じ場所にcomposer.jsonを作成し、中に↓をコピペ。
{
  "repositories": [
    {
      "type": "composer",
      "url": "http://packages.zendframework.com/"
    }
  ],
  "require": {
    "zendframework/zend-config": "2.0.*",
    "zendframework/zend-http": "2.0.*"
  }
  ,"minimum-stability": "beta"
}
次に以下のコマンドを実行すると、composer.jsonの中身を読んで、必要なものを自動的にインストールしてくれるみたいです。

> php composer.phar install

今回の設定では、zend-configとzend-http、および両者が依存しているパッケージをまとめてインストールしてくれます。
やったね。

と言いたいところですが、これZFのパッケージまとめて全部インストールできないっぽいんですができないの?
    "zendframework": "2.0.*",
    "zendframework/": "2.0.*",
    "zendframework/*": "2.0.*",

とか書いてみても動きませんでした。
結局全パッケージに対して70行くらい延々書き並べたんですが、絶対もっとましな方法があるだろ、これ。

一度インストールすると、そのインストール情報がcomposer.lockに書き込まれます。
このファイルを別環境に持っていってcomposer.phar installすると、その全く同じバージョンがインストールされます。
つまり、これを使えば同じ環境を簡単に用意することができるということのようです。

composer.jsonに追記を行ったり、インストールしたパッケージの更新をする場合は、
> php composer.phar update
とします。

今回はZendだけですが、Composerでは設定を書けば色々なサイトからファイルを取得してインストールすることが可能なようです。
ということで、これが真価を発揮するのはインストール時よりアップデート時になるでしょう。
JSONさえ書いてしまえばコマンド一発でアップデートとか便利ですね。

と思いきやたまにエラーが。

> [RuntimeException]
> Package could not be downloaded, 'svn' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチファイルとして認識されていません。


だからWindowsにはsvnなんて気の利いたコマンドはないんだってば。


2012/10/12 23:12 | Comments(0) | PHP
PHP5.3 Smartyのhtml_select_dateで任意の名前を指定
{html_select_date}はYYYY年MM月DD日という表現が一発ではできないというのは昔言いましたが、実はもっと不便なことがあり、実はフォームの名前に任意の値を設定できません。
たとえば{html_select_date prefix="hoge"}とすると
 <select name="hogeYear">
 <select name="hogeMonth">
 <select name="hogeDay">

という名前が自動的に設定されます。

{html_select_date field_array="hoge"}にすると、
 <select name="hoge[Date_Year]">
 <select name="hoge[Date_Month]">
 <select name="hoge[Date_Day]">

と配列形式にもできます。

しかし、何故か
 <select name="yearHoge">
 <select name="monthHoge">
 <select name="dayHoge">

といった任意の値を設定することはできません。

> 以下の属性リストに無いパラメータが与えられた場合は、 作成された年、月、日の各 <select> タグの内側に 名前/値 のペアで表されます。
ということで{html_select_date name="hoge"}にすると、
 <select name="Date_Year" name="hoge">
と残念なことに。

色々検討したのですが、どうやら直接ソースを変更する以外に対処法は見当たらないようでした。

まずデフォルト値設定に↓を追加。
    $year_name = "Year";
    $month_name = "Month";
    $day_name = "Day";


次に引数確認部分、
    $$_key = (string)$_value;
の直前に↓を追加。
    case 'month_name':
    case 'day_name':
    case 'year_name':


最後に
if ($display_years) {
    $_name = $field_array ? ($field_array . '[' . $prefix . 'Year]') : ($prefix . 'Year');
}


のところを、
if ($display_years) {
    $_name = $field_array ? ($field_array . '[' . $prefix . $year_name .']') : ($prefix . $year_name);
}

と変更します($display_months、$display_daysも同様)

これで、以下のように任意のnameにすることができます。

{html_select_date year_name="hoge"}
 <input name="Date_hoge">

{html_select_date year_name="hoge" field_array="fuga" prefix=""}
 <input name="fuga[hoge]">

見てのとおり10行かそこらの変更です。
どうして任意値対応してくれないんでしょうか。

ところでsmarty_function_html_select_date()を見ていたら、リファレンスに載っていないオプションを幾つか見つけました。

$all_id
$day_id
$month_id
$year_id

<select>にid属性を指定します。
未設定だとid属性自体無し、falseだとnameと同じ値、設定するとidが"入力値+nameの値"になります。

$all_empty
単純にyear_empty+month_empty+day_emptyです。

$option_separator
</option>と<option>の間にテキストを配置します。
画面上には表示されないのでCSSやJavaScript用でしょうか。


2012/10/05 22:34 | Comments(0) | PHP
PHP5.3 DB_DataObject::update()がユニークキーを更新できない
タイトル通りのファッキンな仕様。

テーブルを作る。
CREATE TABLE `test` (
 `c_primary` varchar(16) NOT NULL COMMENT '主キー',
 `c_unique` varchar(16) NOT NULL COMMENT 'ユニークキー',
 `c_index` varchar(16) NOT NULL COMMENT 'インデックス',
 `c_hoge` varchar(16) NOT NULL COMMENT '特に何もなし',
 PRIMARY KEY (`c_primary`),
 UNIQUE KEY `k_unique` (`c_unique`),
 KEY `k_index` (`c_index`)
)
とりあえず値を入れておく。
INSERT INTO test (c_primary, c_unique, c_index, c_hoge) VALUES ('a','b','c','d');
INSERT INTO test (c_primary, c_unique, c_index, c_hoge) VALUES ('w','x','y','d');
その後update()を実行。
	//DB_DataObject
		$table = DB_DataObject::factory('test');
	
	//取得
		$table->c_primary = 'a';
		$table->find();
	
	//値をセット
		$table->c_unique = 'e';
		$table->c_index = 'f';
		$table->c_hoge = 'g';
	
	//更新
		$table->update();
こうした場合、testテーブルの値は('a','e','f','g')('w','x','y','d')になってることを期待するわけですが、実際は1行もアップデートされていません。
実際に発行されたSQLがどうなっているかというと、
	UPDATE test SET c_index='f', c_hoge='g' WHERE (c_primary='a') AND (c_unique='e')
何故かユニークキーがWHERE側に回っています。
で、どうやらこれをどうにかする手段がないっぽいという驚愕の事実。

update()の引数に値を渡すことで動作を変更することができます。
	//aを取得
		$table->c_primary = 'a';
		$table->c_unique = 'b';
		$table->find();
	
	//wを取得
		$table2->c_primary = 'w';
		$table2->c_unique = 'x';
		$table2->find();
	
	//差分を更新
		$table->update($table2);
引数にDB_DataObjectを渡すことで、両者の差分のみをアップデートすることができます。
しかしこの場合も、差分といいつつ更新される可能性があるのはc_indexとc_hoge列だけです。
c_hogeは同じなので更新されるのはc_indexだけとなります。
	//WHERE句を指定
		$table->addWhere("c_primary='a'");
		$table->update(DB_DATAOBJECT_WHEREADD_ONLY);
addWhere()を指定後update(DB_DATAOBJECT_WHEREADD_ONLY)とすると、指定したWHERE句でupdateすることができます。
SQLがどうなったかというと、
	UPDATE test SET c_index='f', c_hoge='g' WHERE (c_primary='a')
ユニーク列どっか行きやがった。

結局テーブル定義変更するか自力でクエリを組むかとかいった、DB_DataObjectの利点を何も使えない解決方法をとるしかありません。
構成によっては非常に不便を強いられる作りでした。

まあ、今時DB_DataObjectなんて使ってる方が悪いんですがね。


2012/10/01 22:28 | Comments(0) | PHP

<<前のページ | HOME | 次のページ>>
忍者ブログ[PR]