<rmcreative>

RSS

Осторожно, in_array

3 сентября 2011

Такая функция как in_array используется очень часто. Однако, есть особенности, на которые сегодня обратил моё внимание aktuba, дав мне вот такой кусочек кода:

$array = array(0, 'one', 'two');
var_dump(in_array('three', $array));

Работает он немного неожиданно — выдаёт true. Всё дело в том, что строка three приводится к типу элемента массива перед сравнением. В нашем случае интересно приведение к числу.

Если такое поведение нежелательно, можно заставить сравнивать элементы ещё и по типу, передав третий параметр:

$array = array(0, 'one', 'two');
var_dump(in_array('three', $array, true));

Комментарии RSS

  1. №5279
    Stamm
    Stamm 03.09.2011, 23:17:02

    Ага, такой вопрос есть в тесте от мамбы =) Ещё из интересностей PHP. Есть приватный или защищённый метод класса. Он него инстанциируется объект. К этому методу может получить доступ другой инстанцированный объект от этого же класса =) Много узнал, когда готовился к Zend Certification. =)

  2. №5280
    holdmann
    holdmann 03.09.2011, 23:46:00

    Не совсем вменяемый пример, имхо. Чаще всего бывает вот так:

    $array = array('one', 'two');
    var_dump(in_array(0, $array)); // true
    var_dump(in_array(0, $array, $strict = true)); // false
  3. №5281
    NickSun
    NickSun 03.09.2011, 23:48:02

    Вот вам тоже задачка:

    $array = array('one' => 'one', 'two' => 'two', 3  => 3, 4 => 4, 'five' => 'five');
    $result = array_merge($array);

    Каков результат?

  4. №5282
    holdmann
    holdmann 03.09.2011, 23:53:52

    Есть еще один интересный пример, хотя он и описан в документации, но все же. Нельзя проверять строки на пустоту функцией empty();

    $string = '0';
    var_dump(empty($string)); // true
  5. №5283
    holdmann
    holdmann 03.09.2011, 23:57:18

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

  6. №5284
    dckur
    dckur 04.09.2011, 1:45:22

    holdmann

    $string = '0';
    var_dump(empty($string)); // true

    Ну это как раз стандартное поведение...
    Все это выведет true

    • "" (an empty string)
    • 0 (0 as an integer)
    • 0.0 (0 as a float)
    • "0" (0 as a string)
    • NULL
    • FALSE
    • array() (an empty array)
    • var $var; (a variable declared, but without a value in a class)
  7. №5286
    holdmann
    holdmann 04.09.2011, 2:49:53

    Я понимаю, что это стандартное поведение, впрочем как и в случае NickSun'a. Как это часто бывает, из-за банальной невнимательности, возникает трудноотлавливаемая ошибка.

    Поэтому надо либо на 100% понимать что делаешь, либо использовать более подходящие под конкретный тип данных функции.

    http://phplens.com/phpeverywhere/node/view/52 (6 пункт).

  8. №5290
    dV
    dV 04.09.2011, 10:35:30

    @stamm Не совсем понял про ООП. Поясни еще раз, плиз!

  9. №5292
    resurtm
    resurtm 04.09.2011, 11:22:02

    @dV, имеется ввиду, что в PHP все private и protected методы по умолчанию являются class friend'ами в стиле C++ для своего же класса.

    Вот этот код отработает верно, без ошибок и выдаст двойку, а затем единицу:

    class TestClass
    {
        public $title='';
        public $object=null;
     
        public function __construct($title, $object=null)
        {
            $this->title=$title;
            $this->object=$object;
        }
     
        private function testMethod()
        {
            echo $this->title.'<br/>';
            if($this->object)
                $this->object->testMethod();
        }
     
        public function runTestMethod()
        {
            $this->testMethod();
        }
    }
     
    $testObject1=new TestClass('1');
    $testObject2=new TestClass('2', $testObject1);
     
    $testObject2->runTestMethod();
  10. №5293
    resurtm
    resurtm 04.09.2011, 11:25:07

    Комментарий: testMethod одного экземпляра класса вызывает testMethod другого экземпляра класса, игнорируя права доступа (private, в данном случае).

  11. №5294
    dV
    dV 04.09.2011, 12:21:35

    @resurtm К сожелению, PHP не дает возможности вызывать защищенный метод класса из контекста другога класса, если они ИМПЛЕМЕНТИРУЮТ один интерфейс. Более того, если они наследуют общий класс, все равно не помагает объкету одного класса вызывать защищенный метод объекта другога класса! Только и только если объекты принадлежат ОДНОМУ классу, либо объект, кот вызывает защищенный метод может принадлежать классу, кот ОБЯЗАТЕЛЬНО НАСЛЕДУЕТСЯ от класса того объекта, чей метод вызывает.

    Я много думал о friend-методах в стиле С++ для PHP, когда рассуждал о парретне State. В случае смены состояний у контекста конкретным состоянием, этот метод должен быть защищенным и friend'ом конеткрному состоянию (общему интерфейсу всех конкретных состояний). Другими словами, когда клиент использует контекст он не может явно менять состояние контекста, но котекст должен задавать другое состояние контексту.

  12. №5295
    dV
    dV 04.09.2011, 12:23:05

    Может быть кто-нибудь может внятно объяснить эту особенность и её применение? Еще лучше, если поделитесь ссылкой.

    Спасибо.

  13. №5296
    resurtm
    resurtm 04.09.2011, 12:28:19

    Практического применения получается, что и нет, если учесть, что для интерфейсов и/или наследования это не работает.

    По поводу отсутствия friend в PHP — согласен, немного напрягает. :(

  14. №5297
    dV
    dV 04.09.2011, 12:37:38

    №5294

    Касательно моего комментария, конечно же я имел ввиду:

    Другими словами, когда клиент использует контекст он не может явно менять состояние контекста, но ОДНО СОСТОЯНИЕ должено задавать ДРУГОЕ СОСТОЯНИЕ контексту.

  15. №5298
    Sam
    Sam 04.09.2011, 19:20:10

    Про модификаторы я как-то тоже озадачивался.

  16. №5299
    mrix
    mrix 04.09.2011, 20:09:55

    Натыкался на такое в switch значений из GET

    <?php
    function test($value)
    {
        switch ($value)
        {
            case 0:
                return var_export($value, true) . ' = 0';
     
            case 1:
                return var_export($value, true) . ' = 1';
     
            default:
                return var_export($value, true) . ' = default';
        }
    }
     
    $values = array('1', 'sasa', 5);
    foreach ($values as $value)
    {
        echo test($value) . '<br />';
    }

    поэтому перешёл с него на if ($value === 1) ...

  17. №5300
    dV
    dV 04.09.2011, 20:39:14

    Хотелось бы поднять тему из старого топика

    class A
    {
     
     public function getName()
     {
      echo 'A';
     }
     
     public function getClassName()
     {
      $this->getName();
     }
    }
     
     
    class B
    {
     public function getName()
     {
      echo 'B';
     }
     
     public function getClassName()
     {
      A::getClassName();
     }
     
    }

    Вызываем:

    A::getClassName();

    Получем: Strict standards: Non-static method A::getClassName() should not be called statically Fatal error: Using $this when not in object context

    Тут все ясно.

    Вызываем:

    $b = new B();
    $b->getClassName();

    Получем: Strict standards: Non-static method A::getClassName() should not be called statically, assuming $this from incompatible context B

    PHP говорит, что конечно неправильно вызывать нестатический метод статически, да и $this использует не из подходящего контекста, НО ответ дает как буд-то класс B наследуется от A, WTF??? Я напомню, что E_STRICT войдет в E_ALL лишь в PHP 6!

    Как мне кажется, это совершенно недопустимое поведение. У кого-нить есть мысли на этот счет?

  18. №5301
    mitallast
    mitallast 04.09.2011, 22:01:30

    dV, интерпретатор пишет, что нестатический метод можно вызывать только в контексте класса, к которому он относится. $this это магичная ссылка на экземпляр класса, в котором метод определен. Соответственно, интерпретатор находит контекст в виде экземпляра класса B, но не находит A. Поскольку классы в совершенно разных деревьях - не содержат общего кода - то возникает ошибка, что метод вызывается вне контекста.

    Почему требуется родной контекст? Ровно по тому же принципу, как болт м8 не сможет держать гайку м6. Экземпляр обьекта - это область памяти, содержащая описанные классом данные. Например,

    class Foo{ int32 number; char8 name; }

    Экземпляр будет занимать 32бита (int32)+ 8 * 8 бит (char8)+ 32бит(ссылка на описание класса). описание класса будет просто хранить описание своих переменных, типа name это int, длиной 32 бита, отступ 0 бит, и т.п.

    Когда класс наследуется и расширяется, этот участок памяти будет расширен новыми переменными, и расширится описание обьекта. Что делает с этим интерпретатор, когда пытается вызвать класс A в контексте класса B? Просто смотрит описание класса текущего контекста - т.е класса B - и сравнивает ссылку на описание класса контекста с ссылкой на класс A. Помимо прочего, проверяется иерархия класса B, на случай, если A есть в этой иерархии. А его нет, вот вам и стрикт еррор.

  19. №5302
    dV
    dV 04.09.2011, 22:09:24

    @mitallast

    Я отлично понимаю почему так происходит, меня удивляет, что эта ошибка уровня E_STRICT. Я видел много программистов, кот пишут код c error_reporting E_ALL.

    Если угодно, это логическая ошибка на уровне языка!

  20. №5303
    mitallast
    mitallast 04.09.2011, 22:10:16

    А по теме - ну реально, пример омерзительного интерфейса. Я на работе сталкиваюсь постоянно с кривыми api, которые сознательно портят из-за недостатков языковых средств, что уже вводит в бешенство :@ Посмотрите на тесты к zend sertifier engineer - over 900 вопросов, так или иначе связанными с костылями на уровне стандартной библиотеки.

    Мне нравятся статически типизированные языки, и динамические. Но я терпеть не могу эту чертовую магию, связанную с преобразованием типов черт знает по какой логике! Такого, как в c++ совершенно не хочется - просто тронуться можно, пишут библиотеки для type cast!!! ИМХО, магичная динамическая типизация порождает больше проблем, чем создает.

  21. №5304
    mitallast
    mitallast 04.09.2011, 22:16:26

    @dV обоснуйте, почему логическая? Безусловно, может существовать метод, не требующий контекста класса и не являющийся статическим. Тогда компилятор действительно может дать поблажку, и ошибка будет на уровне varning, если не ошибаюсь.

  22. №5305
    dV
    dV 04.09.2011, 22:21:56

    @mitallast

    Если существует метод вне контекста класса - то $this в нем неуместен.

    Мы вызываем метод класса A из класса B (напомню никак не связанных между собой), но интерпретатор считает, что хоть $this не в своем контектсе, все равно берет неверное значение. И да, при error_reporting E_ALL все работает без ошибок - поэтому логическая ошибка

  23. №5306
    dV
    dV 04.09.2011, 22:24:10

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

  24. №5310
    Oleg
    Oleg 05.09.2011, 18:59:35

    Ух как тема пошла в ход)) И никто не ожидал такого разворота событий))))

  1. Почта опубликована не будет.

  2. Можно использовать синтаксис Markdown или HTML.

  3. Введите ответ в поле. Щёлкните, чтобы получить другую задачу.