<?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\Code\Reflection;

use 
Zend\Code\Scanner\CachingFileScanner;

class 
FileReflection implements ReflectionInterface
{
    
/**
     * @var string
     */
    
protected $filePath null;

    
/**
     * @var string
     */
    
protected $docComment null;

    
/**
     * @var int
     */
    
protected $startLine 1;

    
/**
     * @var int
     */
    
protected $endLine null;

    
/**
     * @var string
     */
    
protected $namespaces = array();

    
/**
     * @var array
     */
    
protected $uses = array();

    
/**
     * @var array
     */
    
protected $requiredFiles = array();

    
/**
     * @var ReflectionClass[]
     */
    
protected $classes = array();

    
/**
     * @var FunctionReflection[]
     */
    
protected $functions = array();

    
/**
     * @var string
     */
    
protected $contents null;

    
/**
     * @param  string $filename
     * @throws Exception\RuntimeException
     */
    
public function __construct($filename)
    {
        if ((
$fileRealPath realpath($filename)) === false) {
            
$fileRealPath stream_resolve_include_path($filename);
        }

        if (!
$fileRealPath || !in_array($fileRealPathget_included_files())) {
            throw new 
Exception\RuntimeException(sprintf(
                
'File %s must be required before it can be reflected',
                
$filename
            
));
        }

        
$this->filePath $fileRealPath;
        
$this->reflect();
    }

    
/**
     * Required by the Reflector interface.
     *
     * @todo   What should this do?
     * @return null
     */
    
public static function export()
    {
        return 
null;
    }

    
/**
     * Return the file name of the reflected file
     *
     * @return string
     */
    
public function getFileName()
    {
        
// @todo get file name from path
        
return $this->filePath;
    }

    
/**
     * Get the start line - Always 1, staying consistent with the Reflection API
     *
     * @return int
     */
    
public function getStartLine()
    {
        return 
$this->startLine;
    }

    
/**
     * Get the end line / number of lines
     *
     * @return int
     */
    
public function getEndLine()
    {
        return 
$this->endLine;
    }

    
/**
     * @return string
     */
    
public function getDocComment()
    {
        return 
$this->docComment;
    }

    
/**
     * @return DocBlockReflection
     */
    
public function getDocBlock()
    {
        if (!(
$docComment $this->getDocComment())) {
            return 
false;
        }

        
$instance = new DocBlockReflection($docComment);

        return 
$instance;
    }

    
/**
     * @return array
     */
    
public function getNamespaces()
    {
        return 
$this->namespaces;
    }

    
/**
     * @return string
     */
    
public function getNamespace()
    {
        if (
count($this->namespaces) == 0) {
            return 
null;
        }

        return 
$this->namespaces[0];
    }

    
/**
     * @return array
     */
    
public function getUses()
    {
        return 
$this->uses;
    }

    
/**
     * Return the reflection classes of the classes found inside this file
     *
     * @return ClassReflection[]
     */
    
public function getClasses()
    {
        
$classes = array();
        foreach (
$this->classes as $class) {
            
$classes[] = new ClassReflection($class);
        }

        return 
$classes;
    }

    
/**
     * Return the reflection functions of the functions found inside this file
     *
     * @return FunctionReflection[]
     */
    
public function getFunctions()
    {
        
$functions = array();
        foreach (
$this->functions as $function) {
            
$functions[] = new FunctionReflection($function);
        }

        return 
$functions;
    }

    
/**
     * Retrieve the reflection class of a given class found in this file
     *
     * @param  null|string $name
     * @return ClassReflection
     * @throws Exception\InvalidArgumentException for invalid class name or invalid reflection class
     */
    
public function getClass($name null)
    {
        if (
null === $name) {
            
reset($this->classes);
            
$selected current($this->classes);

            return new 
ClassReflection($selected);
        }

        if (
in_array($name$this->classes)) {
            return new 
ClassReflection($name);
        }

        throw new 
Exception\InvalidArgumentException(sprintf(
            
'Class by name %s not found.',
            
$name
        
));
    }

    
/**
     * Return the full contents of file
     *
     * @return string
     */
    
public function getContents()
    {
        return 
file_get_contents($this->filePath);
    }

    public function 
toString()
    {
        return 
''// @todo
    
}

    
/**
     * Serialize to string
     *
     * Required by the Reflector interface
     *
     * @todo   What should this serialization look like?
     * @return string
     */
    
public function __toString()
    {
        return 
'';
    }

    
/**
     * This method does the work of "reflecting" the file
     *
     * Uses Zend\Code\Scanner\FileScanner to gather file information
     *
     * @return void
     */
    
protected function reflect()
    {
        
$scanner             = new CachingFileScanner($this->filePath);
        
$this->docComment    $scanner->getDocComment();
        
$this->requiredFiles $scanner->getIncludes();
        
$this->classes       $scanner->getClassNames();
        
$this->namespaces    $scanner->getNamespaces();
        
$this->uses          $scanner->getUses();
    }

    
/**
     * Validate / check a file level DocBlock
     *
     * @param  array $tokens Array of tokenizer tokens
     * @return void
     */
    
protected function checkFileDocBlock($tokens)
    {
        foreach (
$tokens as $token) {
            
$type    $token[0];
            
$value   $token[1];
            
$lineNum $token[2];
            if ((
$type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
                continue;
            } elseif (
$type == T_DOC_COMMENT) {
                
$this->docComment $value;
                
$this->startLine  $lineNum substr_count($value"\n") + 1;

                return;
            } else {
                
// Only whitespace is allowed before file DocBlocks
                
return;
            }
        }
    }
}