<?php
/**
 * Zend Framework (http://framework.zend.com/)
 *
 * @link      http://github.com/zendframework/zf2 for the canonical source repository
 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

namespace Zend\Di\ServiceLocator;

use 
Zend\Di\Di;
use 
Zend\Di\Exception;

/**
 * Proxy used to analyze how instances are created by a given Di. Overrides Zend\Di\Di to produce artifacts that
 * represent the process used to instantiate a particular instance
 */
class DependencyInjectorProxy extends Di
{
    
/**
     * @var Di
     */
    
protected $di;

    
/**
     * @param Di $di
     */
    
public function __construct(Di $di)
    {
        
$this->di              $di;
        
$this->definitions     $di->definitions();
        
$this->instanceManager $di->instanceManager();
    }

    
/**
     * {@inheritDoc}
     * @return GeneratorInstance
     */
    
public function get($name, array $params = array())
    {
        return 
parent::get($name$params);
    }

    
/**
     * {@inheritDoc}
     * @return GeneratorInstance
     */
    
public function newInstance($name, array $params = array(), $isShared true)
    {
        
$instance parent::newInstance($name$params$isShared);

        if (
$instance instanceof GeneratorInstance) {
            
/* @var $instance GeneratorInstance */
            
$instance->setShared($isShared);

            
// When a callback is used, we don't know instance the class name.
            // That's why we assume $name as the instance alias
            
if (null === $instance->getName()) {
                
$instance->setAlias($name);
            }
        }

        return 
$instance;
    }

    
/**
     * {@inheritDoc}
     * @return GeneratorInstance
     */
    
public function createInstanceViaConstructor($class$params$alias null)
    {
        
$callParameters = array();

        if (
$this->di->definitions->hasMethod($class'__construct')
            && (
count($this->di->definitions->getMethodParameters($class'__construct')) > 0)
        ) {
            
$callParameters $this->resolveMethodParameters($class'__construct'$params$aliastruetrue);
            
$callParameters $callParameters ?: array();
        }

        return new 
GeneratorInstance($class$alias'__construct'$callParameters);
    }

    
/**
     * {@inheritDoc}
     * @throws \Zend\Di\Exception\InvalidCallbackException
     * @return GeneratorInstance
     */
    
public function createInstanceViaCallback($callback$params$alias)
    {
        if (
is_string($callback)) {
            
$callback explode('::'$callback);
        }

        if (!
is_callable($callback)) {
            throw new 
Exception\InvalidCallbackException('An invalid constructor callback was provided');
        }

        if (!
is_array($callback) || is_object($callback[0])) {
            throw new 
Exception\InvalidCallbackException(
                
'For purposes of service locator generation, constructor callbacks must refer to static methods only'
            
);
        }

        
$class  $callback[0];
        
$method $callback[1];

        
$callParameters = array();
        if (
$this->di->definitions->hasMethod($class$method)) {
            
$callParameters $this->resolveMethodParameters($class$method$params$aliastruetrue);
        }

        
$callParameters $callParameters ?: array();

        return new 
GeneratorInstance(null$alias$callback$callParameters);
    }

    
/**
     * {@inheritDoc}
     */
    
public function handleInjectionMethodForObject($class$method$params$alias$isRequired)
    {
        return array(
            
'method' => $method,
            
'params' =>  $this->resolveMethodParameters($class$method$params$alias$isRequired),
        );
    }

    
/**
     * {@inheritDoc}
     */
    
protected function resolveAndCallInjectionMethodForInstance($instance$method$params$alias$methodIsRequired$methodClass null)
    {
        if (!
$instance instanceof GeneratorInstance) {
            return 
parent::resolveAndCallInjectionMethodForInstance($instance$method$params$alias$methodIsRequired$methodClass);
        }

        
/* @var $instance GeneratorInstance */
        
$methodClass $instance->getClass();
        
$callParameters $this->resolveMethodParameters($methodClass$method$params$alias$methodIsRequired);

        if (
$callParameters !== false) {
            
$instance->addMethod(array(
                
'method' => $method,
                
'params' => $callParameters,
            ));

            return 
true;
        }

        return 
false;
    }

    
/**
     * {@inheritDoc}
     */
    
protected function getClass($instance)
    {
        if (
$instance instanceof GeneratorInstance) {
            
/* @var $instance GeneratorInstance */

            
return $instance->getClass();
        }

        return 
parent::getClass($instance);
    }
}