Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

PHP5.3: functie als argument in een andere functie

Pagina: 1
Acties:

Onderwerpen


  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Ik was vandaag even aan het experimenteren met php:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function a() {
    echo "I'm a!" . PHP_EOL;
}

function b() {
    echo "I'm b!" . PHP_EOL;
}

function c($a) {
    $a();
}

class obj {
    private $id;

    public function  __construct($x) {
        $this->id = $x;
    }

    public function Bar() {
        echo "I'm " . $this->id . "!" . PHP_EOL;
    }
}

$foo = a;
c($foo);

$foo = b;
c($foo);

$obj = new obj("x");
$foo = $obj->Bar;

c($foo);


Ik had verwacht dat hieruit de volgende output zou komen:
code:
1
2
3
I'm a!
I'm b!
I'm x!


in plaats daarvan krijg ik de volgende output:
code:
1
2
3
I'm a!
I'm b!
Fatal error: Function name must be a string


Ligt het aan mij dat ik dit raar vind? Ik weet dat PHP het mogelijk maakt om tijdens de uitvoer willekeurige properties aan een object te hangen: als ik in de code $foo->blaa = 42; toevoeg levert dat niet eens een notice op (iets wat ik altijd al discutabel heb gevonden). Ik begrijp dat dit een issue is mbt backward compatibility. Maar dan nog, conceptueel gezien zou het niet uit mogen maken of ik een losstaande functie gebruik of een functie uit een object IMHO

Maak ik een denkfout? Is er een eenvoudige, doch elegante manier om dit op te lossen? Ik ben op de hoogte van call_user_func, maar dat kan ik moeilijk elegant noemen...

  • kluyze
  • Registratie: Augustus 2004
  • Niet online
Op regel 25 gaat je script al de mist in, alleen gaat die geen constante vinden met die naam, dus neemt die aan dat dat een string 'a' is. Indien je de error reporting wat hoger zet, zal je zien dat die daar ook over valt.

$a() kan dus alleen als $a een string is, in de eerste gevallen kan php er een string van maken, in het laatste niet en wordt er dus een fatal error getriggerd ipv een warning.

Edit:
Het is een Notice
code:
1
2
Notice: Use of undefined constant a - assumed 'a' in C:\Program Files (x86)\Apache Software Foundation\Apache2.2\htdocs\test\error.php on line 25
I'm a!

[ Voor 19% gewijzigd door kluyze op 17-02-2011 21:40 ]


  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
kluyze schreef op donderdag 17 februari 2011 @ 21:34:
Op regel 25 gaat je script al de mist in, alleen gaat die geen constante vinden met die naam, dus neemt die aan dat dat een string 'a' is. Indien je de error reporting wat hoger zet, zal je zien dat die daar ook over valt.

$a() kan dus alleen als $a een string is, in de eerste gevallen kan php er een string van maken, in het laatste niet en wordt er dus een fatal error getriggerd ipv een warning.
ow dammit, dus in functie c() gebeurt er dan het volgende:
PHP:
1
2
3
function c() {
"a"();
}


Als je het zo zegt is het logisch ja. (ik had zelfs de error reporting op e_all gezet om te bevestigen dat $foo->blaa = 42 geen notice oplevert en gezien dat er notices op die andere regels waren gekomen, zonder me te realizeren dat deze notices ervoor zorgden dat het werkte :+ $bar->$ietsanders() is me zeker niet onbekend ;) ) Jammer, had gehoopt dat met de toevoeging van closures functies nu ook first class waren maar dat is dus niet het geval!

edit:
Een paar jaar geleden had ik nooit gedacht dat ik dit zou zeggen, maar javascript heeft me verwend!

[ Voor 5% gewijzigd door st0p op 17-02-2011 21:42 ]


  • NMe
  • Registratie: Februari 2004
  • Laatst online: 20-11 11:59

NMe

Quia Ego Sic Dico.

st0p schreef op donderdag 17 februari 2011 @ 21:39:
[...]

ow dammit, dus in functie c() gebeurt er dan het volgende:
PHP:
1
2
3
function c() {
"a"();
}
Nee. Hij roept gewoon een functie aan met de naam die jij opgeeft. Maar die naam verwacht hij wel als string. a is geen string, a is een constante. PHP maakt daar impliciet "a" van en gooit een notice. Van $foo->bar kan hij geen string maken.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
code:
1
2
3
$foo = array($obj, 'Bar'); 

c($foo);


php 5.3 functionaliteit:

code:
1
2
3
4
<?php
c(function() {
    echo "I don't have a name!";
});

  • mithras
  • Registratie: Maart 2003
  • Niet online
Misschien moet je je eerst even inlezen in het principe closures van php5.3. Dit zal wel kunnen werken (niet getest):

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$a = function() { 
    echo "I'm a!" . PHP_EOL; 
} 

$b = function() { 
    echo "I'm b!" . PHP_EOL; 
} 

function c($a) { 
    $a(); 
}

class obj { 
    private $id;
    private $closure;

    public function  __construct($x) { 
        $this->id = $x;
        $this->closure = function() {
            echo "I'm " . $this->id . "!" . PHP_EOL;
        }
    } 

    public function Bar() { 
         return $this->closure;
    } 
} 

c($a);
c($b);

$obj = new obj('x');
c($obj->Bar());


Het enige dat ik hierbij niet weet is of in de closure op regel 20 de variabelen $this->id wel bekend is :)

Verwijderd

Door reflection te gebruiken zou je ook een functie als argument mee kunnen geven

PHP:
1
2
3
4
5
6
7
8
9
10
function invokeTest($func)
{
    $func->invoke(null);
}


$ref = new ReflectionClass('Blaat');
$func = $oRef->getMethod('schaap');

invokeTest($func);


@Mithras:

Fatal error: Using $this when not in object context ... die $this wordt dus binnen de scope van de closure gezien en zal dus niet werken.

Ik moet wel zeggen dat ik closures erg smerig vind en nog geen solide reden gevonden heb om ze te gebruiken, anders dan 'omdat het kan'. Ik probeer sowiezo het gebruik van functies te vermijden en zal og eerder een static gebruiken als een soort van functie container met namespace dan losse functies gebruiken.

[ Voor 46% gewijzigd door Verwijderd op 18-02-2011 10:15 ]


  • Peter
  • Registratie: Januari 2005
  • Laatst online: 21-11 22:36
Een oplossing zou kunnen zijn de __invoke magic-method gebruiken:

PHP:
1
2
3
4
5
6
7
8
9
10
<?php
class Foo {
    public function __invoke ()
    {
        echo 'c';
    }
}

$instance = new Foo;
$instance ();


Dat laat keurig "c" zien. Alternatief is de closure-oplossing van mithras prima.

[ Voor 22% gewijzigd door Peter op 18-02-2011 10:43 ]


  • RayNbow
  • Registratie: Maart 2003
  • Nu online

RayNbow

Kirika <3

Verwijderd schreef op vrijdag 18 februari 2011 @ 10:07:
Ik moet wel zeggen dat ik closures erg smerig vind en nog geen solide reden gevonden heb om ze te gebruiken, anders dan 'omdat het kan'.
Hier is een reden: het kan leiden tot duidelijkere code (in talen waarin closures fatsoenlijk ondersteund worden). ;)

Python:
1
2
3
4
5
def divisibleBy(d):
    return lambda n: n % d == 0

# example:
print filter(divisibleBy(3), range(10))

Ipsa Scientia Potestas Est
NNID: ShinNoNoir


  • mithras
  • Registratie: Maart 2003
  • Niet online
Verwijderd schreef op vrijdag 18 februari 2011 @ 10:07:
@Mithras:

Fatal error: Using $this when not in object context ... die $this wordt dus binnen de scope van de closure gezien en zal dus niet werken.
Naar was ik al een beetje bang voor (zie sidenote) gezien de closure in een Closure klasse verpakt zit.
Ik moet wel zeggen dat ik closures erg smerig vind en nog geen solide reden gevonden heb om ze te gebruiken, anders dan 'omdat het kan'. Ik probeer sowiezo het gebruik van functies te vermijden en zal og eerder een static gebruiken als een soort van functie container met namespace dan losse functies gebruiken.
Juist met callbacks zijn closures ideaal. Namespace pollution is enorm wanneer je telkens een functie onder een naam moet definieren. Nu hoeft dat niet en blijft je code veel schoner.

Zie bijvoorbeeld hoe javascript het gebruikt, imho ideaal :)

Verwijderd

@RayNbow

Dat zou inderdaad best een aardige implementatie zijn ... in PHP zou deze zijn :

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function filterDividableBy($b)
{
    return function($n) use ($b){
        return  $n % $b  ;
    };
}

function filter($func, $nums)
{
    foreach($nums as $i => $num)
    {
        if(!$func($num))
        {
            unset($nums[$i]);
        }
    }
    return $nums;
}


print_r( filter(filterDividableBy(2), range(0, 10) ));


Ik zie ergens de voorwaarden wel maar binnen een OO oplossing zou dit net zo goed kunnen door die mod als param mee te geven. In PHP gaan nog maar weinig standaard functies er van uit dat je een closure als param meegeeft dus zul je het allemaal zelf moeten schrijven zoals de filter functie hierboven en dan zou ik toch voor een andere oplossing gaan.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class DivideByFilter
{
  protected $_mod;
  function __construct($mod)
  {
    $this->_mod = $mod;
  }

  function filter($val)
  { 
    return $val % $this->_mod;
  }
}

class Filter
{
  function filterValues($filter, $values)
  {
    foreach($values as $i => $value)
    {
      if( !$filter->filter($value) )
     {
       unset($values[$i]);
     }
    }
    return $values;
    
  }
}

$oFilter = new Filter();
print_r( $oFilter->filterValues( new DivideByFilter(2), range(0, 10) ) );


kwestie van smaak denk ik .. en inderdaad van ondersteuning, zo snel als meer functies het gebruik van closures als params zullen gaan ondersteunen dan zal er meer voor het gebruik te zeggen zijn.

@Mithras ... voor callbacks is het inderdaad ideaal om niet constant functies te hoeven definieren. Maar het enige wat mij er altijd stoort is dat er totaal geen mogelijkheid is om het te herbruiken , in het bovenstaande voorbeeld heb je dan weer wel een soort closure-factory patroon , dus een named functie die een closure maakt.

[ Voor 11% gewijzigd door Verwijderd op 18-02-2011 14:11 ]


  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
Deze is trouwens ook leuk en nieuw:
code:
1
2
3
4
5
6
7
8
9
10
11
12
<?php
class A
{
    function __invoke()
    {
         echo 'I am a class acting as function...';
    }
}

$a = new A();

$a();


[edit] Oops was al gezegt...

[ Voor 8% gewijzigd door ReenL op 18-02-2011 19:45 ]


Verwijderd

Verwijderd schreef op vrijdag 18 februari 2011 @ 13:49:
@RayNbow

Dat zou inderdaad best een aardige implementatie zijn ... in PHP zou deze zijn :

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function filterDividableBy($b)
{
    return function($n) use ($b){
        return  $n % $b  ;
    };
}

function filter($func, $nums)
{
    foreach($nums as $i => $num)
    {
        if(!$func($num))
        {
            unset($nums[$i]);
        }
    }
    return $nums;
}


print_r( filter(filterDividableBy(2), range(0, 10) ));


Ik zie ergens de voorwaarden wel maar binnen een OO oplossing zou dit net zo goed kunnen door die mod als param mee te geven. In PHP gaan nog maar weinig standaard functies er van uit dat je een closure als param meegeeft dus zul je het allemaal zelf moeten schrijven zoals de filter functie hierboven en dan zou ik toch voor een andere oplossing gaan.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class DivideByFilter
{
  protected $_mod;
  function __construct($mod)
  {
    $this->_mod = $mod;
  }

  function filter($val)
  { 
    return $val % $this->_mod;
  }
}

class Filter
{
  function filterValues($filter, $values)
  {
    foreach($values as $i => $value)
    {
      if( !$filter->filter($value) )
     {
       unset($values[$i]);
     }
    }
    return $values;
    
  }
}

$oFilter = new Filter();
print_r( $oFilter->filterValues( new DivideByFilter(2), range(0, 10) ) );


kwestie van smaak denk ik .. en inderdaad van ondersteuning, zo snel als meer functies het gebruik van closures als params zullen gaan ondersteunen dan zal er meer voor het gebruik te zeggen zijn.

@Mithras ... voor callbacks is het inderdaad ideaal om niet constant functies te hoeven definieren. Maar het enige wat mij er altijd stoort is dat er totaal geen mogelijkheid is om het te herbruiken , in het bovenstaande voorbeeld heb je dan weer wel een soort closure-factory patroon , dus een named functie die een closure maakt.
Of zou kunnen doen:
PHP:
1
2
3
4
5
6
7
8
function filterDividableBy($b)
{
    return function($n) use ($b){
        return  $n % $b  === 0;
    };
}

print_r(array_filter(range(0, 10), filterDividableBy(3)));

iets simpeler. ;)

Verwijderd

Inderdaad iets korter die laatste ...

Nog een leuke al dan niet ietwat smerige toepassing voor lamba's

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class Foo
{
    public $number = 823;
    public function __call($method, $args)
    {
        if(is_callable(array($this, $method))) {
            return call_user_func_array($this->$method, $args);
        }
        // else throw exception
    }
}

$foo->doStuff = function($who) use ($foo) {

    $foo->number = 283832;
    return  'who is the fool ... '.$who. 'is the fool that\'s who ';

};

echo $foo->doStuff('ThiOz');
Pagina: 1