2

I'm using an API that returns different values, and I want to dynamically tell my class to run a function with the same name (so i won't need a huge switch or if/else mess).

  1. How can I declare the object method song.pause?
  2. How can I use the object method song.pause?
  3. This might be an XY Problem, so is there a better way to do this? One alternative i thought of is to always call str_replace('.','_',$type) and set functions without the periods. Thoughts?

Example:

<?php
class MyClass {
    ...
    ...
    $type = "song.pause"; // This value is returned from another API I can't control

    if (property_exists('MyClass', $type)) {
        $success = $this->{$type}(); // ???
    } else {
        header("HTTP/1.1 400 Invalid type: $type);
        exit;
    }


    public function 'song.pause'() { // obviously incorrect :)
          ???
    }
0

3 Answers 3

3

Given a return of song.pause, conceptually song should be the class name and pause should be the method, consider this possibility:

class MyClass {
    protected $classes = array();

    function processResponse($response) {
        // $response example is "song.pause"
        list($class, $method) = explode('.', $response);
        if(!class_exists($class)) {
            // Class doesn't exist
            die("Class name {$class} doesn't exist! Exiting...");
        }

        // Instantiate class
        $this->classes[$class] = new $class();

        if(!method_exists($this->classes[$class], $method)) {
            // Method doesn't exist within specified class
            die("Method name {$method} doesn't exist within {$class}. Exiting...");
        }

        // Call method
        $result = $this->classes[$class]->{$method}();

        return $result;
    }
}

Your logic implementation would be something like this:

class song {
    public function pause() {
        return 'foobar';
    }
}

Here's an example.

Sign up to request clarification or add additional context in comments.

2 Comments

+1 but what's the point of keeping a copy of the object inside of $this->classes (especially since it gets overwritten)?
Purely for not needing to define a property for each child class name - keep them in an array instead. No difference between that and $this->{$class} if you want to define it for each possible child.
2

Unfortunately, what you're generally asking is not supported. From the manual:

Function names follow the same rules as other labels in PHP. A valid function name starts with a letter or underscore, followed by any number of letters, numbers, or underscores. As a regular expression, it would be expressed thus: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*.

This also applies to class methods.

As the walkaround, you may follow the way that you suggested yourself:

$type = 'song.pause';
$type = str_replace('.', '_', $type);
$this->{$type}(); // will call song_pause()

OR use "dark" magic:

<?php
// header('Content-Type: text/plain; charset=utf-8');

class Test {
    function __call($method, $args){
        // do redirect to proper processing method here
        print_r($method);
        echo PHP_EOL;
        print_r($args);
    }
}

$x = new Test();
$x->{'song.pause'}(1,2,3);
?>

Shows:

song.pause           // < the method
Array                // < arguments
(
    [0] => 1
    [1] => 2
    [2] => 3
)

However, the "long" and really transparent way, which I completely agree with, is suggested by @scrowler.

Comments

2

@HAL9000 is right: what you want is not supported. One potential workaround is:

Define the handlers:

$typeHandlers = array();
$typeHandlers['song.pause'] = function () {
    echo 'Pause!'; // or whatever...
};

Call the appropriate handler:

$typeHandlers[$type]();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.