必要なソフトウェア
- PHP5
- FFMpeg
使い方
cd /path/to/wma_files # wmaファイルを置いているディレクトリに移動
php convert_wma_to_mp3.php
感想
FFMpeg、最初は取っ付きづらいけど、一度覚えると色々捗る
PHPで書いたのは一番早く書けそうだったから
Perlの人ならワンライナーで書けそう
cd /path/to/wma_files # wmaファイルを置いているディレクトリに移動
php convert_wma_to_mp3.php
FFMpeg、最初は取っ付きづらいけど、一度覚えると色々捗る
PHPで書いたのは一番早く書けそうだったから
Perlの人ならワンライナーで書けそう
Phalcon + AngularJSで作る 動画プラットフォーム(クリックでスライド表示)
Phalcon Nightという勉強会で、登壇してきました。↑がスライドです(reveal.js製のスライドをGitHub Pagesで公開してる)。SlideShareにも後で上げると思います。
勉強会に誘われたのはTransifexのPhalcon翻訳チームのディスカッションだったので、オープンソースは参加してみるものだなー、と思いました。
Phalcon + AngularJSというタイトルなんですが、実際はAngular側で特別な何かをする必要はなかったりします(なのでスライドにもAngularの話はほとんど出てきません)。
一方、Phalcon側からすると、VoltというPhalcon組み込みのテンプレートエンジンが、Angularのシンタックスとバッティングする(どちらも「{}」という記法を使う)という問題があります。他の登壇者の方のお話を伺った印象だと、Voltは結構バグがあるみたいなので、全面的にAngularJSを使ったのは(結果的には)正解だったみたいですね。
同行者からは「スライドの文字が小さかった」というフィードバックも貰いました。スライド自体はそれなりに大きなフォントサイズだったんですが、プロジェクタへの写し方がまずかったみたいです…。次に発表する際は、その場での写りもちゃんと確認しようと思います。
If you want to translate English documentation into your native language, please request join a translation team at Transifex.翻訳チームへの参加方法は以下のとおり。
https://github.com/phalcon/docs/blob/master/CONTRIBUTING.md
「最速」PHPフレームワークPhalconのモデルについて、基本事項をまとめます(公式ドキュメントの翻訳+αです)。記事執筆時のPhalconのバージョンは1.3.1です。
ビヘイビアとは、複数のモデルに共有される振る舞いのことです。PhalconのORマッパーは、モデルでビヘイビアを実装するためのAPIを提供しています。また、イベントとコールバックを使用して、より自由度の高いビヘイビアを実装することもできます。
ビヘイビアは、モデルの初期化処理に加えなければなりません。モデルは複数のビヘイビアをもつことができます。
<?php
use Phalcon\Mvc\Model\Behavior\Timestampable;
class Users extends \Phalcon\Mvc\Model
{
public $id;
public $name;
public $created_at;
public function initialize()
{
$this->addBehavior(new Timestampable(
array(
'beforeCreate' => array(
'field' => 'created_at',
'format' => 'Y-m-d'
)
)
));
}
}
フレームワークは、以下の組み込みビヘイビアを提供しています。
名前 | 説明 |
---|---|
Timestampable | レコードが作成・更新される際に、自動的にモデルのフィールドを更新する |
SoftDelete | 永続的な削除(物理削除)の代わりに、削除フラグを付与する(論理削除) |
このビヘイビアは、配列をオプションとして受け取ります。最初のキーが、カラムへの代入が行われるイベント名になります。
<?php
public function initialize()
{
$this->addBehavior(new Timestampable(
array(
'beforeCreate' => array(
'field' => 'created_at',
'format' => 'Y-m-d'
)
)
));
}
それぞれのイベントには独自のオプションがあり、fieldは更新されるカラム名になります。formatが文字列なら、PHPのdate()関数の引数として渡されます。formatには無名関数を使うこともでき、お好みの形式でタイムスタンプを作ることができます。
<?php
public function initialize()
{
$this->addBehavior(new Timestampable(
array(
'beforeCreate' => array(
'field' => 'created_at',
'format' => function() {
$datetime = new Datetime(new DateTimeZone('Europe/Stockholm'));
return $datetime->format('Y-m-d H:i:sP');
}
)
)
));
}
formatがオプションの配列に含まれない場合は、タイムスタンプはPHPのtime()関数を使用し、タイムスタンプの数値が代入されます。
このビヘイビアは、以下のように使います。
<?php
use Phalcon\Mvc\Model\Behavior\SoftDelete;
class Users extends \Phalcon\Mvc\Model
{
const DELETED = 'D';
const NOT_DELETED = 'N';
public $id;
public $name;
public $status;
public function initialize()
{
$this->addBehavior(new SoftDelete(
array(
'field' => 'status',
'value' => Users::DELETED
)
));
}
}
このビヘイビアは、2つのオプションを受け取ります。fieldは更新されるフィールド名を、valueは削除フラグの値を指定します。以下のようなデータをもつusersテーブルについて考えてみます。
mysql> select * from users;
+----+---------+--------+
| id | name | status |
+----+---------+--------+
| 1 | Lana | N |
| 2 | Brandon | N |
+----+---------+--------+
2 rows in set (0.00 sec)
もし、いずれかのレコードを削除したら、レコードが削除される代わりに、statusが更新されます。
<?php
Users::findFirst(2)->delete();
上記コードが実行されると、テーブルの中のデータは以下のようになります。
mysql> select * from users;
+----+---------+--------+
| id | name | status |
+----+---------+--------+
| 1 | Lana | N |
| 2 | Brandon | D |
+----+---------+--------+
2 rows in set (0.01 sec)
削除済みか否かのフラグをクエリで明示的に指定してやる必要がある点に注意してください。テーブルが上の状態の時、以下のコードを実行すると、削除されたはずのBrandonも取得されてしまいます。
$users = Users::find();
論理削除済みのレコードを除外するには、以下のようにします。
$users = Users::find('deleted = ' . Users::NOT_DELETED);
ORマッパーは、独自のビヘイビアのためのAPIを提供しています。ビヘイビアは、Phalcon\Mvc\Model\BehaviorInterfaceを実装したクラスでなければなりません。また、Phacon\Mvc\Model\Behaviorが、ビヘイビアの実装に必要なほとんどのメソッドを提供しています。
以下のビヘイビアは、1つの実装例です。Blamableビヘイビアは、ユーザーがモデルに行った操作を特定します。
<?php
use Phalcon\Mvc\Model\Behavior;
use Phalcon\Mvc\Model\BehaviorInterface;
class Blameable extends Behavior implements BehaviorInterface
{
public function notify($eventType, $model)
{
switch ($eventType) {
case 'afterCreate':
case 'afterDelete':
case 'afterUpdate':
// セッションからユーザー名取得
$userName = $this->getDI()->get('session')->get('userName');
// ユーザー名・イベント種別・主キーをログに記録
file_put_contents(
'logs/blamable-log.txt',
$userName . ' ' . $eventType . ' ' . $model->id
);
break;
default:
/* 他のイベントは無視する */
}
}
}
次に、このビヘイビアをモデルに追加してみます。
<?php
class Profiles extends \Phalcon\Mvc\Model
{
public function initialize()
{
$this->addBehavior(new Blamable());
}
}
ビヘイビアは、不明なメソッド呼び出しに対して割り込みができます。
<?php
use Phalcon\Mvc\Model\Behavior,
Phalcon\Mvc\Model\BehaviorInterface;
class Sluggable extends Behavior implements BehaviorInterface
{
public function missingMethod($model, $method, $arguments=array())
{
// 「getSlug」メソッドが呼ばれたら、titleを変換する
if ($method == 'getSlug') {
return Phalcon\Tag::friendlyTitle($model->title);
}
}
}
Sluggableを実装しているモデルにgetSlug()メソッドの呼び出しを行うと、SEO対策のされたtitleが返ります。
<?php
$title = $post->getSlug();
PHP5.4以降では、トレイトを使うことでクラスのコードの再利用ができます。この機能を使って、独自のビヘイビアを実装することもできます。以下のトレイトは、TimeStampableビヘイビアのシンプルな実装例です。
<?php
trait MyTimestampable
{
public function beforeCreate()
{
$this->created_at = date('r');
}
public function beforeUpdate()
{
$this->updated_at = date('r');
}
}
モデルでは、以下のようにして利用します。
<?php
class Products extends \Phalcon\Mvc\Model
{
use MyTimestampable;
}
今回は、Phalconのモデルで利用可能なビヘイビアについて紹介しました。次回は、Transactionsから先をみていきます。
「最速」PHPフレームワークPhalconのモデルについて、基本事項をまとめます(公式ドキュメントの翻訳+αです)。記事執筆時のPhalconのバージョンは1.3.1です。
Phalcon\Mvc\Modelには、INSERT/UPDATE時のバリデーションメッセージの保持・出力を柔軟に行えるサブシステムがあります。
それぞれのメッセージは、Phalcon\Mvc\Model\Messageクラスのインスタンスになります。生成されたメッセージの集合は、getMessages()メソッドで取得することができます。それぞれのメッセージは、フィールド名やメッセージの種類のような、幅広い情報を提供します。
<?php
if ($robot->save() === false) {
foreach ($robot->getMessages() as $message) {
echo "Message: ", $message->getMessage();
echo "Field: ", $message->getField();
echo "Type: ", $message->getType();
}
}
Phalcon\Mvc\Modelが生成可能なメッセージの一覧は以下です。
種類 | 説明 |
---|---|
PresenceOf | フィールドにNOT NULL制約が付与されているときに、null値をINSERT/UPDATEしようとした際に発生 |
ConstrainvViolation | 仮想外部キー制約を設定しているフィールドに、参照しているモデルに存在しない値をINSERT/UPDATEしようとした際に発生 |
InvalidValue | 無効な値によってバリデーションが失敗した際に発生 |
InvalidCreateAttempt | 既に存在するレコードを新規作成しようとした際に発生 |
InvalidUpdateAttempt | 更新しようとしたレコードがまだ存在しない際に発生 |
getMessages()メソッドをオーバーライドすることで、モデルが自動で生成するメッセージを置き換えることができます。
<?php
class Robots extends Phalcon\Mvc\Model
{
public function getMessages()
{
$messages = array();
foreach (parent::getMessages() as $message) {
switch ($message->getType()) {
case 'InvalidCreateAttempt':
$messages[] = 'The record cannot be created because it already exists';
break;
case 'InvalidUpdateAttempt':
$messages[] = 'The record cannot be updated because it already exists';
break;
case 'PresenceOf':
$messages[] = 'The field ' . $message->getField() . ' is mandatory';
break;
}
}
return $messages;
}
}
モデルには、INSERT/UPDATE/DELETE時に実行されるイベントを実装することができるようになっています。これらは、モデルのビジネスルールを定義する助けになります。Phalcon\Mvc\Modelがサポートしているイベントの一覧は以下です(実行順)。
処理 | イベント名 | 処理の中断可否 | 説明 |
---|---|---|---|
INSERT/UPDATE | beforeValidation | ○ | フィールドのNOT NULL・空文字列・外部キー制約のバリデーション前に実行される |
INSERT | beforeValidationOnCreate | ○ | INSERTの場合で、フィールドのNOT NULL・空文字列・外部キー制約のバリデーション前に実行される |
UPDATE | beforeValidationOnUpdate | ○ | UPDATEの場合で、フィールドのNOT NULL・空文字列・外部キー制約のバリデーション前に実行される |
INSERT/UPDATE | onValidationFails | ○(既に停止済み) | バリデーションが失敗した際に実行される |
INSERT | afterValidationOnCreate | ○ | INSERTの場合で、フィールドのバリデーション後に実行される |
UPDATE | afterValidationOnUpdate | ○ | UPDATEの場合で、フィールドのバリデーション後に実行される |
INSERT/UPDATE | afterValidation | ○ | フィールドのバリデーション後に実行される |
INSERT/UPDATE | beforeSave | ○ | INSERT又はUPDATEの実行前に実行される |
UPDATE | beforeUpdate | ○ | UPDATEの実行前に実行される |
INSERT | beforeCreate | ○ | INSERTの実行前に実行される |
UPDATE | afterUpdate | × | UPDATEの実行後に実行される |
INSERT | afterCreate | × | INSERTの実行後に実行される |
INSERT/UPDATE | afterSave | × | INSERT/UPDATEの実行後に実行される |
モデルをイベントに対応させるための最も簡単な方法は、イベント名と同じ名前のメソッドをモデルのクラスに実装することです。
<?php
class Robots extends \Phalcon\Mvc\Model
{
public function beforeValidationOnCreate()
{
echo "このメソッドはRobotの作成前に実行されます";
}
}
イベントは、処理の実行前に値を代入するのに便利です。
<?php
class Products extends \Phalcon\Mvc\Model
{
public function beforeCreate()
{
// 登録日時をセット
$this->created_at = date('Y-m-d H:i:s');
}
public function beforeUpdate()
{
// 更新日時をセット
$this->modified_in = date('Y-m-d H:i:s');
}
}
モデルのイベントコンポーネントは、Phalcon\Events\Managerと統合されています。そのため、イベントが実行される際のリスナーを作ることもできます。
<?php
use Phalcon\Mvc\Model,
Phalcon\Events\Manager as EventsManager;
class Robots extends Model
{
public function initialize()
{
$eventsManager = new EventsManager();
// "model"イベントに無名関数を登録する
$eventsManager->attach('model', function($event, $robot) {
if ($event->getType() === 'beforeSave') {
if ($robot->name === 'Scooby Doo') {
echo "Scooby Dooはロボットではありません!";
return false;
}
}
return true;
});
// イベントマネージャをイベントに登録
$this->setEventsManager($eventsManager);
}
}
上記サンプルコードでは、イベントマネージャはオブジェクトとリスナーの橋渡しを行います。イベントは、Robotsモデルのsave時に発火します。
<?php
$robot = new Robots();
$robot->name = 'Scooby Doo';
$robot->year = 1969;
$robot->save();
全てのオブジェクトに共通のEventsManagerを実装したい場合、EventsManagerをModelsManagerに登録します。
<?php
// modelsManagerサービスをDIコンテナに登録
$di->setShared('modelsManager', function() {
$eventsManager = new \Phalcon\Events\Manager();
// "model"イベントのリスナーとして無名関数を登録する
$eventsManager->attach('model', function($event, $model){
// Robotsモデルに寄って生成されるイベントをキャッチする
if (get_class($model) == 'Robots') {
if ($event->getType() == 'beforeSave') {
if ($model->name == 'Scooby Doo') {
echo "Scooby Dooはロボットではありません!";
return false;
}
}
}
return true;
});
// EventsManagerを登録する
$modelsManager = new ModelsManager();
$modelsManager->setEventsManager($eventsManager);
return $modelsManager;
});
リスナーがfalseを返すと、その処理の実行は中断されます。
INSERT/UPDATE/DELETEの実行時に、モデルはイベント一覧に挙げられた名前のメソッドが無いか確認します。
以下のコード例では、INSERT/UPDATE時のイベントを実装し、yearの値が0未満でないかバリデーションしています。
<?php
class Robots extends \Phalcon\Mvc\Model
{
public function beforeSave()
{
if ($this->year < 0) {
echo "year は0未満にできません!";
return false;
}
}
}
イベントがfalseを返すと、処理は中断されます。イベントが何も返さない場合、Phalcon\Mvc\Modelはtrueが返されたとみなします。
Phalcon\Mvc\Modelは、データをバリデーションし、ビジネスルールを実装するためのイベントを提供しています。「validation」イベントによって、組み込みのバリデーターを呼び出すことができます。Phalconは、いくつかの組み込みバリデーターを提供しています。
<?php
use Phalcon\Mvc\Model\Validator\InclusionIn,
Phalcon\Mvc\Model\Validator\Uniqueness;
class Robots extends \Phalcon\Mvc\Model
{
public function validation()
{
$this->validate(new InclusionIn(
array(
"field" => "type",
"domain" => array("Mechanical", "Virtual")
)
));
$this->validate(new Uniqueness(
array(
"field" => "name",
"message" => "ロボットの名前が重複してはいけません"
)
));
return $this->validationHasFailed() != true;
}
}
上記コード例では、組み込みの「InclusionIn」バリデーターを使用しています。このバリデーターは、「type」フィールドの値がdomainのリストに含まれているかチェックしています。値が含まれていなかった場合は、バリデーションに失敗し、バリデーターはfalseを返します。
組み込みのバリデーターの一覧は以下です。
名前 | 説明 | 実装例 |
---|---|---|
PresenceOf | フィールドの値がnullではなく、空文字列でもないことをバリデーションする。NOT NULL制約が設定されているフィールドに対しては、このバリデーターが自動的に追加される。 | 例 |
フィールドの値が有効なEメールアドレスであるかバリデーションする | 例 | |
ExclusionIn | フィールドの値が禁止リストに含まれないことをバリデーションする | 例 |
InclusionIn | フィールドの値が許可リストに含まれることをバリデーションする | 例 |
Numericality | フィールドの値が数値形式かバリデーションする | 例 |
Regex | フィールドの値が正規表現にマッチするかバリデーションする | 例 |
Uniqueness | フィールドの値が既存のレコードと重複しないかバリデーションする | 例 |
StringLength | 文字列の長さをバリデーションする | 例 |
Url | フィールドの値が有効なURLの形式化バリデーションする | 例 |
組み込みのバリデーターに加えて、独自のバリデーターを作成することもできます。
<?php
use Phalcon\Mvc\Model\Validator,
Phalcon\Mvc\Model\ValidatorInterface;
class MaxMinValidator extends Validator implements ValidatorInterface
{
public function validate($model)
{
$field = $this->getOption('field');
$min = $this->getOption('min');
$max = $this->getOption('max');
$value = $model->$field;
if ($min <= $value && $value <= $max) {
$this->appendMessage(
"フィールドの値が有効範囲外です",
$field,
"MaxMinValidator"
);
return false;
}
return true;
}
}
バリデーターをモデルに追加するには、以下のようにします。
<?php
class Customers extends \Phalcon\Mvc\Model
{
public function validation()
{
$this->validate(new MaxMinValidator(
array(
"field" => "price",
"min" => 10,
"max" => 100
)
));
if ($this->validationHasFailed() == true) {
return false;
}
}
}
バリデーターを作成することで、バリデーションのロジックを複数のモデルで使い回すことができます。1箇所でしか使わないバリデーションであれば、以下のようにシンプルに実装することもできます。
<?php
use Phalcon\Mvc\Model,
Phalcon\Mvc\Model\Message;
class Robots extends Model
{
public function validation()
{
if ($this->type === "Old") {
$message = new Message(
"Sorry, old robots are not allowed anymore",
"type",
"MyType"
);
$this->appendMessage($message);
return false;
}
return true;
}
}
モデルのプロパティに代入された値は全て、そのデータ型に応じたエスケープが施されます。開発者が、DBに入れる値のエスケープを実装する必要はありません。Phalconは内部でPDOのバインド機構を使用して、自動エスケープを行っています。
mysql> desc products;
+------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| product_types_id | int(10) unsigned | NO | MUL | NULL | |
| name | varchar(70) | NO | | NULL | |
| price | decimal(16,2) | NO | | NULL | |
| active | char(1) | YES | | NULL | |
+------------------+------------------+------+-----+---------+----------------+
上記テーブルへのINSERTをする際、PDOを使用すると、以下のようなコードになります。
<?php
$productTypesId = 1;
$name = 'Artichoke';
$price = 10.5;
$active = 'Y';
$sql = 'INSERT INTO products VALUES (null, :productTypesId, :name, :price, :active)';
$sth = $dbh->prepare($sql);
$sth->bindParam(':productTypesId', $productTypesId, PDO::PARAM_INT);
$sth->bindParam(':name', $name, PDO::PARAM_STR, 70);
$sth->bindParam(':price', doubleval($price));
$sth->bindParam(':active', $active, PDO::PARAM_STR, 1);
$sth->execute();
Phalconのモデルを使用すると、以下のように書けます(エスケープは全て自動で行われます)。
<?php
$product = new Products();
$product->product_types_id = 1;
$product->name = 'Artichoke';
$product->price = 10.5;
$product->active = 'Y';
$product->create();
何らかのトリガーや、デフォルト値の代入を行いたいため、バリデーションを行いたくない場合があります。モデルによるバリデーションを行わないようにするには、以下のように書きます。
<?php
class Robots extends \Phalcon\Mvc\Model
{
public function initialize()
{
// INSERT/UPDATE時にフィールドのバリデーションをしない
$this->skipAttributes(array('year', 'price'));
// INSERT時にバリデーションしない
$this->skipAttributesOnCreate(array('created_at'));
// UPDATE時にバリデーションしない
$this->skipAttributesOnUpdate(array('modified_in'));
}
}
これによって、アプリケーション全体で、INSERT/UPDATE時のバリデーションが実行されなくなります。デフォルト値の代入は以下のように行えます。
<?php
$robot = new Robots();
$robot->name = 'Bender';
$robot->year = 1999;
$robot->created_at = new \Phalcon\Db\RawValue('default');
$robot->create();
イベントのコールバックによって、特定条件の場合にデフォルト値を設定することもできます。
<?php
use Phalcon\Mvc\Model,
Phalcon\Db\RawValue;
class Robots extends Model
{
public function beforeCreate()
{
if ($this->price > 10000) {
$this->type = new RawValue('default');
}
}
}
注意:Phalcon\Db\RawValueは、ユーザー入力のような外部データや変数の値の代入に使用してはいけません。RawValueにはバインド機構が使用されないため、SQLインジェクション脆弱性を引き起こす危険性があります。
SQLのUPDATE文は、デフォルトでは全てのカラムをUPDATEするように作成されます。ダイナミックアップデートを有効化すると、変更されたフィールドだけが更新されるUPDATE文が生成されます。
これによって、アプリケーションサーバ・DBサーバ間のトラフィックが減少し、パフォーマンスが改善されることがあります。特に、BLOB/TEXT型のような大きなデータを取り扱うカラムがある場合、ダイナミックアップデートは大きな助けになります。
<?php
class Robots extends Phalcon\Mvc\Model
{
public function initialize()
{
$this->useDynamicUpdate(true);
}
}
Phalcon\Mvc\Model::delete()メソッドに寄って、レコードを削除できます。
<?php
$robot = Robots::findFirst(11);
if ($robot != false) {
if ($robot->delete() == false) {
echo "今はロボットを削除できないようです:\n";
foreach ($robot->getMessages() as $message) {
echo $message, "\n";
}
} else {
echo "ロボットの削除に成功しました!";
}
}
結果セットをforeachでまわすことで、複数のレコードを削除できます。
<?php
foreach (Robots::find("type='mechanical'") as $robot) {
if ($robot->delete() == false) {
echo "今はロボットを削除できないようです:\n";
foreach ($robot->getMessages() as $message) {
echo $message, "\n";
}
} else {
echo "ロボットの削除に成功しました!";
}
}
以下のイベントを使用することで、削除処理の際に実行されるビジネスルールを定義することができます。
処理 | 名前 | 処理の中断可否 | 説明 |
---|---|---|---|
DELETE | beforeDelete | ○ | 削除処理の実行前に実行 |
DELETE | afterDelete | × | 削除処理の実行後に実行 |
上記イベントを使用すると、以下のようにビジネス・ルールを定義できます。
<?php
class Robots extends Phalcon\Mvc\Model
{
public function beforeDelete()
{
if ($this->status == 'A') {
echo "The robot is active, it can't be deleted";
return false;
}
return true;
}
}
バリデーション失敗時のイベントもあります。
処理 | 名前 | 説明 |
---|---|---|
INSERT/UPDATE | notSave | INSERT/UPDATEが何からの理由で失敗した際に発生 |
INSERT/UPDATE/DELETE | onValidationFails | データ処理が失敗した際に発生 |
ここまで、Phalconのモデルを利用したバリデーションの実装方法をみてきました。次回は、Behaviorsから先をみていきます。