Facadeパターンとは?

今日は、デザインパターンの1つ、Facade(ファサード)パターンについて調べてみました。

Facadeパターンは、Wikipediaでは、

異なるサブシステムを単純な操作だけを持ったFacadeクラスで結び、サブシステム間の独立性を高める事を目的とする。
Facade パターン - Wikipedia

と説明されています。

以下のクラス図が示されています(Wikipediaより)。

Facadeパターンのクラス図

『PHPによるデザインパターン入門』

Facadeパターンは、複雑な関連を持つクラス群を簡単に利用するための「窓口」を用意するパターンです。

Facadeパターンの目的は、GoF本では次のように定義されています。

サブシステム内に存在する複数のインターフェースに1つの統一インターフェースを与える。 Facadeパターンはサブシステムの利用を容易にするための高レベルインターフェースを定義する。

Facadeパターンは、複雑に関連しあうクラス群を隠蔽するようなクラスを用意し、そのクラスに統一されたAPIを実装します。利用側はそのAPIを通じてクラス群を利用します。
Facade - GoFデザインパターン~STEP2:少し慣れたら - PHPによるデザインパターン入門 - Do You PHP?

以下のクラス図が示されています(PHPによるデザインパターン入門より)。

Facadeパターンのクラス図

DesignPatternsPHP

The primary goal of a Facade Pattern is not to avoid you to read the manual of a complex API. It's only a side-effect. The first goal is to reduce coupling and follow the Law of Demeter.

A Facade is meant to decouple a client and a sub-system by embedding many (but sometimes just one) interface, and of course to reduce complexity.
https://github.com/domnikl/DesignPatternsPHP/tree/master/Facade

ちなみに、

デメテルの法則(Law of Demetre (LoD))または最小知識の原則とは、ソフトウェアの設計、特にオブジェクト指向プログラムの設計におけるガイドラインである。 このガイドラインは1987年の末にかけてノースイースタン大学で作成された。簡潔に言うと「直接の友達とだけ話すこと」と要約できる。基本的な考え方は、任意のオブジェクトが自分以外(サブコンポーネント含む)の構造やプロパティに対して持っている仮定を最小限にすべきであるという点にある。
デメテルの法則 - Wikipedia

そして、Facadeのサンプルは、

<?php

namespace DesignPatterns\Facade;

/**
 *
 * 
 */
class Facade
{
    /**
     * @var OsInterface
     */
    protected $os;

    /**
     * @var BiosInterface
     */
    protected $bios;

    /**
     * This is the perfect time to use a dependency injection container
     * to create an instance of this class
     *
     * @param BiosInterface $bios
     * @param OsInterface   $os
     */
    public function __construct(BiosInterface $bios, OsInterface $os)
    {
        $this->bios = $bios;
        $this->os = $os;
    }

    /**
     * turn on the system
     */
    public function turnOn()
    {
        $this->bios->execute();
        $this->bios->waitForKeyPress();
        $this->bios->launch($this->os);
    }

    /**
     * turn off the system
     */
    public function turnOff()
    {
        $this->os->halt();
        $this->bios->powerDown();
    }
}

/**
Copyright (c) 2013 Dominik Liebler

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

https://github.com/domnikl/DesignPatternsPHP/blob/master/Facade/Facade.php

このFacadeクラスを使えば、osやbiosの知識なしで、systemを起動したり停止できるというサンプルです。

その他の参考URL

Tags: php, programming

2014/03/15(土) Nagoya.php vol.4が開催されます。

名古屋でのPHPの勉強会、Nagoya.phpのvol.4が、2014/03/15(土)に開催されます。

今回は、以下のような非常に興味深い濃い内容になっています。

  • 技術的負債を生んでしまうチーム開発
  • WEBフレームワークとの依存しない付き合い方を考えてみる
  • 「オフラインリアルタイムどう書く」の問題をみんなで解いてみる

どちらかというと一緒に考える系の勉強会です。

PHPやシステム開発に興味のある方なら、初心者からベテランまでどなたでも大歓迎です!

とのことですので、興味のある方は、是非、以下のDoorkeeperからお申し込みください。

関連

Tags: php, nagoyaphp

FuelPHPのwhitelisted_classesにクラスを追加する人はどういう影響があるかきちんと把握しましょう

FuelPHPのデフォルトのビューはPHPであり、FuelPHPはデフォルトでビューに渡した値をHTMLエスケープします。

文字列をビューに渡す場合はそれで問題ないですが、オブジェクトをビューに渡すと少々話がややこしくなります。

例えば、以下のようなクラスを渡す場合です。

class Data
{
    private $var;

    public function get_var()
    {
        return $this->var;
    }

    public function set_var($var)
    {
        $this->var = $var;
    }
}

例えば、こんなコントローラで。

class Controller_Test extends Controller
{
    public function action_index()
    {
        $obj = new Data();
        $obj->set_var('<s>abc</s>');
        $data['obj'] = $obj;
        return View::forge('test/view', $data);
    }
}

ビューも単純に以下のようにします。

<?php

echo $obj->get_var();

この場合、FuelPHPは以下のエラーを返します。

RuntimeException [ Error ]: Object class "Data" could not be converted to string or sanitized as ArrayAccess. Whitelist it in security.whitelisted_classes in app/config/config.php to allow it to be passed unchecked.

そこで、なんだかよくわからずに、app/config/config.phpのwhitelisted_classesに以下のように'Data'を追加するとエラーは出なくなります。

        'whitelisted_classes' => array(
            'Fuel\\Core\\Response',
            'Fuel\\Core\\View',
            'Fuel\\Core\\ViewModel',
            'Closure', 'Data'
        ),

ところが、ブラウザには「abc」と線が引かれたabcという文字が表示されています。

そうです。タグがエスケープされず使えているということです。もし、ユーザの入力したデータを返すオブジェクトをビューに渡し、whitelisted_classesに追加すれば、XSSの可能性があるということです。

whitelisted_classesはもともと出力時の自動エスケープを無効にするクラスを指定する設定なので、当然といえば当然なんですが。

というわけで、whitelisted_classesにクラスを追加する場合は、そのオブジェクトのデータをビューで表示したときにエスケープ漏れがないことを自分で確認する必要があります。

Sanitizationインターフェイス

FuelPHP 1.7.1からSanitizationインターフェイスが追加されました。これは、ビューでの表示用にデータを加工するためのインターフェイスです。

エスケープ漏れの問題は、ビューに渡されたオブジェクトがデータを表示する際、自分でちゃんとエスケープなり何なりの処理をすれば解消されるわけです。そのためのインターフェイスです。

先ほどのDataクラスにSanitizationインターフェイスを実装すると以下のようになります。

class Data implements Sanitization
{
    private $var;
    private $_sanitization_enabled = false;

    public function get_var()
    {
        if ($this->_sanitization_enabled)
        {
            return Security::htmlentities($this->var);
        }

        return $this->var;
    }

    public function set_var($var)
    {
        $this->var = $var;
    }

    public function sanitize()
    {
        $this->_sanitization_enabled = true;
        return $this;
    }

    public function sanitized()
    {
        return $this->_sanitization_enabled;
    }

    public function unsanitize()
    {
        $this->_sanitization_enabled = false;
        return $this;
    }
}

簡単に説明すると、

  • ビューでの表示時にsanitize()メソッドが呼ばれるので、そこでsanitizeオンの状態にする
  • sanitizeオンの状態での出力はエスケープするように処理する

というものです。

Sanitizationインターフェイスを実装すれば、whitelisted_classesに追加する必要はありません。

これで、ブラウザでは「<s>abc</s>」と表示されました。

ただし、

class Data implements Sanitization
{
    public $var;

のようにpublicでプロパティを宣言し、ビューで以下のように

echo $obj->var;

直接アクセスしてしまえば、エスケープされず生のデータが表示されてしまいますので、Sanitizationインターフェイスを実装していても無意味になります。

これを防ぐには、publicなプロパティをなくし、マジックメソッドでプロパティへのアクセスを完全にコントロールする必要があります。

Tags: fuelphp