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

namespace ZendTest\Code\Generator;

use PHPUnit\Framework\TestCase;
use Zend\Code\DeclareStatement;
use Zend\Code\Exception\InvalidArgumentException;
use Zend\Code\Generator\ClassGenerator;
use Zend\Code\Generator\FileGenerator;
use Zend\Code\Reflection\FileReflection;

use function explode;
use function file_get_contents;
use function file_put_contents;
use function get_class;
use function strlen;
use function strpos;
use function strrpos;
use function sys_get_temp_dir;
use function tempnam;
use function unlink;

/**
 * @group Zend_Code_Generator
 * @group Zend_Code_Generator_Php
 * @group Zend_Code_Generator_Php_File
 */
class FileGeneratorTest extends TestCase
{
    public function testConstruction()
    {
        $file = new FileGenerator();
        self::assertEquals(FileGenerator::class, get_class($file));
    }

    public function testSourceContentGetterAndSetter()
    {
        $file = new FileGenerator();
        $file->setSourceContent('Foo');
        self::assertEquals('Foo', $file->getSourceContent());
    }

    public function testIndentationGetterAndSetter()
    {
        $file = new FileGenerator();
        $file->setIndentation('        ');
        self::assertEquals('        ', $file->getIndentation());
    }

    public function testToString()
    {
        $codeGenFile = FileGenerator::fromArray([
            'requiredFiles' => ['SampleClass.php'],
            'class' => [
                'flags' => ClassGenerator::FLAG_ABSTRACT,
                'name' => 'SampleClass',
                'extendedClass' => 'ExtendedClassName',
                'implementedInterfaces' => ['Iterator', 'Traversable'],
            ],
        ]);

        $expectedOutput = <<<EOS
<?php

require_once 'SampleClass.php';

abstract class SampleClass extends ExtendedClassName implements Iterator, Traversable
{


}


EOS;

        $output = $codeGenFile->generate();
        self::assertEquals($expectedOutput, $output, $output);
    }

    public function testFromReflection()
    {
        $tempFile = tempnam(sys_get_temp_dir(), 'UnitFile');

        $codeGenFile = FileGenerator::fromArray([
            'class' => [
                'name' => 'SampleClass',
            ],
        ]);


    public function testFromFileReflection()
    {
        $this->markTestIncomplete('Some scanning capabilities are incomplete, including file DocBlock comment retrieval and method scanning');

        $file = __DIR__ . '/TestAsset/TestSampleSingleClass.php';
        require_once $file;

        unlink($tempFile);

        self::assertEquals(FileGenerator::class, get_class($fileGenerator));
        self::assertCount(1, $fileGenerator->getClasses());
    }

    public function testFromFileReflection()
    {
        $file = __DIR__ . '/TestAsset/TestSampleSingleClass.php';
        require_once $file;
 */



/* Zend_Code_Generator_FileGenerator-ClassMarker: {ZendTest\Code\Generator\TestAsset\TestSampleSingleClass} */


namespace ZendTest\Code\Generator\TestAsset;

/**
 * class docblock
 *
 */
class TestSampleSingleClass
{
 * @author Ralph Schindler <ralph.schindler@zend.com>
 */
     * Enter description here...
     *
     * @return bool
     *
     */
    public function someMethod()
    {
 */
class TestSampleSingleClass
{

    /**
     * Enter description here...
     *
     * @return bool
     */


EOS;
        $this->assertEquals($expectedOutput, $codeGenFileFromDisk->generate());
    }

    public function testFileLineEndingsAreAlwaysLineFeed()
    {
        $codeGenFile = FileGenerator::fromArray(array(
        return true;
    }

    public function foobar()
    {
    }


}


EOS;

        self::assertEquals($expectedOutput, $codeGenFileFromDisk->generate());
    }

    /**
     * @group test
     */
    public function testFileLineEndingsAreAlwaysLineFeed()
    {
        $codeGenFile = FileGenerator::fromArray([
            'requiredFiles' => ['SampleClass.php'],
            'class' => [
                'abstract' => true,
                'name' => 'SampleClass',
                'extendedClass' => 'ExtendedClassName',
                'implementedInterfaces' => ['Iterator', 'Traversable'],
            ],
        ]);

        // explode by newline, this would leave CF in place if it were generated
        $lines = explode("\n", $codeGenFile->generate());

        $targetLength = strlen('require_once \'SampleClass.php\';');
        self::assertEquals($targetLength, strlen($lines[2]));
        self::assertEquals(';', $lines[2][$targetLength - 1]);
    }

    /**
     * @group ZF-11218
     */
    public function testGeneratesUseStatements()
    {
        $file = new FileGenerator();
        $file->setUse('My\Baz')
             ->setUses([
                 ['use' => 'Your\Bar', 'as' => 'bar'],
             ]);
        $generated = $file->generate();
        self::assertStringContainsString('use My\\Baz;', $generated);
        self::assertStringContainsString('use Your\\Bar as bar;', $generated);
    }

    public function testGeneratesNamespaceStatements()
    {
        $file = new FileGenerator();
        $file->setNamespace('Foo\Bar');
        $generated = $file->generate();
        self::assertStringContainsString('namespace Foo\\Bar', $generated, $generated);
    }

    public function testSetUseDoesntGenerateMultipleIdenticalUseStatements()
    {
        $file = new FileGenerator();
        $file->setUse('My\Baz')
             ->setUse('My\Baz');
        $generated = $file->generate();
        self::assertSame(strpos($generated, 'use My\\Baz'), strrpos($generated, 'use My\\Baz'));
    }

    public function testSetUsesDoesntGenerateMultipleIdenticalUseStatements()
    {
        $file = new FileGenerator();
        $file->setUses([
            ['use' => 'Your\Bar', 'as' => 'bar'],
            ['use' => 'Your\Bar', 'as' => 'bar'],
        ]);
        $generated = $file->generate();
        self::assertSame(strpos($generated, 'use Your\\Bar as bar;'), strrpos($generated, 'use Your\\Bar as bar;'));
    }

    public function testSetUseAllowsMultipleAliasedUseStatements()
    {
        $file = new FileGenerator();
        $file->setUses([
            ['use' => 'Your\Bar', 'as' => 'bar'],
            ['use' => 'Your\Bar', 'as' => 'bar2'],
        ]);
        $generated = $file->generate();
        self::assertStringContainsString('use Your\\Bar as bar;', $generated);
        self::assertStringContainsString('use Your\\Bar as bar2;', $generated);
    }

    public function testSetUsesWithArrays()
    {
        $file = new FileGenerator();
        $file->setUses([
            ['use' => 'Your\\Bar', 'as' => 'bar'],
            ['use' => 'My\\Baz', 'as' => 'FooBaz'],
        ]);
        $generated = $file->generate();
        self::assertStringContainsString('use My\\Baz as FooBaz;', $generated);
        self::assertStringContainsString('use Your\\Bar as bar;', $generated);
    }

    public function testSetUsesWithString()
    {
        $file = new FileGenerator();
        $file->setUses([
            'Your\\Bar',
            'My\\Baz',
            ['use' => 'Another\\Baz', 'as' => 'Baz2'],
        ]);
        $generated = $file->generate();
        self::assertStringContainsString('use My\\Baz;', $generated);
        self::assertStringContainsString('use Your\\Bar;', $generated);
        self::assertStringContainsString('use Another\\Baz as Baz2;', $generated);
    }

    public function testSetUsesWithGetUses()
    {
        $file = new FileGenerator();
        $uses = [
            'Your\\Bar',
            'My\\Baz',
            ['use' => 'Another\\Baz', 'as' => 'Baz2'],
        ];
        $file->setUses($uses);
        $file->setUses($file->getUses());
        $generated = $file->generate();
        self::assertStringContainsString('use My\\Baz;', $generated);
        self::assertStringContainsString('use Your\\Bar;', $generated);
        self::assertStringContainsString('use Another\\Baz as Baz2;', $generated);
    }

    public function testCreateFromArrayWithClassInstance()
    {
        $fileGenerator = FileGenerator::fromArray([
            'filename'  => 'foo.php',
            'class'     => new ClassGenerator('bar'),
        ]);
        $class = $fileGenerator->getClass('bar');
        self::assertInstanceOf(ClassGenerator::class, $class);
    }

    public function testCreateFromArrayWithClassFromArray()
    {

        $this->assertEquals($expectedUses, $class->getUses());
    }
}
namespace ZendTest\Code\Generator\TestAsset;


use ZendTest\Code\Generator\TestAsset\ClassWithNamespace;

class ClassWithUses
{

    public function added()
    {
    }


}


$foo->bar();
CODE;
        $actual = file_get_contents(sys_get_temp_dir() . '/result_class.php');
        self::assertEquals($expected, $actual);
    }

    public function testSingleDeclareStatement(): void
    {
        $generator = FileGenerator::fromArray([
            'declares' => [
                'strict_types' => 1,
            ],
            'class' => [
                'name' => 'SampleClass',
            ],
        ]);
        $generator->setFilename(sys_get_temp_dir() . '/result_file.php');
        $generator->write();

        $expected = <<<EOS
<?php

declare(strict_types=1);

class SampleClass
{


}


EOS;

        $actual = file_get_contents(sys_get_temp_dir() . '/result_file.php');
        $this->assertEquals($expected, $actual);
    }

    public function testMultiDeclareStatements(): void
    {
        $generator = FileGenerator::fromArray([
            'declares' => [
                'strict_types' => 1,
                'ticks' => 2,
            ],
            'class' => [
                'name' => 'SampleClass',
            ],
        ]);
        $generator->setFilename(sys_get_temp_dir() . '/result_file.php');
        $generator->write();

        $expected = <<<EOS
<?php

declare(strict_types=1);
declare(ticks=2);

class SampleClass
{


}


EOS;

        $actual = file_get_contents(sys_get_temp_dir() . '/result_file.php');
        $this->assertEquals($expected, $actual);
    }

    public function testDeclareUnknownDirectiveShouldRaiseException(): void
    {
        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Declare directive must be one of: ticks, strict_types, encoding.');

        FileGenerator::fromArray([
            'declares' => [
                'fubar' => 1,
            ],
            'class' => [
                'name' => 'SampleClass',
            ],
        ]);
    }

    public function testDeclareWrongTypeShouldRaiseException(): void
    {
        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Declare value invalid. Expected integer, got string.');

        FileGenerator::fromArray([
            'declares' => [
                'strict_types' => 'wrong type',
            ],
            'class' => [
                'name' => 'SampleClass',
            ],
        ]);
    }

    public function testDeclareDuplicatesShouldOnlyGenerateOne(): void
    {
        $generator = FileGenerator::fromArray([
            'class' => [
                'name' => 'SampleClass',
            ],
        ]);
        $generator->setFilename(sys_get_temp_dir() . '/result_file.php');
        $generator->setDeclares([
            DeclareStatement::strictTypes(1),
            DeclareStatement::strictTypes(2)
        ]);
        $generator->write();

        $expected = <<<EOS
<?php

declare(strict_types=1);

class SampleClass
{


}


EOS;

        $actual = file_get_contents(sys_get_temp_dir() . '/result_file.php');
        $this->assertEquals($expected, $actual);
    }

    public function testWrongDeclareTypeShouldRaiseException(): void
    {
        $generator = new FileGenerator();

        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('setDeclares is expecting an array of Zend\\Code\\DeclareStatement objects');
        $generator->setDeclares([new \stdClass()]);
    }
}

<MSG> Merge branch 'hotfix/zendframework/zf2#6302-fix-zendframework/zf2#4747-file-generator-properly-generates-files' into develop

Forward port zendframework/zf2#6302

<DFF> @@ -97,8 +97,6 @@ EOS;
 
     public function testFromFileReflection()
     {
-        $this->markTestIncomplete('Some scanning capabilities are incomplete, including file DocBlock comment retrieval and method scanning');
-
         $file = __DIR__ . '/TestAsset/TestSampleSingleClass.php';
         require_once $file;
 
@@ -115,15 +113,10 @@ EOS;
  */
 
 
-
-/* Zend_Code_Generator_FileGenerator-ClassMarker: {ZendTest\Code\Generator\TestAsset\TestSampleSingleClass} */
-
-
 namespace ZendTest\Code\Generator\TestAsset;
 
 /**
  * class docblock
- *
  */
 class TestSampleSingleClass
 {
@@ -132,7 +125,6 @@ class TestSampleSingleClass
      * Enter description here...
      *
      * @return bool
-     *
      */
     public function someMethod()
     {
@@ -148,9 +140,13 @@ class TestSampleSingleClass
 
 
 EOS;
+
         $this->assertEquals($expectedOutput, $codeGenFileFromDisk->generate());
     }
 
+    /**
+     * @group test
+     */
     public function testFileLineEndingsAreAlwaysLineFeed()
     {
         $codeGenFile = FileGenerator::fromArray(array(
@@ -305,4 +301,96 @@ EOS;
 
         $this->assertEquals($expectedUses, $class->getUses());
     }
+
+    /**
+     * @group 4747
+     */
+    public function testIssue4747FileGenerationWithAddedMethodIsCorrectlyFormatted()
+    {
+        $g = new \Zend\Code\Generator\FileGenerator();
+        $g = $g->fromReflectedFileName(__DIR__ . '/TestAsset/ClassWithUses.php');
+        $g->setFilename('/tmp/result_class.php');
+        $g->getClass()->addMethod('added');
+        $g->write();
+
+        $expected = <<<'CODE'
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link      http://github.com/zendframework/zf2 for the canonical source
+ * repository
+ * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc.
+ * (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+
+namespace ZendTest\Code\Generator\TestAsset;
+
+
+use ZendTest\Code\Generator\TestAsset\ClassWithNamespace;
+
+class ClassWithUses
+{
+
+    public function added()
+    {
+    }
+
+
+}
+
+
+CODE;
+        $actual = file_get_contents('/tmp/result_class.php');
+        $this->assertEquals($expected, $actual);
+    }
+
+    /**
+     * @group 4747
+     */
+    public function testCanAppendToBodyOfReflectedFile()
+    {
+        $g = new \Zend\Code\Generator\FileGenerator();
+        $g = $g->fromReflectedFileName(__DIR__ . '/TestAsset/ClassWithUses.php');
+        $g->setFilename('/tmp/result_class.php');
+        $g->getClass()->addMethod('added');
+        $g->setBody("\$foo->bar();");
+        $g->write();
+
+        $expected = <<<'CODE'
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link      http://github.com/zendframework/zf2 for the canonical source
+ * repository
+ * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc.
+ * (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+
+namespace ZendTest\Code\Generator\TestAsset;
+
+
+use ZendTest\Code\Generator\TestAsset\ClassWithNamespace;
+
+class ClassWithUses
+{
+
+    public function added()
+    {
+    }
+
+
+}
+
+
+$foo->bar();
+CODE;
+        $actual = file_get_contents('/tmp/result_class.php');
+        $this->assertEquals($expected, $actual);
+    }
 }

Merge branch 'hotfix/zendframework/zf2#6302-fix-zendframework/zf2#4747-file-generator-properly-generates-files' into develop