zz log

zaininnari Blog

CakePHP 2.3.10 変更点

公式アナウンス

今回のリリースはバグフィックスリリースです。 パフォーマンスの改善、PHP5.5.2でのSessionの動作改善、Emailクラスの改善が主な変更点になります。

また、CakePHP2.3系のリリース方針についてアナウンスがあり、CakePHP2.6.0までセキュリティフィックスを継続していくそうです。

今回は同時に CakePHP2.4 もリリースされています。 CakePHP3 の開発も CakePHP2系以上にリソースをつぎ込んで開発が進められており、 片手間ではコミットを追いきれないほどです。

主な変更点

ドキュメント・テストケース・小さな修正は除きます。

全般

  • null チェックが is_null から === null へ統一され、パフォーマンスを改善した
    • === nullis_null より 40倍程度高速に動作する。

Network/Email

  • AbstractTransport::config が引数を直接代入していたのを、$config + $this->_configになり、デフォルト値が省略できるようになった。
    • SmtpTransport::config でもarray_merge($default, $this->_config, $config)に変更になった。
  • CakeEmail::_renderTemplates 'layout' が null の場合、レイアウトが描画されなくなった
    • ドキュメントでは null で描画と停止できると書いてあるが、null をそのまま View::render に渡すと、null はデフォルトレイアウトを呼び出す意味を持っているので、正しい挙動ではなかった。
    • null を渡すと、View::render で描画されない false に内部で置き換わるようになった。

Utility/CakeTime

  • CakeTime::timeAgoInWords 過去の時間を約1日前などと大雑把に表現できるようになった。また、翻訳も可能にした。
  • CakeTime::CakeTime strtotime("0000-00-00 00:00:00") の結果が32bit(FALSE)と64bit(-62169955200)で異なるため、false を返すよう修正

Utility/CakeNumber

  • CakeNumber::precision 整数区切に「,」を使う国・地域の場合、結果が正しくなかったのを修正

Model/AclNode

  • AclNode::node 結合が'LEFT'から'INNER'へ変更し、パフォーマンスの向上を図る
    • 2.3.9で変更されなかった部分が今回変更され、すべて'INNER'で結合されるようになった。

Utility/Sanitize

  • Sanitize::paranoid データベースとのコネクションを開くのを引数チェックの前から後へ変更し、パフォーマンスを改善した

Routing/Route

  • RedirectRoute::parse options['persist'] を正しく運用するよう変更された

Model/Model

  • Model 使用されていない deleteQuery insertQuery が削除された
    • 1.3 までの機能であり、利用ようとしても利用できなく、ユーザーに混乱を招くため、削除された。

Console/ShellDispatcher

  • ShellDispatcher::_initEnvironment 'ROOT' 'APP_DIR' 'APP' の定数が事前に定義済みかどうかを判別するようになり、外部から制御できるようになった。

Model/Datasource

  • CakeSession::_defaultConfig 'session.auto_start' => 0,が削除された
    • PHP5.5.2では、session.auto_startPHP_INI_PERDIR(php.ini、.htaccesshttpd.conf あるいは .user.ini (PHP 5.3 以降の場合) で設定可能なエントリ)になり、エラーが発生してしまうため、これを回避するために、削除された。この部分は、session.auto_start の初期値を再設定していたものであるため、(独自でphp.ini等を変更していない限り)下位バージョンには影響はない。

Model/Datasource/Database

  • Sqlite::buildColumn bigint primary key が正しく生成されるようになった

Cache/Engine

  • FileEngine::key Windows 環境で、ファイル名として使えない文字をアンダーラインに変更する処理を追加
    • '<', '>', '?', ':', '|', '*', '"'が処理されるようになった

エクセル・アクセスで自然順ソート

概要

excel や Access では自然順ソート(natsort) が提供されていません。(PHP では提供されています!) 自然順ソートを真面目に実装するのは意外に大変です。

大変なポイントとして、以下の3点があります。

  • 数字とそれ以外に分割を行い、個別に比較しないといけないので、ワークシート関数のみではほぼ不可能
  • 01, 001, 0001 のような0の桁埋めされたものとされてないものを区別するかで処理が分岐
  • 空白文字の扱い

仕様

今回のコードは、住所の並び替えを念頭においた仕様です。

  • エクセル上で並び替える必要上、手軽に並び替えを行いたいので、1セルにまとめるため、数字の桁埋めの上限に制限がある(truncateLen = 5)
  • 全角数字は、事前に半角数字に変換
  • 全角空白は、事前に半角数字に変換
  • 漢数字の地番は考慮しない
  • 環境依存文字が含まれると、処理できない(Unicodeで処理しないといけないが、手抜き)
  • 0の桁埋めを考慮しない

参考URL

Function formatNatsort(ByVal text As String) As String

    Dim i As Long, length As Long, ascii As Long
    Dim char As String, bufferString As String, padding As String
    Dim beforeState As Long, currentState As Long, truncateLen As Long

    length = Len(text)
    padding = "0000000000000000"
    beforeState = -1
    currentState = 0
    bufferString = ""
    truncateLen = 5

    If length = 0 Then
        formatNatsort = ""
        Exit Function
    End If

    For i = 1 To length
        char = Mid(text, i, 1)
        ascii = Asc(char)
        If ascii = 32 Then
            currentState = 0
        ElseIf ascii >= 48 And ascii <= 57 Then
            currentState = 1
        Else
            currentState = 2
        End If

        If i = 1 Then
            beforeState = currentState
        End If
        If currentState <> 0 Then
            If currentState = beforeState Then
                bufferString = bufferString & char
            Else
                If currentState = 1 Then
                    formatNatsort = formatNatsort & bufferString
                Else
                    formatNatsort = formatNatsort & Right(padding & bufferString, truncateLen)
                End If
                bufferString = char
            End If
            beforeState = currentState
        End If

    Next i

    If currentState = 1 Then
        formatNatsort = formatNatsort & Right(padding & bufferString, truncateLen)
    Else
        formatNatsort = formatNatsort & bufferString
    End If
End Function

使い方

モジュールを新規に追加して、コードをコピペしてください。

オリジナル 処理後
東京都千代田区永田町2丁目3−1 東京都千代田区永田町00002丁目00003−00001
東京都千代田区永田町1−7−1 東京都千代田区永田町00001−00007−00001

後は、処理後のフィールドをキーにして並び替えを実行してください。

CakePHP 2.3.9 変更点

公式アナウンス

主な変更点

ドキュメント・テストケース・小さな修正は除きます。

CakeResponse

_fileRange Content-Range ヘッダーが正しい範囲を返すよう修正

Range: bytes=0-
Range: bytes=-500

のような HTTP_RANGE の開始値や最終値がない場合、計算結果が正しくなかった。

Bake

Bake Controller で作成されるページネートが Paginator コンポーネントに変更

以前は古い形式 $this->paginate() だったが、$this->Paginator->paginate() に新しくなった。

FormHelper

_introspectModel 動的なルールの追加に対応

$object->validate のチェックが削除された。バリデート関連のメソッドはModelクラスから、ModelValidator へ委譲されている。

AuthComponent

redirectUrl App.baseUrl を指定しているとき、それが含まれてしまうデグレードを修正

CakePHP2.3.7 のデグレードを解消したCakePHP2.3.8 から発生。

I18n

translate $domain が空文字の場合、例外が発生するように変更

ちなみに初期値の null の場合はデフォルト $domain が使用される

FileEngine

clear グループを設定している場合、Cache::clear でファイルが削除されなかったのを修正

clear 使用されていない設定のファイルパスが存在しない場合、エラーが発生するのを解消

clear 上記の修正で、Windows 環境にて、デグレートが発生するのを再修正

この問題はWindows環境でよくある問題(Rubyでも発生する)で、ファイルリソースを開いたまま、リネームや削除ができないというもの。一旦、ファイルリソースを閉じてからならば、リネームや削除が可能になる。

ComponentCollection

setController が追加された

ControllerTestCase::generate を使用時、setController により コントローラーモックオブジェクトを挟み込むことで、ComponentCollection::$_Controller に依存するコンポーネントTrying to get property of non-objectエラーを発するケースを解消するようになった。

CakeRoute

persistParams 用のオプション設定が必須ではなくなった

以前は、CakeRoute::$options['persist'] をキーのチェックや型のチェックをなしに、foreach で回していたが、事前に、キーのチェックや型のチェックを行うようになった。CakeRoute::$options['persist'] はデフォルト値もなく、サブクラスやインスタンスで定義される保証がなかった。

Model

save fieldList や whitelist の配列の指定の解釈が正しくなかったのを修正。

呼び出す元のモデル(自モデル)の指定の場合、自モデルのエイリアスを省略でき、そうでない場合、モデルのエイリアスをキーとした配列を記述しなければならない。今回の場合、自モデルの指定がなく、他モデルの指定だけがある場合、自モデルの指定として解釈する場合があったのを解消した。

Configure

bootstrap App::$bootstrapping = false;App::initの後に移動し、パフォーマンスが向上するようになった

App::$bootstrapping は bootstrap 処理中かどうかを表す boolean。初期値は、false だが、Cake/bootstrap.php が読み込まれると、true になる。その後、今回の変更のある Configure::bootstrap が呼ばれる。

false 時にマッピングされていないファイルを読み込むと、App::$cacheChange が true になる。スクリプト処理が完了した際、App::init で register_shutdown_function により登録された App::shutdown が呼び出され、App::$cacheChange が true と評価されると、Cache::write('file_map', array_filter(self::$_map), '_cake_core_');でキャッシュの書き込みが行われる。

今回のチケットの問題は、手元の環境では再現できなかったが、App::init 中に読み込まれるファイルが、別のファイルを読み込む場合に、再キャッシュを防ぐという目的で今回の変更が行われた模様。

travis

mysql の時だけ AllTests を実行し、それ以外の場合は、データベースが関係する AllDatabase を実行することでテスト時間を短縮

実行タイミングによるものの1分程度の短縮が見られる。

CookieComponent

_write 複数のキーを書き込む場合、期限が正しく設定されなかったのを修正

AclNode

node JOIN を LEFT から INNER に変更し、パフォーマンスを改善

ShellDispatcher

_bootstrap WWW_ROOTTMP が自由に設定できるようになった。

今までは、決まったパスしか利用できなかった。

URLへのキーワードの埋め込みと管理IDの実装調べ

概要

SEO ではURLに商品名を加えたものが良いとされます。 但し、英語圏ならまだしもマルチバイトな日本語では、

  • 日本語のままURLへ含める

→重複の可能性、これをキーにするのは実装上問題が多そう

  • URL用に英語名を入力

→重複の可能性と入力の手間とカタカナ英語以外で効果が低そう

と問題が考えられます。

SEO上の効果の程度や実装の手間を考慮すると、 「データベースの一意なIDをそのまま使用する」(オートインクリメントなIDやハッシュ値、UUID)実装が シンプルで変更に強く魅力的に思えてきます。

また、多数のパラメータを持つ商品(服ならばサイズ・色)をどこまでURLへ反映させているかが気になったので、 各社のURL構造を自分なりに調べました。

※URLの構造とその実装に興味があります。
※中の人ではないため、意図を読み違えている可能性があります。
SEO上の効果を検証したり、また、それ自体について議論するものではありません。

クックパッド

シンプルな構成

http://cookpad.com/recipe/2221390

recipe のディレクトリの下に、シーケンシャルなIDの構成です。 レシピサイトのため、レシピへのオプションは存在しません。 また、他にキーとするならタイトルしかありませんが、レシピは投稿されるもののため、 タイトルの変更を考慮すると、IDしか使用できないと思われます。

ID がデータベースのキーになっている可能性が高く、実装や運用がシンプルに行えそうです。 IDがシーケンシャルに発行されているようなので、クックパッドの規模ですと、SPOFをどう回避しているかが気になります。

アスクル

シンプルな構成

http://www.askul.co.jp/v/4853/

http://www.askul.co.jp/p/5784852/

http://www.askul.co.jp/p/K302379/

v または p の1文字のディレクトリの直下にシーケンシャルなIDの構成です。 色・サイズ違いは別IDになります。

v と p の違いは不明です。v だけ、p だけのカテゴリもあれば、同じカテゴリ内でも混在する場合もあります。 IDは頭にアルファベットの頭文字がつくときがあります。この ID がデータベースのキーになっている可能性が高いです。

実装上の意図が分かりにくいところがありますが、考え方はシンプルです。

lighthouseapp

内部IDとタイトルを同時に表示している例

https://cakephp.lighthouseapp.com/projects/42648/tickets/3891-cakeemail-cant-set-invalid-rfc-email-address

projects プロジェクトであることを示す 42648 アカウントID tickets チケットであることを示す 3891-cakeemail-cant-set-invalid-rfc-email-address 個別チケット(シーケンシャルなIDとタイトルのスラグの構成) ※正規化はされていないが、シーケンシャルなIDだけである

https://cakephp.lighthouseapp.com/projects/42648/tickets/3891

でも同じページを返すので、内部的には、3891だけを見ている可能性が高いです。

コンテンツの内容をURLに含めるが、管理上はなくても構わないという実装です。

アマゾン

内部IDとタイトルを同時に表示している例

書籍の場合

正規化されたURL

http://www.amazon.com/Quiet-Power-Introverts-World-Talking/dp/0307352153 http://www.amazon.co.jp/Quiet-power-introverts-world-talking/dp/0141029196 http://www.amazon.co.jp/内向型人間の時代-社会を変える静かな人の力-スーザン・ケイン/dp/4062178591

洋書のままではIDは同じで、翻訳の場合は異なります。 書籍の場合、商品IDはISBNになります。

上記の記述では、タイトルの内容を削ることが可能で、最小構成は以下になります。(リダイレクトされません)

http://www.amazon.co.jp/dp/4062178591

但し、サイト内では以下のようなタイトル部分が「gp」に置き換えられたURLが使用されています。(リダイレクトされません)

http://www.amazon.co.jp/gp/dp/4062178591

また、カート内からのリンクでは、「product」が含まれるURLが使用されています。(リダイレクトされません)

http://www.amazon.co.jp/gp/product/4062178591

おそらく、タイトル部分の容量を削減しているのでしょう。

ファッションの場合

サイズ・色毎に商品IDが用意されています。URLへはIDによって商品のオプションが反映されています。 但し、変わっているのは、これらの商品の正規化されるURLがサイズ・色毎の個別の商品IDでなく、全く別の商品IDになることです。

つまり、代表する商品IDがあり、これを直接注文することはできず、それらにぶら下がるサイズ・色が具体的に決まった商品IDで注文するという構成になっています。 おそらく、サイズ・色毎にURLを用意したいが、サイズ・色は生産計画や在庫の都合でなくなる可能性があるため、代表する商品IDを別途用意していると思われます。

カート内からの商品個別のURL(サイズ違いはなく、色違いが2種類のみ)

http://www.amazon.co.jp/dp/B00E97LP7W

http://www.amazon.co.jp/dp/B00E97LOO6

上記から正規化されたURL

http://www.amazon.co.jp/dp/B00E97LNLU

書籍以外の商品IDはB[0-9A-Z]{9}で構成され、1桁目から採番されているようです。 36の9乗≒10の14乗で約100兆のアイテムが登録可能になっています。

流石アマゾン、必要な要件をすべて取り揃えています。

楽天ブックス

内部IDとタイトルを同時に表示している例 正規化されたURL

http://books.rakuten.co.jp/rb/内向型人間の時代-社会を変える静かな人の力-スーザン・ケイン-9784062178594/item/12279831/

アマゾンより複雑な構成で、 rb は商品単位のディレクトリであることを示していますが、item の扱いが不明です。 また、タイトル後の 9784062178594 が何を示しているのかも不明です。 タイトル部分は削ることができ、アマゾン同様に以下まで削ることが可能です。

http://books.rakuten.co.jp/rb/item/12279831/

但し、削られたURLは正規化されたURLへリダイレクトされます。

サイト内では、

http://books.rakuten.co.jp/rb/12279831/

が使用されていますが、このURLも正規化されたURLへリダイレクトされます。

商品IDは、整数値がインクリメントされるもののようです。ちなみに、現在の最小idは以下の商品でした。

http://books.rakuten.co.jp/rb/ア-スアンカ-による圧入工法の設計と施工-角田安一-9784274099434/item/12/

zappos

タイトルだけを表示している例

正規化されたURL

http://www.zappos.com/oakley-full-auto-tour

http://www.zappos.com/oakley-full-auto-tour-black

ディレクトリなしで、商品名をスラグ(+属性[色])にした構成です。 商品名が色なしが正規化されたURLで、 デフォルトの色のURLや色違いのURLは色なしに正規化されます。 但し、サイズはURLに含まれず、HTMLから判断するに、個別の商品idを持っているようです。

後発ほど、商品名の重複を気にしなければならないので、運用はどうやっているのかが気になります。 また、 URLが任意に指定できる以上、変更があった場合、URLの永続化をどのようにしているかも気になりましたが、 外部からうかがい知ることはできませんでした。

Qiita

ハッシュで構成されているURL

http://qiita.com/dz_/items/55ae18f7fe3a873bace1

  • dz_ はユーザー名
  • items は固定
  • 55ae18f7fe3a873bace1 TIPSを特定するID

DELL

商品の最小URL v323010dc2ojp でカスタマイズ前の商品構成を識別している模様

http://configure.apj.dell.com/dellstore/config.aspx?oc=v323010dc2ojp

カートから商品を編集する際に発行されるURL

http://configure.apj.dell.com/dellstore/config.aspx?reconfigure=true&cart=ActiveCart&id=e66da8b6-2362-451c-89ff-a102f6eacea2

UUIDの e66da8b6-2362-451c-89ff-a102f6eacea2 がカスタマイズされた内容を保持しているキーになるようです。 カートから削除したり、別のセッションでは利用できないことを考慮すると、セッションと一緒に管理されており、 カスタマイズ後のURLは提供されていないようです。

ゾゾタウン

オートインクリメントな商品IDを持つシンプルなURLです。 商品IDは、サイズ・色を統括する「商品」に振られ、サイズ・色毎の個別のURLは生成されないようです。

グラフィック

商品の選択が進んだ後のURL

http://www.graphic.jp/price/12_2_1/250_2

「12_2_1」について

  • 12 はA4チラシのID
  • 2 は用紙コート63kgのID
  • 1 は5日納期のID

「250_2」について

  • 250 は部数の数
  • 2 は両面モノクロのID

と、ディレクトリ構造にはあまり意味がなく、実装側の都合のURLのようです。 オプションについては、URLに反映されませんでした。

納期・カラー数・部数を選ぶ画面

http://www.graphic.jp/price/12_2_3/

上記の価格一覧表では、行列の項目と生成されるURLに規則性があることから、すべて自動生成されているようです。 また、こんなにURLを生成していると、どうインデックスされているのかと思ったら、 「A4チラシ」と最初に仕様を選んだページまでがインデックスされるようになっており、 その後の用紙の種類以降は、「noindex,follow」とリンクは辿るけど、インデックスはしないという指定がされていました。

まとめ

各社とも考えられたURLの構造をしており、勉強になる部分が多かったです。

特に、 アマゾン、lighthouseapp、楽天のような、管理IDとコンテンツ内容をURLに含めつつ、 その実際は、管理IDだけでアクセス可能な仕組みは、SEO上のURLと実装・運用の妥協点となっているようでした。

CakePHP 2.3.8 変更点

  • 公式アナウンス
  • セキュリティリリース
    • AssetDispatcher を有効にしている(デフォルトでは有効)アプリケーションが対象
      • CakePHP 2.2.9 も同時にリリースされている
      • このバグに対する詳細は、後日、公式から発表されるそうです。
      • このバグは2.0.5から存在するが、2.0系、2.1系のリリースはない(1.x系は未調査)

主な変更点

ドキュメント・テストケースの修正は除きます。

Console

  • ExtractTask I18nShell のプラグインのバリデーションメッセージを正しく表示できるようになった
  • ExtractTask App::objects の第3引数を false に設定
    • 第3引数はキャッシュ設定、Console 時は、常にキャッシュしない方針だが、ここだけキャッシュをしていたので他と整合性が取れるようになった。
  • ServerShell カスタム documentRoot を設定したとき、その後のパスが正しくなかったのを修正
  • ServerShell 静的ファイルのクエリーパラメータ付のリクエストが処理できなかったのを修正
    • クエリーパラメータ付のパス名を file_exists でファイルが存在するかどうか調べていたのを修正

Utility

  • Inflector 複数化できる単語を追加

CakeResponse

  • send 206 ステータスコードの Content-Range を正しく処理できるようになった。
    • rfc2616 に準拠した Content-Range を返すようになった。
    • 416 Requested Range Not Satisfiable (リクエストしたレンジは範囲外にある。実ファイルのサイズを超えるデータを要求した。)を追加

Component

  • AuthComponent 2.3.7 での redirectUrl メソッドのリグレッションを修正
    • 2.3.7 の修正で、サブディレクトリを含めた redirectUrl が正しく動作しなくなっていた。

Blink HTMLTokenizer を PHP に移植して、HTML Minifyをできるようにした「html-minifier」

概要

Webkit からフォークされたレンダリングエンジン Blink で使用されている HTMLTokenizer をPHPに移植して、 HTML の Minify(圧縮) を行う「html-minifier」を作りました。

zaininnari/html-minifier

※アルファ版にも満たないため、名称の変更、APIの変更、歴史改変のための push -f がありえます。

できること・できないこと

  • 条件付きコメントを残す
  • textarea や pre 要素内のテキストのHTMLの意味を変えない
  • 開始タグ内のホワイトスペースの最適化
  • タグ間の文字列のホワイトスペースの最適化

但し、未実装の部分があります。

  • DOCTYPE memo SYSTEM の Tokenizer が未実装(PUBLICはOK)。ここを通る処理は未実装のメソッドがあります。
  • invalid なHTMLを処理する際のエラー処理に未実装が多い。
  • 実体参照(©等)が最低限
  • 最適化が甘い(type="checkbox" → type=checkbox や disabled="disabled" → disabled はまだ未実装)

動機

css の最適化なら LESS や Sass があり、JavaScript ならば、Closure Compiler, YUICompressor, UglifyJS 等のライブラリが存在していますが、 それらを活用する HTML には代表的なものをあまり聞きません。

必要な要件は以下の通りです。

  • MUST HTMLの意味を変えない
    • 条件付きコメントは除外されると、挙動が変わってしまうので、条件付きコメント(ダウンレベルから見えない/見える)を除去しない
    • textarea や pre タグ内の要素の空白は意味があるので、最適化を行わない
  • MUST アプリケーションレベルで最適化の実行が可能
    • 素のPHP 、Twig や Smartyのコンパイル後テンプレートキャッシュに対して、最適化を実行したい
  • better 最適化のレベルが選択できる
    • ドキュメントタイプによる最適化の違い(<br>, <br/>, <br />)
    • 属性の最適化( type="checkbox" → type=checkbox 一定の条件で「"」は省略できます。また、disabled="disabled" → disabled という最適化もあります。 )
    • コメントを除去を On/Off 可能(ライブラリによっては、コメントタグに情報を持たせるものが存在するので、問答無用に削除してもらっては困ります。)

今回はブラウザの処理を理解したいのと、PHPでデバック情報も取得したいので、Blink(現時点では、フォーク前のWebkitWebcoreとほとんど変わりありません)の処理を移植することにしました。

最適化の分類

HTMLに独自タグを埋め込む方式

Twig (Symfony 等で採用されているテンプレート言語です。)では、spacelessブロックによって、そのブロック内のタグ間のホワイトスペースを除外できます。 処理方法は正規表現により、「'/>\s+</', '><'」という形式になっており、タグ間のホワイトスペースを一律削除します。

欠点は、テンプレートの導入とコンテンツの移植が必要なことと、属性の最適化・複雑なホワイトスペース・条件付コメントに対応していないことです。

別のマークアップ言語からHTMLへ変換する方式

最高のパフォーマンスを発揮できる形式だと思われますが、有名どころが haml や slim等で Ruby なのが残念。

欠点は、テンプレートの導入とコンテンツの移植が必要なことです。

HTMLの文字列に対して最適化を実行する方式

今回採用した方式、汎用性がある代わり、ブラウザが解釈するレベルの実装でないと、適切な最適化ができません。

PageSpeed Module

最適化のタイミングは、PHP等の処理が行われた後のHTMLに対して処理されます。

ブラウザ → Webサーバー → PHP等 → Webサーバー(ここ) → ブラウザ

実装は、C言語で、Tokenization の簡略化された処理でトークンを作成後、各種フィルタによって圧縮が行われています。 処理の精度も高く、条件付きコメントを残し、インラインの cssJavaScript まで処理してくれます。

欠点は、Webサーバーにインストールを行うので、アプリケーションレベルで最適化を行うとすると、HTTPリクエストが必要になってしまう点です。

minify

PHP で動作するHTML最適化ライブラリです。 方式は正規表現による置換で最適化を行います。 HTMLの構造は正規表現で表現しきれないので、過剰な最適化が発生してしまいます。

欠点は、最適化が甘いこと、過剰な最適化を行う場合がある、更新が止まっていることです。

kangax/html-minifier

JavaScript で動作するHTML最適化ライブラリです。 方式は、正規表現によるトークンの検出後に最適化を実施する方式です。

開発が止まっておらず、ほしい要件も満たしていました。 Node.js からも利用できるので、PHP内で最適化された結果を取得することも可能ですし、そのままPHPへ移植することも可能です。

使い方

composer からインストールできます。

composer.json

{
    "require": {
        "zaininnari/html-minifier": "0.1.*"
    }
}
<?php

require 'vendor/autoload.php';

$html = 'HTML文字列';
$out = zz\Html\HTMLMinify::minify($html);

example.com のTOPページの最適化の様子です。左が最適化前、右が最適化後です。

f:id:zainin:20130716000305p:plain

CakePHP 2.3.7 変更点

主な変更点

ドキュメント・テストケースの修正は除きます。

View

  • CacheHelper 使用時、content-type ヘッダーを含めるようになった。
    • 以前は Routing/Filter/CacheDispatcher, View/Helper/CacheHelper に加えて、 View/View にも手を加えなければならなかったのが解消された。
    • Cached Render Time の出力が xml 以外から html のみに変更された。

Mail

  • SmtpTransport Return-Path ヘッダーを含めないよう変更
    • RFC2821 に準拠するようになった。最終的な配信から抜ける(SMTPプロトコル処理からメールボックスに格納される)とき MAIL FROM の管理情報は Return-Pathヘッダーとして格納される。
  • CakeEmail::_render _createBoundary の呼び出しを send から _render へ移動
    • View::render のイベント後に、_createBoundary されるようになり、自由度が増した。
    • boundary は添付ファイルやtextとhtmlを指定した際の境界線。

Model

basics

  • SCRIPT_FILENAME と SERVER_IIS によるドキュメント化もテストもされないコードを削除

Helper

  • TextHelper::autoLinkUrls 閉じタグを含めないように正規表現を修正
<?php
$text = '<li>lorem: http://example.org?some</li>';
$result = $this->Text->autoLink($text, array('escape' => false));
// -> <li>lorem: <a href="http://example.org?some">http://example.org?some</a></li>
  • FormHelper::dateTime 現在より未来の年と指定した時、maxYear になるよう修正

Component

  • AuthComponent::redirect URL の最初のセグメントが base プロパティと同一の場合誤ったURLを生成してしまうのを修正
  • PaginatorComponent::validateSort Model::order を自動的に継承するようになった

App

  • load ..を含むクラス名を指定したとき、処理が止まり、false を返すようになった。