Bir sınıfın işlevleri geçmek

8 Cevap php

PHP mysql database I have created a follow on question to this one here that is specifically about pagination

Ben başka bir sınıftan bir yöntemini çağırın, ve denir yöntemini değiştirmek gerekiyor. Şöyle

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();

I want to use a method 'a' from the db class in the 'yourClass' method 'invoke' Thanks

Tamam bu i sağlanan ve çalıştığını cevaplardan araya budur.

    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");

Hangi yazar, ekrana 'Yöntemi kullanılır'

Ama gerçekten "" bu yüzden aşağıdaki kodu denedim yönteme argüman geçmek mümkün olmak istiyorum.

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);

Neredeyse çalıştığı ama bu hataları doğru cevabı yukarıda olsun

Missing argument 1 for A::a(),.....for argument 2 and 3 as well but then the answer prints to the screen "the three passed values are apple and banana and pineapple"

I'm probably making a rookie mistake I've been coding all day. If someone could fix the script above and submit the working code, I would be eternally grateful. I have to put this issue to bed so i can go to bed. Thanks

8 Cevap

PHP5.3 itibariyle size closures or functors etrafında yöntemleri geçmek için kullanabilirsiniz. Bundan önce, size create_function() ile bir anonim işlev yazabilirsiniz, ama bu oldukça garip.

Temelde, ne yapmaya çalışıyorsun Strategy Pattern ile yapılabilir.

removed example code, as it wasnt helpful anymore after the OP changed the question (see wiki)

Bunun dışında, size Fowlers yıllardan Data Source Architectural Patterns içine bakmak isteyebilirsiniz. Zend Framework (ve hemen hemen tüm diğer PHP çerçeveler) teklifi database access classes bu desen için kullanabilirsiniz ve bir paginator class da var, neden onları kontrol değil bunu nasıl yaptığını öğrenmek için.

removed EDIT 1 as it wasnt helpful anymore after the OP changed the question (see wiki)

EDIT 2 Ok, let's take a step by step approach to this (not using a Strategy Pattern though)

Ne soru soruyorlar kolayca bu kod ile çözülebilir:

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);
    }
}

Bu kod ile sadece uygun yöntemleri çağırır. Yöntem adları yok ayarı. Hayır beceriksiz ekstra Invoke yöntemi. Yöntemler denir nasıl yok esinlenmiştir. PHP sadece, örneğin, $ myObject tüm yöntemler MyInvoker mevcut değil göndermek için öğretti __ çağrı işlevi vardır, çünkü gerek yok Foo.:

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

Siz sadece de Foo sınıfının bir alt sınıfı olmak MyInvoker genişletilmiş olabilir, örneğin

class MyInvoker extends Foo {}

ve daha sonra aynısını yapabilirdi. Bu olsa gerek ne ve o böyle bir şey yapmak, ne kadar anlamsız göstermektedir. MyInvoker artık tek başına hiçbir şey yapmaz. Bu boş bir sınıf ve etkili Foo gibi aynı. Hatta __ çağrı yöntemini kullanarak önceki yaklaşımla bu şey yapmıyor. Ben bir paginator olduğu istenen sonucu, hakkında daha spesifik olmak istedi nedeni budur.

İlk deneyin:

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);
    }
}

Bu kod ile, bizim paginator sıkıca db bağlantısı sınıfını ve tüm sorguları birleştirilmesi, kendi içinde ihtiyacı olan her şeyi yaratan ve bu çağrıda sağlar, gibi pek

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

Isimli sonra ne oluyor, paginator o (getImageCount bilmiyor tanıyacak) ve __ çağrı yöntemini çağırmak olacaktır. __ Çağrısı yöntemi DbQueries üzerine getImageCount () yöntemini çağırmak için çalışacağız. Mevcutsa bu yana, sırayla çalıştırmak için db bağlantı geçirilen sorgu dönecektir. Büyük sen derdim, ama öyle değil. Aslında, bu korkunç. Sizin paginator sorumluluğu bir tablodaki öğeleri saymak ve belirli bir aralık ve miktarda bu tablodan ürün almak için. Ama şu anda, böyle bir şey yapmıyor. Bu ne oluyor tamamen habersiz olduğunu, bu nedenle yeni bir sınıf deneyelim:

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);
    }
}

Çok daha iyi. Şimdi bizim paginator kendisi içindeki nesneleri örneğini değil yani, yerine kompozit bir toplamıdır, ancak kurucusuna ona geçirilecek bunları gerektirir. Bu değişim kolay olduğu gibi bu app çok daha rahat hale getirecek dependency injection (ve aynı zamanda bir loose coupling sağlayan, dbConn bir interface kullandığında) denir Şimdi bileşenler. Bu da zaman Unit Testing kod kullanışlı gelecektir.

Ayrıca, paginator şimdi bunu yapmak için ne gerekiyor üzerinde yoğunlaşmaktadır: sayma ve keyfi bir sorgu getiriliyor ürün. Etrafında yöntemleri geçmesine gerek yok. Karanlık yöntem çağırma gerek yok. Bu gibi kullanmak istiyorum:

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

Ve işte bu. Evet, hemen hemen. Elbette sayfalandırma işlemek olurdu. Zaten yukarıda ne uzatmak Ama bu, oldukça önemsiz olmalıdır. $ ImageCount özellik sonraki gitmek için nerede bir ipucu.

Neyse, biraz ışık tutabileceğini umuyorum.

P.S. $this->dbConn->query($sql, $query) aramalar ders kukla kod vardır. Kopyalama ve yapıştırma onu ve çalışma elde edebilmek için beklemeyin. Buna ek olarak, emin paginator SQL eklenen sorguları kullanmak güvenli olduğundan emin olunmalıdır. Sen birisi tüm db satırları silen bir sorgu eklemek istemem. Kullanıcı girişi asla güvenme.

P.P.S. $query, bir SQL sorgusu dize olmalıdır. PDO::prepare için PHP kılavuzuna bakın. Genel olarak, onu yürütmeden önce prepare bir açıklamaya daha iyi bir performans ve güvenlik verir. Kılavuzda sayfası, sorgu aramaları ? ile ilgili ipuçları verecektir. Eğer PDO kullanmak istemiyorsanız, sadece sprintf() veya str_replace() olarak değiştirin ? ile kullanmak $query, örneğin $this->dbConn->query(sprintf('SELECT count(*) from (%s)', $query) ama bu bir hazır deyimi faydaları hiçbiri vardır ve potansiyel SQL Injection güvenlik açıkları için kapıyı açar unutmayın.

P.P.P.S Evet Dependency Injection, genellikle tercih edilen bir stratejidir. Bu bile advanved konudur ve tam şu anda kavramak için çok fazla olabilir, ama bunun içine bakarak değer. Eğer kompozisyon üzerinde iyilik toplama lehine çalışırsanız şimdi, bu yeterli olmalı. Sizin sınıflar sadece sorumlu oldukları ne yapmak ve yapıcı aracılığıyla herhangi bir bağımlılıkları almalısınız.

İşte bunu yapmanın iki yolu vardır:

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();

Bu sınıf içinde gelen "Ben () değilim" yazdırmak olacak.

neredeyse var

	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();

sözdizimi oldukça fantezi, ama çalışıyor

/ / Edit: basit seçenek bu gibi dize gibi bir yöntem adı geçmek gibi Yorumlarınız gelen görünüyor

 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');

Burada php kullandığınızı tahmin ediyorum. Php destekler variable functions Sana sorunu çözebilir ama bildiğim kadarıyla benim gibi delegeler / işlev işaretçileri desteklemeyen.

Ne veritabanı kullanıyorsunuz? Ben bu kullandığınız veritabanında destekleniyorsa, kodundaki sorguları koyarak ve bir alternatif olarak saklanan prosedürleri kullanarak karşı olacaktır. Bu sahip yatan sorunu çözebilir.

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' ) );
    }
}

Bu yardımcı olur umarım. Doğru cevap olarak bu kontrol etmeyi unutmayın.

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');

Benim ilk cevap biraz değişiklikler yaptık. Ayrıca bu yazı kontrol edebilir, bu yardımcı olabilir:

http://stackoverflow.com/questions/1716652/database-and-oop-practices-in-php/1716908#1716908

Bu değişikliği yapmak gerekir:

$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);

Eğer yardımcı olur doğru cevap olarak bu kene unutmayın.

Observer Design Pattern bu tür bir şey için yararlı olabilir, ya da desen bir yanlış olabilir; Henüz bilmiyorum. Neyse, senin göz için:

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
**/