Передавать функции классу

PHP mysql database Я создал следующий вопрос к этому здесь, который посвящен именно разбивке на страницы

Мне нужно вызвать метод из одного класса в другой и иметь возможность изменить вызываемый метод. Вот так

class db{ function a(){ echo 'I run a query';} function b(){ echo 'im the other query';} } class YourClass { var $fcn; $db = new db() function invoke(){ call_user_func($this->fcn); } } $instance = new YourClass; $instance->fcn = 'db->a'; $instance->invoke(); 

Я хочу использовать метод 'a' из класса db в методе 'yourClass' 'invoke' Спасибо

Хорошо, это то, что я собрал из предоставленных ответов, и это работает.

  class A { function a(){ $x = 'Method a is used'; return $x; } function b(){ $x = 'Method b is used'; return $x; } } class B { function invoke($obj, $method){ echo call_user_func( array( $obj, $method) ); } } $instance = new B(); $instance->invoke(new A(),"a"); 

Что пишет: «Метод a используется» на экране

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

 class A { function a($var1,$var2,$var3){ $x = 'the three passed values are ' . $var1 . ' and ' . $var2 . ' and ' . $var3; return $x; } function b(){ $x = 'im method b'; return $x; } } class B { function invoke($obj,$arguments){ echo call_user_func_array($obj,$arguments); } } $arguments = array('apple','banana','pineapple'); $use_function = array(new A(),"a"); $instance = new B(); $instance->invoke($use_function,$arguments); 

Он почти работает, но я получаю эти ошибки выше правильного ответа

Отсутствует аргумент 1 для A :: a (), ….. для аргументов 2 и 3, но затем ответ печатает на экране «три переданных значения: яблоко, банан и ананас»

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

Начиная с PHP5.3 вы можете использовать закрытие или функторы для передачи методов. До этого вы могли бы написать анонимную функцию с create_function () , но это довольно неудобно.

В принципе, то, что вы пытаетесь сделать, может быть сделано с помощью шаблона стратегии .

удаленный пример кода, поскольку он больше не помог после того, как OP изменил вопрос (см. wiki)

Кроме того, вы можете захотеть ознакомиться с архитектурными шаблонами источников данных Fowlers. Zend Framework (и почти все другие фреймворки PHP) предлагает классы доступа к базам данных, которые вы можете использовать для этих шаблонов, а также класс paginator , поэтому почему бы не проверить их, чтобы узнать, как они это сделали.

удалил EDIT 1, поскольку он больше не помог после того, как OP изменил вопрос (см. wiki)

EDIT 2 Хорошо, давайте сделаем шаг за шагом подход к этому (не используя шаблон стратегии, хотя)

То, о чем вы просите в вопросе, можно легко решить с помощью этого кода:

 class Foo { public function bar() { echo 'bar method in Foo'; } } class MyInvoker { protected $myObject; public function __construct() { $this->myObject = new Foo(); } public function __call($method, $args) { $invocation = array($this->myObject, $method); return call_user_func_array($invocation, $args); } } 

С помощью этого кода вы просто вызываете соответствующие методы. Нет настроек имен методов. Нет неуклюжий дополнительный метод вызова. Не изобретать способы вызова методов. Вам это не нужно, потому что PHP имеет функцию __call, которую вы только что научили отправлять все методы, не существующие в MyInvoker, в $ myObject, например Foo .:

 $invoker = new MyInvoker; $invoker->bar(); // outputs 'bar method in Foo called' 

Вы могли бы также расширить MyInvoker, чтобы быть подклассом Foo, например

 class MyInvoker extends Foo {} 

и тогда вы можете сделать то же самое. Это не то, что вам нужно, но это и иллюстрирует, как это бессмысленно, делать такое. MyInvoker теперь ничего не делает сам по себе. Это пустой класс и фактически тот же, что и Foo. Даже с предыдущим подходом, использующим метод __call, он ничего не делает. Вот почему я попросил вас уточнить желаемый результат, который является Paginator.

Первая попытка:

 class Paginator() { // A class holding all possible queries of our application protected $queries; // A class providing access to the database, like PDO_MySql protected $dbConn; public function __construct() { $this->db = new MyPdo(); $this->queries = new DbQueries(); } public function __call($method, $args) { $invocation = array($this->queries, $method); $query = call_user_func_array($invocation, $args); return $this->dbConn->query($query); } } 

С помощью этого кода наш Paginator создает все, что ему нужно, сам по себе, тесно связанный с классом связи db и всеми запросами, и это позволяет вам называть их так,

 $paginator = new Paginator; // assuming we have something like getImageCount() in DbQueries echo $paginator->getImageCount(); 

Тогда происходит то, что Paginator распознает, что он не знает getImageCount () и вызовет метод __call. Метод __call попытается вызвать метод getImageCount () в DbQueries. Поскольку он существует, он возвращает запрос, который, в свою очередь, передается в соединение db для его выполнения. Замечательно, что вы скажете, но это не так. На самом деле это ужасно. Обязанность вашего paginator состоит в том, чтобы подсчитывать элементы в таблице и извлекать элементы из этой таблицы в определенном диапазоне и размере. Но сейчас это ничего не значит. Это совершенно не обращает внимания на то, что происходит, поэтому давайте попробуем новый класс:

 class Paginator { protected $dbConn; protected $itemCount; public function __construct($dbConn) { $this->dbConn = $dbConn; } public function countItems($query) { $this->itemCount = $this->dbConn->query('select count(*) from (?)', $query); return $this->itemCount; } public function fetchItems($query, $offset = 0, $limit = 20) { $sql = sprintf('select * from (?) LIMIT %d, %d', $offset, $limit); return $this->dbConn->query($sql, $query); } } 

Намного лучше. Теперь наш Paginator является совокупностью вместо составного, то есть он не создает объекты внутри себя, а требует, чтобы они были переданы ему в конструкторе. Это называется инъекцией зависимостей (а также обеспечивает свободную связь , когда dbConn использует интерфейс ), что сделает ваше приложение более удобным для обслуживания, так как теперь легко заменить компоненты. Это также пригодится, когда Unit Testing вашего кода.

Кроме того, ваш Paginator теперь концентрируется на том, что он должен делать: подсчет и выборка элементов произвольного запроса. Не нужно проходить методы вокруг. Нет необходимости скрывать вызов метода. Вы бы использовали его так:

 $paginator = new Paginator($dbConn); $query = $dbQueries->findImagesUploadedLastWeek(); // returns SQL query string $images = $paginator->countItems($query); if($images > 0) { $images = $paginator->fetchItems($query); } 

Вот и все. Ну, почти. Конечно, вам нужно будет отображать нумерацию. Но это должно быть довольно тривиально, если вы расширите то, что у вас уже есть. Свойство $ imageCount – это подсказка о том, куда идти дальше.

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

PS $this->dbConn->query($sql, $query) – это, конечно, фиктивный код. Не ожидайте, что сможете скопировать и вставить его и заставить его работать. Кроме того, вы должны убедиться, что запросы, добавленные в Paginator SQL, безопасны в использовании. Вы не хотите, чтобы кто-то вставлял запрос, который удаляет все ваши строки. Никогда не доверяйте пользовательскому вводу.

$query PPS $query должен быть строкой запроса SQL. Проверьте руководство по PHP для PDO :: prepare . В общем, это дает лучшую производительность и безопасность для подготовки заявления перед его выполнением. Страница в руководстве даст вам подсказки о ? в запросах. Если вы не хотите использовать PDO, просто используйте sprintf() или str_replace() для замены ? с $query , например $this->dbConn->query(sprintf('SELECT count(*) from (%s)', $query) но имейте в виду, что это не имеет преимуществ подготовленного оператора и потенциально открывается дверь для уязвимостей SQL Injection .

PPPS Да, инъекция зависимостей обычно является предпочтительной стратегией. Это продвинутая тема, хотя и может быть слишком много, чтобы полностью понять ее прямо сейчас, но стоит посмотреть на нее. На данный момент этого должно быть достаточно, если вы попытаетесь поддержать агрегацию одобрения по составу. Ваши классы должны выполнять только то, за что они отвечают, и получать любые зависимости через конструктор.

Вот два способа сделать это:

 class YourClass { var $fcn; function invoke($arguments){ //one way: $this->{$this->fcn}($arguments); //another way: call_user_func_array(array($this, $this->fcn), $arguments); } function a(){ echo 'I am a()'; } } $instance = new YourClass; $instance->fcn = 'a'; $instance->invoke(); 

Это напечатает «Я () изнутри класса.

Вы почти там

  class db { function a(){ echo 'I run a query';} function b(){ echo 'im the other query';} } class YourClass { var $fcn; function __construct() { $this->db = new db(); } function invoke() { call_user_func(array( $this->{$this->fcn[0]}, $this->fcn[1] )); } } $instance = new YourClass; $instance->fcn = array('db', 'a'); $instance->invoke(); $instance->fcn = array('db', 'b'); $instance->invoke(); 

синтаксис довольно причудливый, но он работает

// edit: из вашего комментария это выглядит как самый простой вариант – передать имя метода как строку, например

  class Paginator { function __consturct($db, $counter_func) ... function get_count() { $args = func_get_args(); return call_user_func_array( array($this->db, $this->counter_func), $args); } } new Paginator($db, 'get_num_products'); 

Я предполагаю, что вы используете php здесь. Php поддерживает переменные функции, которые могут решить вашу проблему, но, насколько мне известно, не поддерживает указатели делегатов / указателей функций.

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

Вы спрашиваете, есть ли у PHP функциональные ссылки? Это не так. Но это позволяет вам вызывать функции, помещая их имя в строку или массив имени класса и имени метода. См. call_user_func() для описания и переменных функций .

 class A { function a(){ echo 'I run a query';} function b(){ echo 'im the other query';} } class B { function Test() { invoke(new $A(), "a"); } function invoke($obj, $method){ // Non static call call_user_func( array( $obj, $method ) ); // Static call //call_user_func( array( 'ClassName', 'method' ) ); } } 

Надеюсь, это поможет. Не забудьте проверить это как правильный ответ.

 class DB { function a(){ echo 'I run a query';} function b(){ echo 'im the other query';} } class B { protected $db; private $method; function __constructor($db) { $this->db; } function invoke($m){ $this->method = $m; // Non static call call_user_func( array( $this->db, $this->method ) ); } } $db = new DB(); $b = new B($db); $b->invoke('a'); 

Я внес небольшие изменения в свой первоначальный ответ. Вы также можете проверить этот пост, это может помочь:

Практика базы данных и ООП в PHP

Вам нужно внести следующие изменения:

 $arguments = array('apple','banana','pineapple'); $a = new A(); $use_function = array(&$a,"a"); // Make this changes to your code $instance = new B(); $instance->invoke($use_function,$arguments); 

Не забудьте отметить это как правильный ответ, если это поможет.

Шаблон проектирования Observer может быть полезен для такого рода вещей, или это может быть неправильное использование шаблона; Я еще не знаю. Во всяком случае, для вашего рассмотрения:

 class DbObserver implements SplObserver { public function update(SplSubject $subject) // Required { $method = $subject->getObserverMethod(); $args = $subject->getObserverArgs(); $this->$method($args); } private function a($args) { echo 'I run query ' . $args[0] . '<br />'; } private function b($args) { echo 'I run query ' . $args[0] . ' because ' . $args[1] . '<br />'; } private function c() { echo 'I have no argument' . '<br />'; } } class ObserverObserver implements SplObserver { public function update(SplSubject $subject) // Required { if (count($subject->getAttached()) > 1) { echo 'I saw that<br />'; } else { echo 'Nothing happened<br />'; } } } class DbSubject implements SplSubject { private $observerMethod; private $observerArgs = array(); private $attached = array(); public function notify() // Required { foreach ($this->attached as $each) { $each->update($this); } } public function attach(SplObserver $observer) // Required { $this->attached[] = $observer; } public function detach(SplObserver $observer) // Required { $key = array_keys($this->attached, $observer); unset($this->attached[$key[0]]); } public function setObserverMethod($method, $args = array()) { $this->observerMethod = $method; $this->observerArgs = $args; return $this; } public function getObserverMethod() { return $this->observerMethod; } public function getObserverArgs() { return $this->observerArgs; } public function getAttached() { return $this->attached; } } $db_subj = new DbSubject; $db_obs = new DbObserver; $db_subj->attach($db_obs); $args = array('A'); $db_subj->setObserverMethod('a', $args)->notify(); $args = array('B', 'I can'); $db_subj->setObserverMethod('b', $args)->notify(); $obsvr = new ObserverObserver; $db_subj->attach($obsvr); $db_subj->setObserverMethod('c')->notify(); $db_subj->detach($db_obs); $db_subj->notify(); /** I run query A I run query B because I can I have no argument I saw that Nothing happened **/ с class DbObserver implements SplObserver { public function update(SplSubject $subject) // Required { $method = $subject->getObserverMethod(); $args = $subject->getObserverArgs(); $this->$method($args); } private function a($args) { echo 'I run query ' . $args[0] . '<br />'; } private function b($args) { echo 'I run query ' . $args[0] . ' because ' . $args[1] . '<br />'; } private function c() { echo 'I have no argument' . '<br />'; } } class ObserverObserver implements SplObserver { public function update(SplSubject $subject) // Required { if (count($subject->getAttached()) > 1) { echo 'I saw that<br />'; } else { echo 'Nothing happened<br />'; } } } class DbSubject implements SplSubject { private $observerMethod; private $observerArgs = array(); private $attached = array(); public function notify() // Required { foreach ($this->attached as $each) { $each->update($this); } } public function attach(SplObserver $observer) // Required { $this->attached[] = $observer; } public function detach(SplObserver $observer) // Required { $key = array_keys($this->attached, $observer); unset($this->attached[$key[0]]); } public function setObserverMethod($method, $args = array()) { $this->observerMethod = $method; $this->observerArgs = $args; return $this; } public function getObserverMethod() { return $this->observerMethod; } public function getObserverArgs() { return $this->observerArgs; } public function getAttached() { return $this->attached; } } $db_subj = new DbSubject; $db_obs = new DbObserver; $db_subj->attach($db_obs); $args = array('A'); $db_subj->setObserverMethod('a', $args)->notify(); $args = array('B', 'I can'); $db_subj->setObserverMethod('b', $args)->notify(); $obsvr = new ObserverObserver; $db_subj->attach($obsvr); $db_subj->setObserverMethod('c')->notify(); $db_subj->detach($db_obs); $db_subj->notify(); /** I run query A I run query B because I can I have no argument I saw that Nothing happened **/