Strict Standards: Declaration of...

Baranov_Dron

Новичок
Strict Standards: Declaration of...

класс вида
PHP:
class AutoEtapTwoForm extends sfForm
{
................................

    public function bind()
    {
        $request = sfContext::getInstance()->getRequest();

        $values = array(
		  'email'    => $request->getParameter('email'),
		  'contact'  => $request->getParameter('contact'),
		);

        parent::bind($values, array());
    }
}
вызвал ошибку
<b>Strict Standards</b>: Declaration of AutoEtapTwoForm::bind() should be compatible with that of sfForm::bind() in <b>C:\xampp\htdocs\as3\apps\board\lib\form\AutoEtapTwo.class.php</b> on line <b>11</b><br />
посмотрел базовый класс
PHP:
public function bind(array $taintedValues = null, array $taintedFiles = null)
Попробывал так
PHP:
class AutoEtapTwoForm extends sfForm
{
................................
    public function bind(array $taintedValues = null, array $taintedFiles = null)
    {
        $request = sfContext::getInstance()->getRequest();

        $values = array(
		  'email'    => $request->getParameter('email'),
		  'contact'  => $request->getParameter('contact'),
		);

        parent::bind($values, array());
    }
}
Всё сработало... Но почему php ставит такие ограничения? если в функции которую я переопределяю я должен использовать те же типы аргументов, что и в базовом классе, зачем разработчики так поступили?
И что мне делать, если в функции аргументы не нужны, не охото создавать два аргумента лишь для галочки...
 

Baranov_Dron

Новичок
спасибо! действительно я не обратил внимания, что класс имеет строчку implements, невнимательность...
 

Rone

Новичок
Получил такую же ошибку...
Допустим, в потомке надо переопределить метод родителя, принимающий на вход экземпляр своего же класса. Пишем:
PHP:
class A {
	public function myFunc(A $other) {}
}

class B extends A {
	public function myFunc(B $other) {
		parent::myFunc($other);
	}
}
Работает, но на выходе получаем "Strict Standards: Declaration of B::myFunc() should be compatible with that of A::myFunc()..."
Но почему? Ведь в мануале сказано:
Если класс или интерфейс указан для контроля типа, то все его потомки или реализации также допустимы
 

Вурдалак

Продвинутый новичок
Да, так нельзя, потому что
PHP:
class C
{
	public function doSomething(A $a)
	{
		$a->myFunc(new A);
	}
}
Если ты передашь этому методу инстанс класса B, то в момент вызова myFunc произойдёт ошибка: он ожидает объект класса B, а ему дали A, поэтому интерфейс должен сохраняться.

А в мануале речь идёт о передаче объекта (инстанса), а не о наследовании.
 

Rone

Новичок
наследуясь ты переопределяешь реализацию метода, но не его интерфейс, базовый принцип ООП -- полиморфизм.
Разумеется, но после плюсов как-то непривычно. Ведь В - не какой-то сторонний объект, а наследник А (читай, является также и объектом А).
Например, такая штука отработает в С++ на ура:
PHP:
class A {
	private:
		double x;
		
	public:
		A(double x) : x(x) {}
		void operator+=(const A &other) {
			x += other.x;
		}
};

class B : public A {
	private:
		double y;
		
	public:
		B(double x, double y) : A(x), y(y) {}
		void operator+=(const B &other) {
			A::operator+=(other);
			y += other.y;
		}
};
Сложилось так, что нужно что-то подобное написать на рнр. Ладно нет перегрузки операторов, но вот что при контроле типа надо указывать родителя, никак не ожидал...
 

Вурдалак

Продвинутый новичок
Неа, в C++ такое тоже не будет работать так, как ты, насколько я понимаю, ожидаешь. Если ты вызовешь класс B в контексте, где подразумевается класс A (короче, смотри мой пример выше), то вызовется оператор += из класса A. Ну а в PHP к тому же нет перегрузки методов (здесь правильнее говорить о перегрузке методов, а не операторов, акцент именно на них).
 

Rone

Новичок
Неа, в C++ такое тоже не будет работать так, как ты, насколько я понимаю, ожидаешь. Если ты вызовешь класс B в контексте, где подразумевается класс A (короче, смотри мой пример выше), то вызовется оператор += из класса A. Ну а в PHP к тому же нет перегрузки методов (здесь правильнее говорить о перегрузке методов, а не операторов, акцент именно на них).
Под отработает, подразумевалось, что скажем код:
PHP:
B *b1 = new B(1, 1);
B *b2 = new B(10, 10);
	
*b1 += *b2;
Не вызовет никаких предупреждений и проблем (в итоге оба поля будут равны 11). Что в данном конкретном случае является целью. Если же поменять первую строку на:
PHP:
A *b1 = new B(1, 1);
То будет вызываться += класса А.

В любом случае, спсб, что ткнули носом - буду в этом разбираться.

И ещё один вопрос. В этом примере - сложение и т.д. некоторых полей с полями другого экземпляра - можно обойтись без контроля типа:
PHP:
class A {
	private $x;
	
	public function __construct($x) {
		$this->x = $x;
	}
	
	public function add($other) {
		if ( !($other instanceof A) ) {
			throw new Exception();
		}
		$this->x += $other->x;
	}
}

class B extends A {
	private $y;
	
	public function __construct($x, $y) {
		parent::__construct($x);
		$this->y = $y;
	}
	
	public function add($other) {
		if ( !($other instanceof B) ) {
			throw new Exception();
		}
		parent::add($other);
		$this->y += $other->y;
	}
}
Код рабочий. Но...приемлем ли такой вариант? Или это называется выкрутился?
 

fixxxer

К.О.
Партнер клуба
PHP:
class A {
    private $x;
    
    public function __construct($x) {
        $this->x = $x;
    }
    
    public function add(A $other) {
        $this->x += $other->x;
    }
}

class B extends A {
    private $y;
    
    public function __construct($x, $y) {
        parent::__construct($x);
        $this->y = $y;
    }
    
    public function add(A $other) {
        parent::add($other);
        if ($other instanceof B) 
           $this->y += $other->y;
    }
}
Вот так будет идентично твоему примеру на С++.
 

Sufir

Я не волшебник, я только учусь
Вот, кстати! Всё думаю как сделать... Есть интерфейс "хранилища":
PHP:
interface StorageInterface {

  public function getOne($id = null);
  public function getAll();
  public function getCount();
  ...
  public function save(EntityInterface $entity);

}
Есть классы имплементирующие этот интерфейс. Но при этом они могут работать с разными сущностями. Дело в том, что все сущности имплементируют интерфейс EntityInterface, методы у них совпадают, но при этом очень сильно различаются свойства (количество, именование, типы). Пока сделал, на скорую руку, вот так:
PHP:
class SomeStorage implements StorageInterface {

    public function save(EntityInterface $entity) {
        if (!$entity instanceof SomeEntity) {
            throw new Exception('...');
        }
    }

}
Стоит убрать из интерфейса метод save, но каждое хранилище должно его реализовывать. Убрать проверку типа из интерфейса и воспользоваться throw Exception уже в конкретных классах?
Или, может, я вообще нагородил что-то не то по неопытности? Мнения, рекомендации?
 

Здыхлик

Kohaner
Команда форума
Возможно, меня закидают помидорами...

PHP:
class SomeStorage implements StorageInterface {

    protected function _check_entity($entity) {
         return ($entity instanceof SomeEntity);
    }

    public function save(EntityInterface $entity) {
        if (!$this->_check_entity($entity)) {
            throw new Exception('...');
        }

        // реализация отличается для разных классов
        $this->_do_save($entity);
    }
}
Если для дочернего класса требуется не EntityInterface, а один из его потомков, то добавляем в него свою реализацию _check_entity(). Ну и сохранение тоже у каждого класса свое. Если вдруг окажется, что сохранение более-менее общее для всех, то можно по аналогии с _check_entity() сделать метод _do_save() и в базовом классе SomeStorage.

Вообще, можно убрать интерфейс и сделать SomeStorage абстрактным, если он сам по себе не является цельным и дееспособным.
 

fixxxer

К.О.
Партнер клуба
Дело в том, что все сущности имплементируют интерфейс EntityInterface, методы у них совпадают, но при этом очень сильно различаются свойства (количество, именование, типы).
А не всё равно? Приведи пример, где это важно. У тебя явно в Storage идут ответственности которые должны быть в Entity, оттуда и проблемы.
 

Sufir

Я не волшебник, я только учусь
А не всё равно? Приведи пример, где это важно. У тебя явно в Storage идут ответственности которые должны быть в Entity, оттуда и проблемы.
Вполне вероятно, что я изначально не верно что-то напланировал. Ну, на мой взгляд задача хранилища - сохранить сущность. Например так:
PHP:
class DbStorageNews implements StorageInterface {

    public function save($entity) {
        
        $dbTable->insert(array(
            'title' => $entity->title,
            'body' => $entity->body,
            'date' => $entity->date,
        ));

    }

}

class FileStorageNews implements StorageInterface {

    public function save($entity) {

       file_put_contents($entity->title . ';' . $entity->body . ';' . $entity->date . "\n");

    }

}

class DbStorageRequest implements StorageInterface {

    public function save($entity) {

        $dbTable->insert(array(
            'date' => $entity->date,
            'client' => $entity->client,
        ));

    }

}
Ну, и вероятно стоит добавить прослойку в виде адаптера, что б одно хранилище было (да и для тестов). Но это история уже отдельная:
PHP:
class DbStorageNews implements StorageInterface {

    public function save($entity) {
        
        $adapter->append(array(
            'title' => $entity->title,
            'body' => $entity->body,
            'date' => $entity->date,
        ));

    }

}
Я себе представлял это как-то так, fixxxer. Буду благодарен, если укажешь на ошибки и предложишь более верное решение.
 
Сверху