Get the Factory in this post as a part of my (free) Zend Framework 3 Auto-Wiring Package! 

The recent march toward ZF3 has signed the blockade that keeps the service locator out of all controllers.  If you've been developing a ZF2 app, you're probably fixing your composer to not install zend-mvc 2.7+.  Pause that version freeze, patch below.

The deprecation of this pivotal auto-service is ratified by an academic mix of "it's an anti-pattern", code rigor and testability - all true.  However rationalized, the real world impact can be a PITA.  If you want to stay up-to-date, you've got to face the music.  I did anyways, in looking at the bevy of controllers I had to deal with if I wanted to keep current.

Anecdotal, I hit IRC quickly to ask "how are you guys dealing with this?".  All I got was "not upgrading yet".

If you've read this far, you're in my shoes -- keep reading!

Here then, is an abstract factory that you can modify to help you get around this zend-mvc change.  All you have to do, is write constructors with the right class names - and this abstract factory will use reflection to inject your dependencies for you.  What's more, some cheat code can sub parts in, for example: if you use "array $config" or other aliases as constructor parameters, it'll get the ZF config and so forth.

The abstract factory relies on the fact that you are using ::class to declare your service manager components.  If you're not using ::class, you should!  e.g.:

'service_manager' => [
    'factories' => [
        SuperService::class => SuperServiceFactory::class,
    ],
],

Here's the abstract factory (tailed to Controllers) that uses reflection to identify dependencies.

class LazyControllerFactory implements AbstractFactoryInterface
{

    /**
     * Determine if we can create a service with name
     *
     * @param ServiceLocatorInterface $serviceLocator
     * @param                         $name
     * @param                         $requestedName
     *
     * @return bool
     */
    public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
    {
        list( $module, ) = explode( '\\', __NAMESPACE__, 2 );
        return strstr( $requestedName, $module . '\Controller') !== false;
    }


    /**
     * These aliases work to substitute class names with SM types that are buried in ZF
     * @var array
     */
    protected $aliases = [
        'Zend\Form\FormElementManager' => 'FormElementManager',
        'Zend\Validator\ValidatorPluginManager' => 'ValidatorManager',
        'Zend\Mvc\I18n\Translator' => 'translator',
    ];

    /**
     * Create service with name
     *
     * @param ServiceLocatorInterface $serviceLocator
     * @param                         $name
     * @param                         $requestedName
     *
     * @return mixed
     */
    public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
    {
        $class = new \ReflectionClass($requestedName);
        $parentLocator = $serviceLocator->getServiceLocator();
        if( $constructor = $class->getConstructor() )
        {
            if( $params = $constructor->getParameters() )
            {
                $parameter_instances = [];
                foreach( $params as $p )
                {

                    if( $p->getClass() ) {
                        $cn = $p->getClass()->getName();
                        if (array_key_exists($cn, $this->aliases)) {
                            $cn = $this->aliases[$cn];
                        }

                        try {
                            $parameter_instances[] = $parentLocator->get($cn);
                        }
                        catch (\Exception $x) {
                            echo __CLASS__
                                . " couldn't create an instance of $cn to satisfy the constructor for $requestedName.";
                            exit;
                        }
                    }
                    else{
                        if( $p->isArray() && $p->getName() == 'config' )
                            $parameter_instances[] = $parentLocator->get('config');
                    }

                }
                return $class->newInstanceArgs($parameter_instances);
            }
        }

        return new $requestedName;

    }
}

Setup

Here's a run down of what your setup would look like in parts:

Abstract Factory | module.config.php

'controllers' => [
    'abstract_factories' => [
        LazyControllerFactory::class,
    ]
],

Route Setup | module.config.php

Your routes have to use class names, or it won't work.

'home' => [
        'type' => 'Literal',
        'options' => [
            'route' => '/',
            'defaults' => [
                'controller' => \Application\Controller\IndexController::class,
                'action' => 'index',
            ],
        ],
    ],

Controller | fix dependencies

Your controller constructor is where the factory discovers dependencies using reflection.  Note, in cases where you have uber complex setup, you should probably stick to a bona-fide factory.  That's ok, ZF will match factories before it does abstract factories.  The lazy factory can be your backup.

public function __construct( FormElementManager $formElementManager, ConfigurationMapper $configurationMapper, array $config )
{
    $this->formElementManager = $formElementManager;
    $this->configurationMapper = $configurationMapper;
    $this->countryConfig = $config['lemonade']['default_country'];
}

After this, it's just a matter of removing any reference to $this->getServiceLocator() in your controller code.  Remove the locator, add the dependency you were locating to the constructor; rinse and repeat.

Good luck with your migration!  I always welcome improvements and feedback!  

I kept this in post-migration.  It lets me prototype just as fast as when I had the SL readily available and makes things a bit less nebulous when comparing tests to controllers/services, etc.