Autor Zpráva
sjiamnocna
Profil
Ahoj. Mám třídu, do které mám načtenou textovou proměnnou (podobně tomuto $var='text ext xt t'), a poté ji dělím $v=explode(' ',$var);, ale potřebuji poté volat funkce s těmito názvy (z toho rozdělení= text(); ext(); xt(); t(); tzn musí to fungovat nějak takto: foreach($v as $fc){classname($fc)->$fc();} ), které jsou vložené před druhou třídu:

class textual extends base{
public function text(){}
public function ext(){}
public function xt(){}
public function t(){}
}
něco jako toto, ale v různých třídách:
class A{
public function A(){
$str='bla la b';
$v=explode(' ',$str);
foreach($v as $fc){
$this->$fc();
}

}

public function bla(){
echo'bla ';
}
public function la(){
echo'la ';
}
public function b(){
echo'b';
}

}
new A;
kdyby to bylo všechno jedna třída, je to jednoduché, ale není. Napadlo mně, kdybych vytvořil další třídu
potřebuju to udělat tak, aby se mohly zadávat do proměnné funkce, které si někdo doplní novou třídou extended. Je to možné, nebo je to celé totální nesmysl?
Díky
Virtus
Profil
Zdravím,
Prvně něco k tomuhle:
sjiamnocna:
foreach($v as $fc){classname($fc)->$fc();}

ukázka 1:
<?php
class A{
  public function a(){
    echo 'něco';
  }
}
$c = new A(); //output 1: něco
$c->a(); //output 2: něco
ukázka 2:
<?php
//output 1:PHP Strict standards:  Redefining already defined constructor for class A in file.php on line 7
class A{
  public function a(){
    echo 'něco';
  }
  public function __construct(){}
}
$c = new A();
$c->a(); //output 2: něco
ukázka 3:
<?php
class A{
  public function __construct(){}
  public function a(){
    echo 'něco';
  }
}
$c = new A();
$c->a() //output 1: něco
ukázka 4:
Samotné volání s proměnnýma potom:
  <?php
class A{
  public function a(){
    echo 'něco';
  }
}

$b = 'a';

$c = new $b();//output 1: něco
$c->$b(); //output 2: něco
ukázka 5:
A tady, pokud jsem pochopil správně tak hledáš asi něco takového:
<?php
abstract class Base
{
  public function __construct($var){
    $this->$var()
  }
}
class A extends Base
{
  public function a(){
    echo 'něco';
  }
}
class B extends Base
{
  public function a(){
    echo 'něco 2';
  }
}

$var = 'a';
new A($var); //output: něco
new B($var); //output: něco 2
sjiamnocna
Profil
:)
Můžeš mi, prosím, vysvětlit, která část patří k čemu? Nějak jsem to nepochopil. Díky
Virtus
Profil
No ty první tři ukázky jenom ukazují různé chování třídy v případě použití metody se stejným názvem jako má třída samotná a je potřeba si nato dávat pozor, obvzlášť pokud na daném kódu/projektu pracuje více lidí, mohou tak potom vznikat špatně dohledatelné chyby, tuším že to je kvůli zpětné kompatibilitě s PHP5.3.[0-2], a sám jsem jí tam udělal v té páté ukázce u class A se metoda __construct() z abstract class Base přepíše metodou a() z class A, jinými slovy pokud zavolám třídu A nevikoná se kód $this->$var() jako u volání class B. Čtvrtá ukázka jenom ukazuje že lze volat dinamicky třídu (Namespaces and dynamic language features) a pátá ukázka by měla být zjednodušená odpověď na tvojí otázku. Pro lepší pochopení jak sem to myslel a jak pochopil teda tvojí otázku to ratši rozeberu po bodech aby nevznikali nějaké nedorozumění:
1) Pochopil jsem to tak, že tedy potřebuješ nějakou základní třídu (v ukázce abstract class Base), kterou budou používat další členové programátorského týmu
2) Jako další člen týmu si tedy vytvořím vlastní třídu(class A/B) která bude extendovat tu tvojí základní ( extends Base)
3) V této své třídě si vytvořím vlastní metodu podle nějakého stringu ($var = 'a';) a tato metoda se bude automaticky po zavolání mojí třídy (A/B) volat

Ještě mně napadá že ta poslední ukázka by se dala zapsat třeba takto:
<?php
abstract class Base
{
  protected $var = 'error';
  public function __construct(){
    $this->{$this->var}();
  }
  private function error(){
    echo 'neni nadefinována metoda pro řídu: ' . get_called_class();
  }
}
class A extends Base
{
  protected $var = 'metodaA';
  public function metodaA(){
    echo 'metodaA';
  }
}
class B extends Base
{
  public function metodaB(){
    echo 'metodaB';
  }
}
 
new A(); //output: metodaA
new B(); //output: neni nadefinována metoda pro třídu: B

Edit:
tady ještě dodávám kód pro ověření toho že se u volání class A v páté ukázce z [#2] opravdu nevikonává $this->$var():
<?php
//file.php
abstract class Base
{
  public final function __construct($var){
    $this->$var();
  }
}
class A extends Base
{
  public function a(){
    echo 'něco';
  }
}
class B extends Base
{
  public function a(){
    echo 'něco 2';
  }
}
 
$var = 'a';
new A($var);
new B($var);

$>php file.php
PHP Fatal error:  Cannot override final Base::__construct() with A::a() in file.php on line 14
sjiamnocna
Profil
No, trochu jsem si hrál, a podařilo se mi dosáhnout vícecméně něčeho podobného, ALE funguje pouze pro jednu extendovanou třídu. Co jsem udělal špatně? Díky

<?php
abstract class Base
{
  protected $var;
  
  public function __construct(){
    $this->var = 'error';
  }
  public function gentext($fcname){
    $methods=explode(' ',$fcname);
    
    foreach($methods as $method){
      $this->$method();
      }
  }
  
  public function get_extends_classes($base){
  foreach(get_declared_classes() as $class)
        if(is_subclass_of($class,$base))$extclasses[]=$class;
   return $extclasses;
  }
  
  public function get_extends_methods($classname){
  if(is_array($classname))foreach($classname as $name){
    $methodsnames[$name]=get_class_methods($name);
  }elseif(!is_array($classname)){
    $methodsnames=get_class_methods($classname);
  }else return false;
  return $methodsnames;
}
  
  private function error(){
    echo 'neni nadefinována metoda pro řídu: ' . get_called_class();
  }
}

class A extends Base
{
  public function metodaA($desc=false){
    if($desc==true)return array('title'=>'whoami','desc'=>'about');
    echo 'metodaA';
  }
  public function metodaB($desc=false){
    if($desc==true)return array('title'=>'whoami','desc'=>'about');
    echo 'metodaB';
  }
}

class B extends Base
{
  public function findme($desc=false){
    if($desc==true)return array('title'=>'whoami','desc'=>'about');
    echo'Findme :)';
  }
}
 
$fc=new A; //output: metodaA
var_dump($fc->get_extends_methods($fc->get_extends_classes('Base')));
/* array(2) { ["A"]=> array(7) { [0]=> string(7) "metodaA" [1]=> string(7) "metodaB" [2]=> string(11) "__construct" [3]=> string(7) "gentext" [4]=> string(19) "get_extends_classes" [5]=> string(19) "get_extends_methods" [6]=> string(5) "error" } ["B"]=> array(6) { [0]=> string(6) "findme" [1]=> string(11) "__construct" [2]=> string(7) "gentext" [3]=> string(19) "get_extends_classes" [4]=> string(19) "get_extends_methods" [5]=> string(5) "error" } } */

$fc->gentext('metodaA metodaB'); // $fc->gentext('metodaA metodaB findme');
/* FmetodaAmetodaB Fatal error: Call to undefined method A::findme() in /web/com/140610143514668/main.php on line 13 */
sjiamnocna
Profil
Zdravím přátelé :)
Asi jsem správně nepochopil, jak abstraktní třídy fungují, zřejmě můžu používat pouze extendovanou třídu, kterou vyvolám new B(); - abstract class volat nemůžu. Máte někdo nějaké doporučení, jak bych se mohl dostat z abstract class i k ostatním metodám extendů? Díky?

Co si myslíte o tomto:
http://cdmckay.org/blog/2010/04/23/adding-extension-methods-to-php/
?
Virtus
Profil
Zdravím,
prvně něco k těm abstract class, co to vlastně je a k čemu to slouží, napadá mně pěkný vysvětlení z reálnýho života. Představ si že máš elektrickou energii, víš k čemu to slouží, ale nemůžeš si na ni sáhnout, nevíš jak vypadá, jak je velká, atd., řekněme tedy že energie je abstraktní. Abychom teda mohli využít energii potřebujem nějaké rozhraní. Vytvořím tedy baterii, na tu si můžu šáhnout, můžu jí změři a zjistit že má třeba 6 voltů a označit tuto hodnotu za velkost energie, můžu tedy označit baterii za potomka energie. Nebo můžu vytvořit elektrickou zásuvku, opět si na ní můžu sáhnout a změřit, přičemž zjistím že zásuvka má velikost 230 voltů, mám tedy druhého potomka energie. Obojí (baterie a elektrická zásuvka) vypadají úplně jinak, mají úplně jinou velikost, ale přesto obojí mi dává energii. Na druhou stranu pokud bych těmto potomkům odstranil (abstraktní) energii, přestali by potomci plnit svojí funkčnost a byli by zbyteční. A úplně stejně to funguje v programování, abstrakce ti zajišťuje jenom jakýsi základ, který je vpodstatě sám o sobě nepoužitelný dokud není zděděn a doplněn o soubor dalších informací/funkčností. Toto má jednu velkou výhodu: šetří to čas (podle mně největší výhoda): upravme předchozí příklad, místo abych vytvořil použitelného potomka (batterii o 6 voltech), vytvoříme abstraktní baterii o 6 voltech. Co o této baterii víme: dětí energii a obsahuje invormaci že energie má velikost 6 voltů. Informace sami o sobě celkem k ničemu, nevím třeba jaké má tato baterie rozměry, jestli je kulatá, hranatá. Tak si teda vytvořím dva potomky: Jedna baterie bude kulatá a bude mít 6 voltů a druhá baterie bude hranatá a bude mít 6 voltů. Tak teď tyto dvě baterie vezmu a každou použiju do jiných hodinek. Nějakou dobu je tedy používám a najednou výrobce změní hodinky, místo aby se používali baterie o velikosti energie 6 voltů, začne vyrábět hodinky, které potřebují velikost energie jenom 3 volty. A tady se dostáváme k silné stránce abstraktních class/věcí. Já totiž v tento moment nemusím upravovat mojí kulatou a hranatou baterii, mně stačí když upravím mojí abstraktní baterii, protože od mojí abstraktní baterie kulatá a hranatá baterii dědí informaci, že velikost energie je 6 voltů.
Dále je důležité si uvědomit, a to jak u abstraktní baterie tak energie, že žádná z těchto abstraktních věcí neví otom kdo jí kde dědí/používá. Nenapadá mně nic z reálnýho světa kde by abstraktní věc věděla otom, jaký potomek ji používá. V programování to možné sice je, ale pak to smrdí antipatternem circular dependency (viz.: zdrojak.cz). Ještě zmíním jednu nevýhodu, občas je opravdu těžké určit co má být a co nemá být abstraktní. (Ps.:jde jenom o pokus vysvětlení principu abstrakce a tohle mi přišlo jako pěkný příklad, a přehlédněte prosím "velikost enegrie = X voltů". Jinak ve skutečném světe je tak vysoká úroveň abstrakce, že jej naprogramovat (v současné době) "nelze";)

Tak a teď k dalšímu problému, už chápu co potřebuješ a vpodstatě jsi se dostal i k tomu jak to vyřešit:
var_dump($fc->ge...
{ ["A"]=> array(7) { [0]=> string(7) "metodaA" [1]=> stri...
V podstatě potřebuješ nějaký registrační prvek, ve kterém budou uloženy informace otom, jaké máš třídy a jaké obsahují metody. Přesně jako je výstup funkce var_dump() v [#5], nemusí to být pole, může to být třeba xml soubor, nějaká třída, databáze atd., a tento registr si potom předáš do "hlavní" třídy, v našem případě tedy class Base (už né abstraktní). Nyní tedy máme seznam všechn dostupných tříd a metod co obsahují a už stačí jenom zavolat $fc->gentext('metodaA metodaB'); a implementovat funkčnost, která v registru najde dané metody a v jakých třídách se nacházejí. Zde ti ovšem může nasta problém a to sice, že můžeš najít dvě a více metod se stejným názvem, ale každá v jiné třídě, to jen taková poznámka na okraj (implementace je na tobě). Snad ti tohle pomůže :)
sjiamnocna
Profil
To znamená, že mám zrušit to abstract, a potom jen získat názvy ext tříd a jejich metod, které potom budu používat pro spouštění?

class Base{
public function __costruct(){

}
}

class A extends Base{
public function __costruct(){

}
}

class B extends Base{
public function __costruct(){

}
}
//...

Je nějaká možnost, jak vyvolat metodu třídy, aniž bych vždycky vytvářel novou instanci (new X;) abych nemusel dělat foreach($classes)$$class=new $class(); $$class->$method[$i]();?
-Nejlépe kompatibilně s nejnovějším PHP

Díky za perfektní příklad, snad už jsem to částečně pochopil. K těm antipatternům: Můj kód by asi profíkovi hodně smrděl, možná by museli odklidit jeho mrtvé tělo, kdo ví :D :)
Tori
Profil
sjiamnocna:
Pokud třída slouží jen jako obal na několik samostatných a vzájemně nezávislých funkcí, tak můžete klidně použít statické metody. Nebo namespace.
K čemu to celé slouží? Možná by se to dalo udělat i jinak (místo napevno definovaných metod používat magické __get a __call, a místo dědění jen vložit do proměnné třídy nějaký blok dat, z něhož se bude číst.
sjiamnocna
Profil
No, snažím se dosáhnout něčeho podobného jako např. WP_widget ve Wordpressu, tedy že vývojáři mohou přidat vlastní widgety. V tomto případě jim však stačilo pár pevně daných názvů metod, které si individuálně spouštěli, když je potřebovali (nebo tak to vidím já :) ) + konstruktor, který nese jen informace - popis, název, krátké info. Já bych chtěl mít variabilně použitelné metody (tedy autor pojmenuje metody podle sebe) a poté si uživatel zvolí, co chce zobrazovat (tipnu, třeba jména účastníků a věk) a ve struktuře proměnné bude mít funkce ($info='ucastnik vek') ze třídy ucastnik: public function ucastnik; public function vek;

Ehm, co dělají statické metody, __get a __set nebo namespace?

Teď mě napadá, tak jak ukazoval[#2] Virtus použít abstract třídu a více private $var; [#4] Virtus, které by mohly nést nějaké užitečné informace, jako informace, co která metoda dělá nebo tak něco. V tom případě bych asi řešil pro každou funkci (+-s nadhledem :) widget) vlastní třídu

Celé je to jen můj pokus o seznaméní se třídami a fungováním extendů. Pokud by se mi něco takového povedlo,možná bych se pustil do něčeho jako předělávání mých předchozích výtvorů, ale kdo ví... :)
Tori
Profil
Zjednodušeně:
Namespace (česky asi „jmenný prostor“ nebo „prostor jmen“) je způsob, jak zabránit kolizi názvů, když se dvě různé třídy jmenují náhodou stejně. Pro ilustraci si představte, že máte dvě verze nějaké písničky, s různými zpěváky: buď můžou být v jednom adresáři a mít odlišné názvy souboru, anebo se ty soubory jmenují stejně ale musí být v různých adresářích. Podobně nemůžou být dvě třídy se stejným názvem v jednom namespace. Dokonce i zápis vypadá podobně jako cesta k souboru:
<?php
$datum = new DateTime(); // tohle je normální PHP třída DateTime
$datum2 = new Sjian\DateTime(); // tohle je nějaká vaše vlastní třída, která se jmenuje stejně, ale je v namespace "Sjian"

Statické metody jsou takové, které nepotřebují konkrétní instanci ani její data, ale jsou společné pro celou třídu. Např.
class User
{
    public static function generateRandomPassword($length = 8)
    {
        // ... funkce vygeneruje náhodný alfanum. řetězec zadané délky
    }
}
Anebo další možnost je, že celá třída obsahuje pouze statické metody, které jsou navzájem nezávislé a mají společné třeba jen to, že se týkají různého zpracování řetězců, jako třída Nette\Utils\Strings.
Alphard
Profil
Odpovídám v podstatě na základě [#1] a [#10], zbytek se mi nezdá přínosné číst.

Problém chápu jako zapouzdření N na sobě nezávislých tříd do jedné obslužné. U té obslužné třídy si představuji nějaké následující rozhraní:
$ws = new WidgetService;
// ...
$ws->call('user', 'age');
přitom očekáváne, že metoda call() interně zavolá
$user->age();

Není řečeno, jak se definují parametry předáváné konstruktorům (nějaká konfigurace?), ale pro implementaci stačí mít interní registr ve formě asociativního pole.

V tomto modelu, kdy požadujeme nezávislost jednotlivých třídy, nevidím místo pro dědičnost.
Kdyby jednotlivé třídy definovaly stejné vnější rozhraní (stejně pojmenované metody se stejnými parametry) mluvili bychom o polymorfismu. Tento případ zde myslím nenastává.
sjiamnocna
Profil
Ještě něco: Je možné zpětně ovlivnit obsah nějaké metody, nebo proměnné - myslím, aby se zpracoval v předešle vložené metodě? Myslím něco jako add_filter ve Wordpressu? Nebo možná, kdybych všechny funkce zapsal před hlavní třídou, daly by se nějak takto zpracovat...?

Takové řešení mě nenapadlo a asi by bylo možná i nejlepší: nadefinovat proměnnou (nebo metodu), do které by se každá metoda manuálně zapisovala i s názvem třídy, které by se poté volaly. Nemusel bych řešit všechny extend třídy ani názvy jejich metod.
Tori
Profil
sjiamnocna:
Je možné zpětně ovlivnit obsah nějaké metody, nebo proměnné - myslím, aby se zpracoval v předešle vložené metodě?
Wordpress neznám, ale obecně by šlo nevolat hned funkci, ale uložit si její název, a uložit data která se mají upravit. Až někde na konci při zobrazování obsahu stránky by se postupně zavolaly všechny nastavené funkce. Do té doby by bylo možné seznam uložených funkcí libovolně upravovat.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: