読み取り専用の配列を用いて柿の種が降ってきてあわててミドリゾウリムシを持って逃げる防空頭巾姿の白い生き物のプロフィールを保障する。

| | コメント(9) | トラックバック(0)
xxxnyann-seiza.png

定数のような振る舞いをする array やオブジェクトが必要になるケースとはどのような場面でしょうか。 例えば、あるキャラクタの性格を array へ定義しておきそれを参照するケースにて、第三者によって後付設定を付加されたり書き換えられてしまい、 キャラクタが作者の意図しない方向へ進んでしまう可能性がある場合です。

<?php

/**
 * プロフィールを定義します。ここ以外で変更しちゃダメ!
 * 
 * @type array
 */
$profile = array(
        'image' => 'xxxnyann-zukai.png',
        'nickname' => 'モチ',
        'favorite' => 'お団子',
        'hobby' => '散歩',
        'skill' => 'じゃんけん',
        'option' => '井伊直政当世具足',
    );

$profile['favorite'] = 'お魚、お肉'; // な、何するだァー!
$profile['option'] = 'しっぽ'; // な、何するだァー!

?>

<table summary="ひこにゃんのプロフィールです。">
    <caption>ズバリ!これがひこにゃんだ!</caption>
    <thead>
        <tr>
            <th>いめーじ</th>
            <th>ふぃーちゃー</th>
            <th>すぺっく</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td rowspan="5"><img src="<?php echo htmlspecialchars($profile['image']); ?>" width="120" height="160" /></td>
            <td>ひこにゃんマウス</td>
            <td><?php echo htmlspecialchars($profile['favorite']); ?>をおもむろにむさぼるぞ!</td>
        </tr>
        <tr>
            <td>ひこにゃんハンド</td>
            <td><?php echo htmlspecialchars($profile['skill']); ?>でどんな逆境も順境に!</td>
        </tr>
        <tr>
            <td>ひこにゃんレッグ</td>
            <td><?php echo htmlspecialchars($profile['hobby']); ?>で鍛えた足腰で傾斜角60度の坂でもぐいぐいのぼる!</td>
        </tr>
        <tr>
            <td>ひこにゃんオプション</td>
            <td><?php echo htmlspecialchars($profile['option']); ?>がとても魅力的だぞ!</td>
        </tr>
    </tbody>
</table>
profile.png

これを防ぐ手立てとして、要素を変更できない array が欲しくなります。残念ながら PHP には読み取り専用の配列は用意されていないので、自作する必要があります。

<?php

/**
 * ひこにゃんのプロフィールを表します。
 */
class Profile implements ArrayAccess, IteratorAggregate, Countable
{
    /**
     * プロフィールの要素を収めた array
     *
     * @var array
     */
    private $_items;
    
    /**
     * オブジェクトが読み取り専用かどうかを表す値。
     *
     * @var bool
     */
    private $_readOnly;
    
    /**
     * Profile クラスの新しいインスタンスを初期化します。
     *
     * @param mixed $base 基の要素となる array または Traversable インターフェイスを実装したオブジェクト。
     * @param bool $readOnly オブジェクトが読み取り専用かどうかを表す値。
     */
    public function __construct($base = null, $readOnly = false)
    {
        if ($base === null)
        {
            $this->_items = array();
        }
        elseif (is_array($base))
        {
            $this->_items = $base;
        }
        elseif ($base instanceof Traversable)
        {
            $this->_items = iterator_to_array($base, true);
        }
        else
        {
            throw new InvalidArgumentException('$base がヘンな型です。');
        }
        
        $this->_readOnly = $readOnly;
    }
    
    /**
     * オブジェクトが読み取り専用かどうかを表す値を取得します。
     *
     * @return bool オブジェクトが読み取り専用の場合は TRUE、そうでない場合は FALSE。
     */
    public function isReadOnly()
    {
        return $this->_readOnly;
    }
    
    /**
     * オブジェクトが読み取り専用かどうかを表す値を取得します。
     *
     * @param bool $readOnly オブジェクトが読み取り専用かどうかを表す値;
     */
    public function setReadOnly($readOnly)
    {
        $this->_readOnly = (bool)$readOnly;
    }
    
    /**
     * 現在のオブジェクトの array 表現を取得します。
     *
     * @return array array。
     */
    public function toArray()
    {
        return $this->_items;
    }
    
    
    // ここから ArrayAccess インターフェイスの実装。
    
    /**
     * 指定したキーの要素を取得します。
     *
     * @param mixed $key キー。
     * @return mixed 値。
     */
    public function offsetGet($key)
    {
        if (!array_key_exists($key, $this->_items))
        {
            throw new OutOfRangeException('$key がオブジェクトに存在しません。');
        }
        
        return $this->_items[$key];
    }
    
    /**
     * 指定したキーの要素を新しい値で更新します。
     *
     * @param mixed $key キー。
     * @param mixed $value 新しい値。
     */
    public function offsetSet($key, $value)
    {
        if ($this->_readOnly)
        {
            throw new BadMethodCallException('オブジェクトが読み取り専用になっている状態で要素を変更しようとしました。');
        }
        
        $this->_items[$key] = $value;
    }
    
    /**
     * 指定したキーがオブジェクトに存在するかどうかを表す値を返します。
     *
     * @param mixed $key キー。
     * @return bool 指定したキーがオブジェクトに存在する場合は TRUE、それ以外の場合は FALSE。
     */
    public function offsetExists($key)
    {
        return isset($this->_items[$key]);
    }
    
    /**
     * 指定したキーの要素をオブジェクトから削除します。
     *
     * @param mixed $key キー。
     */
    public function offsetUnset($key)
    {
        if ($this->_readOnly)
        {
            throw new BadMethodCallException('オブジェクトが読み取り専用になっている状態で要素を変更しようとしました。');
        }
        
        unset($this->_items[$key]);
    }
    
    
    // ここから IteratorAggregate インターフェイスの実装。
    
    /**
     * 現在のオブジェクトの Iterator オブジェクトを取得します。
     *
     * @return Iterator 現在のオブジェクトの Iterator オブジェクト。 
     */
    public function getIterator()
    {
        return new ArrayIterator($this->_items);
    }
    
    
    // ここから Countable インターフェイスの実装。
    
    /**
     * 現在のオブジェクトの要素数を取得します。
     *
     * @return int 要素数。 
     */
    public function count()
    {
        return count($this->_items);
    }
    
}

Profile クラスは、SPL 関数で定義されている ArrayAccess インターフェイス、IteratorAggregate インターフェイス、そして Countable インターフェイスを実装しています。 ArrayAccess インターフェイスを実装すると、オブジェクトを配列のように扱えます。

<?php

$profile = new Profile(array('abc' => 123));
echo $profile['abc'];

# 123

IteratorAggregate インターフェイスを実装すると、オブジェクトを foreach で扱えるようになります。

<?php

$profile = new Profile(array(1, 2, 3, 4, 5));

foreach ($profile as $key => $value)
{
    printf('$profile[%s] is %s%s', (string)$key, (string)$value, PHP_EOL);
}

# $profile[0] is 1
# $profile[1] is 2
# $profile[2] is 3
# $profile[3] is 4
# $profile[4] is 5

Countable インターフェイスを実装すると、count() の引数としてオブジェクトが渡された際の振る舞いを変更することが出来ます。

<?php

$profile = new Profile(array(1, 2, 3, 4, 5));
echo count($profile);

# 5

今回の目的である要素の変更禁止は、これらのメソッドを定義する際に読み取り専用かどうかによって動作を変える事で実現しています。 読み取り専用かどうかはコンストラクタの $readOnly で指定するか、Profile::setReadOnly() で設定します。

<?php

$profile = new Profile(array(1, 2, 3), true);
//unset($profile[0]);

# Fatal error: Uncaught exception 'BadMethodCallException' with
# message 'オブジェクトが読み取り専用になっている状態で要素を変更しようとしました。' in ...

では、Profile クラスを使って最初の例と置き換えてみましょう。

<?php

/**
 * プロフィールを定義します。
 * 
 * @type Profile
 */
$profile = new Profile(
        array(
                'image' => 'xxxnyann-zukai.png',
                'nickname' => 'モチ',
                'favorite' => 'お団子',
                'hobby' => '散歩',
                'skill' => 'じゃんけん',
                'option' => '井伊直政当世具足',
            ),
        true
    );

$profile['favorite'] = 'お魚、お肉'; // な、何するだァー!
$profile['option'] = 'しっぽ'; // な、何するだァー!

// ...
// ...
// ...

# Fatal error: Uncaught exception 'BadMethodCallException' with message
# 'オブジェクトが読み取り専用になっている状態で要素を変更しようとしました。' in ...

かくして、ひこにゃんの方向性を固めることに成功しました。ですがこれは数ある対応策であり解決策ではありません。$profile['favorite'] = '肉だんご、魚肉だんご' にするとか、しっぽを許してあげるとか、 お互いが譲り合うのもいいのではないかと思う、この頃です。

トラックバック(0)

このブログ記事を参照しているブログ一覧: 読み取り専用の配列を用いて柿の種が降ってきてあわててミドリゾウリムシを持って逃げる防空頭巾姿の白い生き物のプロフィールを保障する。

このブログ記事に対するトラックバックURL: http://zenith.sakura.ne.jp/mt/mt-tb.cgi/5

コメント(9)

One more new write-up with powerful points, I've been a lurker below for any brief time but wish to become a great deal much more engaged inside long term.

Me and my friend were arguing about an problem similar to this! Now I realize that I had been correct. lol! Thank you for that information you article.

It really is good to possess the capacity to examine a great high quality article with practical specifics on topics that plenty are interested on. The stage that the information indicated are all first hand on reside experiences even guide a lot more. Proceed performing what you do as we really like readi?-

I admire the valuable info you offer you inside your articles. I'll bookmark your website and have my kids examine up the following typically. I am really confident they will understand a lot of new stuff below than anybody else!

I really enjoy the article post.Much thanks again. Will read on…

We altered this username and password instantly after i identified vizgin was obviously a fraud... My spouse and i aware every one of the folks in the pal collection over it. That received delivered to myself via a person in the Bing good friend listing... they were signed down. I really hope they will didn't receive cheated. Their a new shame folks would likely do that.

Thanks for this content. We attempted this specific and yes it perfectly proved helpful.

While I might commonly concur, in such cases it's rather trivial. Furthermore, Wikipedia provides are available an extended techniques as it has been bashed to get letting customers to help edit articles, there are numerous clubs involving moderators in which trail in addition to resolve the various blunders usual consumers create any time adding/editing a new wiki post.

Excellent website I really like the layout and fast responsive technology utilized in the template your site uses. Great work keep writing.

コメントする


画像の中に見える文字を入力してください。

このブログ記事について

このページは、zenithが2007年12月 9日 02:19に書いたブログ記事です。

ひとつ前のブログ記事は「柿の種が降ってきてあわててミドリゾウリムシを持って逃げる防空頭巾姿の白い生き物がクラスの定義ファイルを読み込む手段に spl_autoload_register() をお勧めする7つの理由。」です。

次のブログ記事は「最近のお気に入り」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Powered by Movable Type 4.12