Zend Frameworkでプロジェクトの雛形作成
個人でPHPを利用してWebサイトを作りたい、他にはあまり無いですが仕事でPHPを利用してサイトを構築しなければならない、という事もあるかもしれません。今回はPHPの代表的なFrameworkのZend FrameworkでCLIツールを使用してサイトの雛形を作成する手順を説明します。
framework.zend.comCLIツールの使用 - Zend_Tool_Framework
サイトの雛形作成にはZend FrameworkのZendToolを使って作成します。使用できるコマンドは以下を参考にしてください。
ZFコマンド一覧
コマンド | 説明 |
---|---|
zf show version | Zend Frameworkのバージョンを表示します。 |
zf show phpinfo | phpinfoを表示します。 |
zf show manifest | manifestを表示します。 |
zf show profile | profileを表示します。 |
zf create project [作成するパス] | Zendのプロジェクトを作成します。パスを指定しない場合は作成するパスの問い合わせが表示されます。 |
zf show project | プロジェクトを表示します。 |
zf create controller [コントローラ名] | Zend Frameworkのcontrollerを作成します。コントローラ名を指定しない場合は作成するコントローラ名の問い合わせが表示されます。 |
zf create action [アクション名] | Zend Frameworkのactionを作成します。アクション名を指定しない場合は作成するアクション名の問い合わせが表示されます。 |
zf create view [コントローラ名] [アクション名] | Zend Frameworkのviewを作成します。コントローラ名とアクション名を指定しない場合は作成するコントローラ名とアクション名の問い合わせが表示されます。 |
zf create module [モジュール名] | Zend Frameworkのmoduleを作成します。モジュール名を指定しない場合は作成するモジュール名の問い合わせが表示されます。 |
では実際にサイトの雛形を作成してみましょう。ZFコマンドはcreate projectを使用します。プロジェクト名は「hoge」で作成を行います。
$ zf create project hoge
作成されたプロジェクトの構成は以下を参考にしてください。これで雛形が作成されました。雛形ではデフォルトコントローラのIndexコントローラのみ作成されていますので、ControllerとViewを追加して実装することになります。
hoge |-- application | |-- Bootstrap.php | |-- configs | | `-- application.ini | |-- controllers | | |-- ErrorController.php | | `-- IndexController.php | |-- models | `-- views | |-- helpers | `-- scripts | |-- error | | `-- error.phtml | `-- index | `-- index.phtml |-- library |-- public | |-- .htaccess | `-- index.php `-- tests |-- application | `-- bootstrap.php |-- library | `-- bootstrap.php `-- phpunit.xml
index.phtmlにスタートページのタグを書き込み、配置すれば簡単なWebサイトの完成です。業務アプリであればIndexController.phpで認証処理を行い認証がNGの場合、ログイン処理を行ってからコントローラへのリダイレクトを行うなどの実装が必要になると思います。
framework.zend.comプロジェクトを作成 - Zend Framework Quick Start
これでプロジェクトの雛形作成の解説は終わりです。Zend Frameworkはリファレンスドキュメントが充実しており公式サイトで事細かに解説がなされています。PHP初心者でも作成はしやすいと思いますので、チャレンジしてみてはいかがでしょうか。
Androidでタップの制御
アプリ開発をしているとほぼ必ず行うタップの制御。
今回はこのタップ制御を使いやすくしてみようと思います。
とりあえず普通に作っていくと、以下のようになると思います。
private boolean isTap = false; Button button = new Button(getApplication()); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(isTap) return; isTap = true; [何かしらの処理] isTap = false; } });
これでも特に問題はないのですが、ボタンが複数ある場合にはそれぞれに処理を書かないといけないですし、フラグの管理を間違えたらボタンが反応しなくなってしまうなどのバグが出てしまいます。
そんな問題を少しでも解消するために少し一工夫をしてみましょう。
まずはベースとなるActivity、もしくはFragmentを作成します。
そして、自動解除用のHandlerとRunnable、タップ管理用のMapを用意します。
public class BaseFragment extends Fragment{ // 自動解除用Handler private Handler handler = new Handler(); // 遅延実行のためのRunnable private Runnable runnable = null; // タップ管理用Map private Map<String, Boolean> map = new HashMap(); }
Activity、FragmentのonPauseでフラグの解除を行っておきます。
@Override public void onPause() { super.onPause(); map.clear(); if (null != runnable) { handler.removeCallbacks(runnable); } }
次にタップ管理の処理を書いていきます。
// タップ制御開始 protected void executeEvent(String key) { map.put(key, true); } // タップ制御開始(自動解除) protected void executeEvent(String key, long delay) { map.put(key, true); runnable = new Runnable() { @Override public void run() { finishEvent(key); } }; handler.postDelayed(runnable, delay); } // タップ制御中かどうか protected boolean isEvent(String key) { return map.containkey(key); } // タップの制御解除 protected void finishEvent(String key) { map.remove(key); }
ベースクラスを継承したActivity, Fragmentであれば以下のように使用することができます。
// 手動解除 Button button = new Button(context); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(isEvent("blockKey")) return; executeEvent("blockKey"); [何かしらの処理] finishEvent("blockKey"); } }); // 自動解除 Button button = new Button(context); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(isEvent("blockKey")) return; executeEvent("blockKey", 1000); // 1秒後に自動解除 [何かしらの処理] } });
単純なタップ制御なら、自動解除が結構使いやすくて便利だと思います。
タップの管理はMapで行っているので、複数ボタン別々のイベントも簡単に実装できます!
以上、タップ制御でした!
【iOS 10】Speech Frameworkで音声認識
先月行われたWWDCでiOS 10が発表され、DeveloperにはすでにiOS 10とXcode 8のベータ版が配布されています。
今回は、iOS 10で実装されたSpeech Frameworkを紹介します。
Speech Frameworkとは
iOS 10から対応するAppleが純正のAPIとして公開した音声認識Frameworkです。
今までiOSで音声認識を使う場合、サードパーティの音声認識ライブラリを使う必要がありましたが、今後はSpeech Frameworkを利用することで、サードパーティに頼る必要がなくなります。
公式のサンプルコード
Using Speech Recognition with AVAudioEngineというサンプルコードがAppleから公開されています。
プロジェクトファイルを開くためにはXcode 8が必要になります。
使い方
音声認識を行うためには"SFSpeechRecognizer"というクラスを使うようです。
初期化の引数としてLocaleクラスを渡します。
// 識別言語を日本語とする private let speechRecognizer = SFSpeechRecognizer(locale: Locale(localeIdentifier: "ja_JP"))!
続いて、Delegateを設定します。
speechRecognizer.delegate = self
Delegateメソッドは以下のものが存在し、Recognizerのステータスが変化した場合に呼び出されるようです。
availableにRecognizerが使用可能か否かの値が入っています。
optional public func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool)
音声認識を使用する場合は権限の許可を得なければならないので、
SFSpeechRecognizerクラスのrequestAuthorizationメソッドで、権限を求めるアラートを表示します。
サンプルでは、許可されている場合に認識を開始するボタンを有効にする処理になっていました。
SFSpeechRecognizer.requestAuthorization { authStatus in // 音声アクセス許可を求める OperationQueue.main().addOperation { switch authStatus { case .authorized: // 許可 case .denied: // 拒否 case .restricted: // 設定などにより制限されている case .notDetermined: // 未認証 } } }
音声入力を使う場合
認識リクエスト
// プロパティで宣言する private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest? // 初期化 recognitionRequest = SFSpeechAudioBufferRecognitionRequest() guard let recognitionRequest = recognitionRequest else { fatalError("Unable to created a SFSpeechAudioBufferRecognitionRequest object") } // 確定前の結果を取得する recognitionRequest.shouldReportPartialResults = true
音声ファイルを使う場合
// プロパティなどで宣言 private var recognitionRequest: SFSpeechURLRecognitionRequest? // 初期化 // URL recognitionRequest = SFSpeechURLRecognitionRequest(url: URL(string: "http://xxxxxxxxxxxxxxxx")!) // ローカルファイル recognitionRequest = SFSpeechURLRecognitionRequest(url: URL(fileURLWithPath: localPath))
タスクの設定
タスクを設定し、値を受け取った場合の処理をBlockで渡します。
// プロパティなどで宣言 private var recognitionTask: SFSpeechRecognitionTask? // タスクを設定 recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in if let result = result { // 結果を取得できた場合の処理 } if error != nil { // 取得エラー } }
resultはSFSpeechRecognitionResultオブジェクトになっています。
// 認識したテキストの取得 result.bestTranscription.formattedString // 終了検知 result.isFinal
音声ファイルを使用する場合は以上で終了です。
認識の開始
AVAudioEngineによりマイクからの音声入力を取得します。
// プロパティなどで宣言 private let audioEngine = AVAudioEngine() // 初期化 audioEngine = AVAudioEngine() guard let inputNode = audioEngine.inputNode else { fatalError("Audio engine has no input node") } // 音声取得形式を設定 let recordingFormat = inputNode.outputFormat(forBus: 0) // 音声入力を受け取った際に、SFAudioBufferRecognitionRequestに受け取ったバッファを追加します。 inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in self.recognitionRequest?.append(buffer) } // 音声の取得を開始します audioEngine.prepare() try audioEngine.start()
注意点
上記のコードで簡単に実装できるSpeech Frameworkですが、いくつか問題点があるようです。 調べて分かった問題点を幾つか紹介します。
・インターネット環境必須
・回数制限あり
・連続使用は1分まで
使ってみて
実際にiPhoneに向かって喋ってみましたが、かなり高い精度で認識してくれているようです。 50文字程度の文章を音読してみましたが、誤字などは無く、識別率100%となりました。
PHPのよくある質問集
WEB構築用の言語としてはやや下火になってきた感がありますが、PHPのよくある質問をまとめてみました。
質問:if文の条件に「==」と「===」って使えるけどこれ何なの…。
回答:「==」は型までは比較しない比較演算子です。「===」の場合は型まで厳密にチェックします。「==」は内部で型の変換が行われるので遅くなります。速度面で速くなる「===」を使いましょう。
例: $a = 1; if ($a == ‘1’) { // 一致 } if ($a === ‘1’) { // 一致しない }
質問:文字列を定義するときに「’」と「”」の2通りあるけどこれ何なの…。
回答:「’」で定義したときは変数の展開はされません。「”」で定義したときは変数が展開されます。変数を展開したいときは「”」を、それ以外のときは「’」を使いましょう。変数展開するときに英字と混ざってわかりにくくなるときがあります。その場合「{}」で囲って記述します。
変数展開の例: $name = ‘田中’; echo “わたしは @{name} です”; 出力結果 わたしは 田中 です
質問:for文遅くね?これ何なの…。
回答:PHPのfor文は結構遅いです。あまりよろしくないコードだとcount関数でループの度に値を取得したりする人がいます。このようなダメコードを排除するためにもforeachを使いましょう。
悪い例: $arr = array(1, 2, 3); for ($i = 0; $i < count($arr); i++) { // 処理 } 良い例: $arr = array(1, 2, 3); foreach ($arr as $key => $value) { // 処理 }
質問:foreachだと最初と最後の要素わからなくね?それでもforeach使うの…。
回答:配列ポインタの先頭と最後に移動するreset関数とend関数があります。これを使いましょう。
例: $arr = array(1, 2, 3); // 先頭の要素を取り出す echo reset($arr); // 先頭の要素を取り出す echo end ($arr);
質問:require, require_once, include, include_onceを書きすぎると遅くなってない?気のせいかな…。
回答:気のせいではありません。まずパスの指定が参照パスになっていないか確認しましょう。参照パスで指定したときは、そのファイルが見つかるまでinclude_pathを探しにいきます。その結果無駄なコストが発生しているのです。 require, require_once, include, include_onceは絶対パスで記述をしましょう。
例: 悪い例: require_once ‘PEAR.php’ 良い例: require_once ‘/usr/share/pear/PEAR.php’
質問:PHPってメソッドのオーバーロード書けなくね?これ何なの…。
回答:PHPではオーバーロードはサポートされていません。その代わりに引数を省略可能とする引数のデフォルト値を定義することができますので、こちらを使用しましょう。またPHP5.6以降を使用するのでしたら可変長引数リストを使用することもできます。
引数のデフォルト値の例: function hoge($arg = null) { if (!is_null(@arg)) { // 処理 } } 可変長引数リストの例: function hoge(...$args) { foreach ($args as $arg) { // 処理 } }
質問:PHPでも春分、秋分の日って日付型で計算できるの?
回答:PHPでも一般に公開されている計算式で算出できます。以下が春分、秋分の日付を計算するサンプルとなります。
/** * 春分日を算出します。 * @param int $year 算出対象となる年 * @return DateTime 春分日 */ public function CalcVernalEquinoxDay(@year) { // 2000年の太陽の春分点通過日 @baseDate = 20.69115; // 春分点通過日の移動量 = (西暦年 - 2000年) * 0.242194 @movement = (@year - 2000) * 0.242194; // 閏年によるリセット量 = (int)((西暦年 - 2000年) / 4) @leapYearOffset = (int)((@year - 2000) / 4.0); // 春分日 = (int)((1)+ (2) - (3)) @vernalEquinoxDay = (int)(@baseDate + @movement - @leapYearOffset); $date = new DateTime(); return $date->setDate(@year, 3, @vernalEquinoxDay); } /** * 秋分日を算出します。 * @param int $year 算出対象となる年 * @return DateTime 秋分日 */ public function CalcAutumnalEquinoxDay(@year) { // 2000年の太陽の秋分点通過日 @baseDate = 23.09; // 秋分点通過日の移動量 = (西暦年 - 2000年) * 0.242194 @movement = (@year - 2000) * 0.242194; // 閏年によるリセット量 = (int)((西暦年 - 2000年) / 4) @leapYearOffset = (int)((@year - 2000) / 4.0); // 秋分日 = (int)((1)+ (2) - (3)) @autumnalEquinoxDay = (int)(@baseDate + @movement - @leapYearOffset); $date = new DateTime(); return $date->setDate(@year, 9, @autumnalEquinoxDay); }
Androidでも音声認識
先日のWWDCでSiriのAPIを公開するという発表がありました。
これでサードパーティのアプリに組み込むことができるようになったので、今後音声を利用したアプリが増えてきそうですね。
そこで、Androidでも音声を使ってみようと思います!
AndroidMから音声認識のAPIが使えるようになっているので、今回はそれを利用したサンプルアプリのご紹介です。
Voice Interaction API
https://developers.google.com/voice-actions/interaction/voice-interactions
まずは普通にプロジェクトを作成。
次にAndroidManifet.xmlに以下のようにintent-filterを作成します。
<intent-filter> <action android:name="android.media.action.STILL_IMAGE_CAMERA" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.VOICE" /> </intent-filter>
音声コマンドで立ち上がった場合は、isVoiceInteraction()にtrueが返却されるので、以下のように判定します。
@Override protected void onResume() { super.onResume(); if (isVoiceInteraction()) { startVoiceTrigger(); } }
getVoiceInteraction().submitRequestで音声確認を要求します。
ここが実際に音声のやりとりを行う所になります。
PickOptionRequest.Optionで発声する文字のリストを作成します。
(リストに含まれている文字が発呼されたらonPickOptionResultにコールバックされます)
今回は「select pet」という要求が表示され、「dog」もしくは「cat」といった場合のみトーストが表示されます。
PickOptionRequest.Option[] options = new PickOptionRequest.Option[] { new PickOptionRequest.Option("dog", 0), new PickOptionRequest.Option("cat", 1) }; getVoiceInteractor().submitRequest( new VoiceInteractor.PickOptionRequest(newVoiceInteractor.Prompt("select pet"), options, new Bundle()) { @Override public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) { if (finished) { Toast.makeText(getApplicationContext(), "Ok", Toast.LENGTH_SHORT).show(); } } } );
ここまで作成したらアプリを実行。
ホーム画面で「ok google」。
そして、「take a selfie」でアプリを起動することができます。
アプリが立ち上がると、「select pet」と聞かれるので、
今回は「cat」と発声!(「dog」でも可)
すると、onPickOptionResultが呼ばれてトーストが表示されました!
以上、簡単ですがAndroidでの音声操作の利用方法でした。
音声を使用すれば、手を使わなくてもスマホを操作できるようになるので、手が使えないときの画面のスクロールや切り替えなどがしやすくなるかも?(料理中とか?)
下記URL先を見れば、手順を踏みながら作成することもできるので、興味ある方はぜひ触ってみてください。
http://io2015codelabs.appspot.com/codelabs/voice-interaction#1
ではでは~~。
【Swift】KVCを用いたJSONパーサー
REST-APIやWebSocketで通信するアプリを作っているとどうしても通らないといけないのがJSONのパース。
普通に実装するとこのようになると思います
class Object: NSObject { dynamic var str0: String = "a" dynamic var str1: String = "b" dynamic var str2: String = "c" dynamic var num0: Int = 0 dynamic var num1: Int = 1 dynamic var num2: Int = 2 init(dictionary: [String : AnyObject]) { super.init() str0 = dictionary["str0"] as! String str1 = dictionary["str1"] as! String str2 = dictionary["str2"] as! String num0 = dictionary["num0"] as! Int num1 = dictionary["num1"] as! Int num2 = dictionary["num2"] as! Int } }
しかし、APIが増えるごとにそれぞれのモデルごとにinitを書いているのは正直疲れます。 今回はKVC(Key Value Coding)を用いて、initを書かずにJSON⇔モデルクラスを実現する方法をSwiftで紹介します。
KVCとは?
キー値コーディングとは、オブジェクトのプロパティに間接的にアクセスするための仕組みです。ア クセサメソッドを呼び出してアクセスしたり、インスタンス変数としてアクセスするのではなく、プ ロパティの識別に文字列を使用してアクセスします。 引用元: https://developer.apple.com/jp/documentation/KeyValueCoding.pdf
要は、プロパティに対して.str1のようにアクセサメソッドやインスタンス変数でアクセスするのではなく、 "str1"といった文字列でプロパティに対してアクセスする手法です。 Objective-CではこのKVCを使用してプライベートなAPIを無理やり叩くこともできました。(Swiftではやったことないけど...)
プロパティ名の取得
オブジェクトからJSONにマッピングするためにはKeyとなる物が必要なので、 JSONのKeyに対応するプロパティ名をすべて取得します。
class KVCModel: NSObject { class func allKeys() -> [String] { var names: [String] = [] var count: UInt32 = 0 self.classForCoder() let properties:UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(self.classForCoder(), &count) for i: UInt32 in 0..<count { let property = properties[Int(i)]; let cname = property_getName(property) let name = String.fromCString(cname) names.append(name!) } free(properties) return names } }
self.classForCoder()
では自身のクラスを取得します。
Objective-Cでいうところの
+ (Class)class;
に対応しています。
イニシャライザでマッピングする JSON->モデル
イニシャライザにJSONから取り出したDictionaryを渡すことで自動的にプロパティに値が設定されるようにします。
class KVCModel: NSObject { init(dictionary: [String : AnyObject]) { super.init() if dictionary.count == 0 { return } let allPropertyKeys = self.dynamicType.allKeys() for key: String in allPropertyKeys { let val = dictionary[key] if val != nil { self.setValue(val, forKey: key) } } } }
self.dynamicType
でも自身のクラスを取得していますが、 こちらはObjective-Cでは
- (Class)class;
に対応しています。
Dictionaryで取り出す モデル->JSON
最後に以下のメソッドを追加し、モデルのプロパティをDictionaryとして取り出せるようにします。
func dictionaryObject() -> [String : AnyObject] { let allKeys = self.dynamicType.allKeys() return self.dictionaryWithValuesForKeys(allKeys) }
動作
以上までで作成したクラスを継承し、モデルクラスを作成しましょう。
class Object: KVCModel { dynamic var str0: String = "" dynamic var str1: String = "" dynamic var str2: String = "" dynamic var num0: Int = 0 dynamic var num1: Int = 0 dynamic var num2: Int = 0 }
テストコード
let dic = ["str1" : "str", "str2" : "strstr", "num1": 1, "num2" : 2] var jsonData: NSData = NSData() var jsonDic: [String : AnyObject] = [:] // Dictionary => JSON do { jsonData = try NSJSONSerialization.dataWithJSONObject(dic, options: .PrettyPrinted) } catch { // なんかやる } // JSON => Dictionary do { jsonDic = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as! Dictionary } catch { // なんかやる } // Dictionary => Model let model: Model = Model(dictionary: jsonDic) print("str1 : \(model.str1)") print("str2 : \(model.str2)") print("num1 : \(model.num1)") print("num2 : \(model.num2)") print("dic : \(model.dictionaryObject())")
ログ
str1 : str str2 : strstr num1 : 1 num2 : 2 dic : ["num2": 2, "str2": strstr, "num1": 1, "str1": str]