Как создать класс PHP, который может быть приведен к логическому (быть правдивым или ложным) - PullRequest
8 голосов
/ 24 мая 2011

Я создаю класс коллекции и хотел бы, чтобы он заменял массивы, которыми я сейчас пользуюсь.

Как создать класс, который может быть приведен к логическому, чтобы класс мог быть правдивым или ложным?

Простой тест показывает, что объект пустого класса является правдивым:

class boolClass {}
$obj = new boolClass();
var_dump( (bool)$obj);
//prints 
//bool(true)

Но мне нужно решить, правдив ли мой класс или нет. Есть ли способ рассказать движку PHP, как привести мой класс к логическому? Как я мог бы сделать с __toString ()?

Справочная информация:

Допустим, я пишу такой класс (это только пример):

class MyCollection implements ArrayAccess, Iterator {
    //...
}

В настоящее время я интенсивно использую этот паттерн:

$var = array();

if (empty($var)) {
   //array is empty, (or there is no array at all)
   // I do something here
}

Я бы хотел, чтобы это выглядело так:

$var = new MyCollection(array());

и оставьте остальные без изменений. Но $ var, содержащий MyCollection, всегда верен, поэтому мне необходимо выполнить все условия:

if ($var->isEmpty()) {
    //...
}

Но это недопустимо, так как в моей кодовой базе много мегабайт.

Есть здесь какое-нибудь решение?

Ответы [ 6 ]

21 голосов
/ 15 января 2013

После долгих волнений, разочарований и взломов - я думаю, что нашел решение.Решение не требует каких-либо расширений;это может быть реализовано с очень небольшим количеством шаблонов PHP.Однако, прежде чем приступить к реализации этого решения самостоятельно, обратите внимание, что это - ОГРОМНЫЙ взлом.При этом, вот что я обнаружил:

Разочарованный, я потратил некоторое время на просмотр документации PHP для Booleans .В то время как созданные пользователем классы просто лишены возможности быть приведенными как булевы, одному классу - как ни странно - была предоставлена ​​возможность.Проницательный читатель заметит, что этот класс - не что иное, как встроенный SimpleXmlElement .В процессе дедукции кажется справедливым предположить, что любой подкласс SimpleXmlElement также унаследует свою уникальную возможность логического приведения.Хотя теоретически этот подход кажется верным, магия, окружающая SimpleXmlElement, также лишает его полезности.Чтобы понять, почему это так, рассмотрим следующий пример:

class Truthy extends SimpleXmlElement { }

Truthy является подклассом SimpleXmlElement, поэтому мы должны иметь возможность проверить, было ли унаследовано его специальное свойство boolean-casting:

$true = new Truthy('<true>1</true>'); // XML with content eval's to TRUE
if ($true) echo 'boolean casting is happening!'; 
$false = new Truthy('<false></false>'); // empty XML eval's to FALSE
if (!$false) echo 'this is totally useful!';

Действительно, свойство приведения к логическому значению, предоставленное SimpleXmlElement, наследуется Truthy.Тем не менее, этот синтаксис неуклюж, и маловероятно, что кто-то получит большую пользу от этого класса (по крайней мере, если сравнивать с использованием SimpleXmlElement изначально).В этом сценарии начинают появляться проблемы:

$false = new Truthy('<false></false>'); // empty XML eval's to FALSE
$false->reason = 'because I said so'; // some extra info to explain why it's false
if (!$false) echo 'why is this not false anymore?!?';
else echo 'because SimpleXMLElements are magical!';

Как вы можете видеть, попытка установить свойство в нашем подклассе немедленно ломает утилиту, которую мы получаем из унаследованного логического преобразования.К сожалению для нас, SimpleXmlElement имеет еще одну волшебную особенность, которая нарушает наше соглашение.По-видимому, когда вы устанавливаете свойство SimpleXmlElement, оно изменяет XML!Убедитесь сами:

$xml = new SimpleXmlElement('<element></element>');
xml->test = 'content';
echo $xml->asXML(); // <element><test>content</test></element>

Ну, есть любая утилита, которую мы получили бы от создания подкласса SimpleXmlElement!К счастью, после долгих взломов я нашел способ сохранить информацию в этом подклассе, не нарушая магию булевского кастинга: комментарии!

$false = new Truthy('<!-- hello world! --><false></false>');
if (!$false) echo 'Great Scott! It worked!';

Прогресс!Мы смогли получить полезную информацию в этом классе, не нарушая логическое приведение!Хорошо, теперь все, что нам нужно сделать, это очистить его, вот моя окончательная реализация:

class Truthy extends SimpleXMLElement {

public function data() {

    preg_match("#<!\-\-(.+?)\-\->#", $this->asXML(), $matches);

    if (!$matches) return null;

    return unserialize(html_entity_decode($matches[1]));


}

public static function create($boolean, Serializable $data = null) {
    $xml  = '<!--' . htmlentities(serialize($data)) . "-->";
    $xml .= $boolean ? '<truthy>1</truthy>' : '<truthy/>';
    return new Truthy($xml);
}

}

Чтобы удалить некоторую неуклюжесть, я добавил публичный статический метод фабрики.Теперь мы можем создать объект Truthy, не беспокоясь о деталях реализации.Фабрика позволяет вызывающей стороне определять любой произвольный набор данных, а также логическое приведение.Затем можно вызвать метод данных, чтобы получить копию этих данных, доступную только для чтения:

$false = Truthy::create(false, array('reason' => 'because I said so!'));
if (!$false) {
   $data = $false->data();
   echo 'The reason this was false was ' . $data['reason'];
}

Вот и все!Совершенно хакерский (но пригодный для использования) способ выполнить логическое приведение в пользовательских классах.Пожалуйста, не судитесь со мной, если вы используете это в рабочем коде, и он взорвется.

7 голосов
/ 24 мая 2011

На этой странице перечислены магические методы, которые вы можете определить для своих классов.

http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring

Вы демонстрируете, что уже знаете о __toString().

К сожалению, там нет ни одного магического метода, который бы делал то, что вы просите Итак, я думаю, что пока ваш единственный вариант - определить метод и вызвать его явно.

1 голос
/ 24 мая 2011

Вы не можете.

В PHP объект при приведении к bool всегда создает true.Нет способа изменить это.

0 голосов
/ 24 мая 2011

Если вы не хотите реализовывать свой собственный метод, вы можете взломать свой путь в __toString, например:

class foo
{
    public function __toString()
    {
        return strval(false);
    }
}

$foo = new foo();

if (strval($foo) == false) // non-strict comparison
{
   echo '$foo is falsy';
}

echo (bool) strval($foo); // false
0 голосов
/ 24 мая 2011

Если у вас размер кода «много мегабайт», возможно, проблема не в том, что ваша схема именования слишком многословна. Вместо этого я хотел бы искать дублирование и пытаться абстрагировать код.

OTOH, почему размер кода является большой проблемой? Старайтесь минимизировать код, который вы включаете каждый раз, когда запускается php. Блуждающий код, который не используется, не имеет значения. Если у вас есть для включения большого количества кода, рассмотрите возможность использования программного обеспечения для кэширования, такого как MMCache.

И чтобы ответить на ваш оригинальный вопрос, AFAIK, нет способа добавить приведение типов в классы PHP. экземпляр всегда будет иметь значение true.

...