Brian
Brian

Reputation: 3958

Best DRY Method for PHP methods 2 levels deep in a Class?

I am working inside of a large class that is one of the backbones of an application, we'll call this myClass. I need to create a new public method of this class and have it accept a variable array that calls out to the individual methods of the function I'm writing: my_new_iterative_function()... In a sense, I need to have a "class within a class" (javascript is nice for this ;) ... how can I achieve the following, where I iterate over the $fallback_order parameter array to change the order these methods are called and then have a default order, which is executed if $fallback_order is false as defined in the default paramater?

class myClass {
    public function existing_app_method_1() {
        // other code
    }
    public function existing_app_method_2() {
        // other code
    }
    // large app-centric class with many more methods

    public function my_new_iterative_function( array $fallback_order = false ) {
        method_foo() {
            // do stuff
        }
        method_bar() {
            // do stuff
        }
        method_more_methods() {
            // there are at least 5 of them in total
        }

        if ( $fallbackorder == false ):
            method_foo();
            method_bar();
        else:
            foreach ( $fallback_order as $index => $this_fallback ) {
                $name = $this_fallback;
                if ( $this_fallback == $name ):
                    $this->$name();
                endif;
            }
        endif;
    }
}

The goal would be the following in the view templates:

<div><?php $myClass->my_new_iterative_function( array( 'method_more_methods', 'method_bar', 'method_foo', ) ); ?></div>

EDIT: Fixed some obviously faulty logic

Upvotes: 1

Views: 228

Answers (2)

complex857
complex857

Reputation: 20753

You could have your methods as anonymous functions in an array inside your method and index those arrays with the input, something like this:

class Foo {
    private $stuff = 'tickle me elmo';        

    public function my_new_iterative_function(array $fallback_order = array('method_foo', 'method_bar')) {

        $self = $this; // this acts as a keyword, you can't use in the `use ()` part

        $order_functions = array(
            'method_foo' => function() use ($self) {
                print "im in foo: {$self->stuff}<br>";
            },
            'method_bar' => function(){
                print "im in bar<br>";
            },
        );

        foreach ( $fallback_order as $index => $this_fallback ) {
            $order_functions[$this_fallback]();
        }
    }
}
(new Foo)->my_new_iterative_function(); // look, one-line `new` and method call, since php 5.4
(new Foo)->my_new_iterative_function([ 'method_bar', 'method_foo', ]); // and even [] for array literals too!

I've also embeded the default order in the default parameter. (setting it to false when you typehint it as an array is a fatal error)

Upvotes: 3

net.uk.sweet
net.uk.sweet

Reputation: 12431

I realise you already have a good answer which you've accepted, and I should caveat this by saying that I don't really know PHP very well, but thought I might chime in regardless as I've given this a little bit of thought.

I wondered if it might be neater to abstract the logic in the methods you iterate over into separate commands (with the obvious advantages that you can add further methods without touching the iteration code and you'll minimise the amount of additional code you add to your large class). You could then set a default order array as a property of myClass but override it, if required, in the parameter of the my_new_iterative_function method (or, probably better, via a setter on myClass).

Excuse the code, but something like this:

class myClass 
{

    // override default order via class setter if required
    protected $fallback_order = array(new FooCommand(), new BarCommand(), new ANOtherCommand());

    public function my_new_iterative_function(  ) {

            foreach ( $this -> fallback_order as $value ) {
                $value -> execute();
            }
    }
}

Or maybe like this:

class MyClass 
{

    // override default order in method invokation if required
    protected $default_fallback_order = array(new FooCommand(), new BarCommand(), new ANOtherCommand());

    public function my_new_iterative_function( $fallback_order = null ) {

            // Use default fallback order if an override has not been 
            // specified in method parameter
            if (is_null($fallback_order))
                $fallback_order = $this -> default_fallback_order;

            foreach ( $fallback_order as $value ) {
                $value -> execute();
            }
    }
}

$myClass = new MyClass();
$myClass -> my_new_iterative_function(); // use default fallback order
$myClass -> my_new_iterative_function(array(new ANOtherCommand(), new BarCommand(), new FooCommand()));

Upvotes: 1

Related Questions