<?php declare(strict_types=1);

namespace Lmc\Steward\Timeline;

/**
 * Process results xml data to build data-sets for timeline visualisation via Vis.js
 */
class TimelineDataBuilder
{
    /** @var \SimpleXMLElement */
    private $xml;
    /** @var array Array of executors, key = executor URL, value = executor ID */
    private $executors;

    /**
     * @param \SimpleXMLElement $xml XML read from results.xml file
     */
    public function __construct(\SimpleXMLElement $xml)
    {
        $this->xml = $xml;
        $this->executors = $this->getExecutors();
    }

    public function buildTimelineGroups(): array
    {
        $timelineGroups = [];

        foreach ($this->executors as $executorUrl => $executorId) {
            $timelineGroups[] = [
                'id' => $executorId,
                'content' => $executorUrl,
                'title' => $executorUrl,
            ];
        }

        return $timelineGroups;
    }

    public function buildTimelineItems(): array
    {
        $testElements = $this->xml->xpath('//testcase/test[@status="done"]');
        $timelineItems = [];

        foreach ($testElements as $testElement) {
            $timelineItems[] = [
                'group' => $this->resolveTestExecutorId($testElement),
                'content' => (string) $testElement['name'],
                'title' => $this->buildItemTitle($testElement),
                'start' => $this->toCompatibleDateFormat((string) $testElement['start']),
                'end' => $this->toCompatibleDateFormat((string) $testElement['end']),
                'className' => (string) $testElement['result'],
            ];
        }

        return $timelineItems;
    }

    /**
     * Get array of unique executors
     *
     * @return array Array of executors, key = executor URL, value = executor ID
     */
    private function getExecutors(): array
    {
        $testElements = $this->xml->xpath('//testcase/test[@status="done"]');
        $hasTestWithoutExecutor = false;
        $executors = [];

        foreach ($testElements as $testElement) {
            $executorValue = (string) $testElement['executor'];

            if (!empty($executorValue)) {
                $executors[] = $executorValue;
            } else {
                $hasTestWithoutExecutor = true;
            }
        }

        $executors = array_unique($executors);
        // sort and reindex
        sort($executors);
        // use executors index as a value and executor name as a key
        $executors = array_flip($executors);

        if ($hasTestWithoutExecutor) {
            $executors['unknown'] = 'unknown';
        }

        return $executors;
    }

    private function buildItemTitle(\SimpleXMLElement $testElement): string
    {
        return sprintf(
            '%s<br>(%d sec)',
            $this->assembleFullTestName($testElement),
            $this->calculateTestDurationSeconds((string) $testElement['start'], (string) $testElement['end'])
        );
    }

    private function assembleFullTestName(\SimpleXMLElement $testElement): string
    {
        $parentElement = $testElement->xpath('..');
        $testcaseName = (string) reset($parentElement)['name'];

        return $testcaseName . '::' . $testElement['name'];
    }

    private function calculateTestDurationSeconds(string $start, string $end): int
    {
        return (new \DateTimeImmutable($end))->getTimestamp() - (new \DateTimeImmutable($start))->getTimestamp();
    }

    private function resolveTestExecutorId(\SimpleXMLElement $testElement): string
    {
        $testExecutor = (string) $testElement['executor'];

        if (!empty($testExecutor) && isset($this->executors[$testExecutor])) {
            return (string) $this->executors[$testExecutor];
        }

        return 'unknown';
    }

    /**
     * Convert ISO date to compatible date format (drop timezone).
     *
     * @see https://stackoverflow.com/q/6427204/464890
     */
    private function toCompatibleDateFormat(string $isoDate): string
    {
        $date = new \DateTimeImmutable($isoDate);

        return $date->format('Y-m-d\TH:i:s');
    }
}


<?php declare(strict_types=1);

namespace Lmc\Steward\Selenium;

use Facebook\WebDriver\Firefox\FirefoxDriver;
use Facebook\WebDriver\Firefox\FirefoxProfile;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\WebDriverBrowserType;
use Facebook\WebDriver\Remote\WebDriverCapabilityType;
use Facebook\WebDriver\WebDriverPlatform;
use Lmc\Steward\ConfigProvider;
use Lmc\Steward\Test\AbstractTestCase;
use OndraM\CiDetector\CiDetector;

final class CapabilitiesResolver
{
    /** @var ConfigProvider */
    protected $config;
    /** @var CiDetector */
    protected $ciDetector;
    /** @var CustomCapabilitiesResolverInterface|null */
    protected $customCapabilitiesResolver;

    public function __construct(ConfigProvider $config)
    {
        $this->config = $config;

        if (!empty($config->capabilitiesResolver)) {
            $this->customCapabilitiesResolver = new $config->capabilitiesResolver($config);
        }

        $this->ciDetector = new CiDetector();
    }

    public function resolveDesiredCapabilities(AbstractTestCase $test): DesiredCapabilities
    {
        $capabilities = new DesiredCapabilities(
            [
                WebDriverCapabilityType::BROWSER_NAME => $this->config->browserName,
                WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY,
                'name' => get_class($test) . '::' . $test->getName(),
            ]
        );

        if (!empty($this->config->capability)) {
            $extraCapabilities = json_decode($this->config->capability);
            foreach ($extraCapabilities as $extraCapabilityName => $extraCapabilityValue) {
                $capabilities->setCapability($extraCapabilityName, $extraCapabilityValue);
            }
        }

        if ($this->ciDetector->isCiDetected()) {
            $capabilities = $this->setupCiCapabilities($capabilities, $test);
        }

        $capabilities = $this->setupBrowserSpecificCapabilities($capabilities, $this->config->browserName);

        if ($this->customCapabilitiesResolver !== null) {
            $capabilities = $this->customCapabilitiesResolver->resolveDesiredCapabilities($test, $capabilities);
        }

        return $capabilities;
    }

    public function resolveRequiredCapabilities(AbstractTestCase $test): DesiredCapabilities
    {
        $capabilities = new DesiredCapabilities();

        if ($this->customCapabilitiesResolver !== null) {
            $capabilities = $this->customCapabilitiesResolver->resolveRequiredCapabilities($test, $capabilities);
        }

        return $capabilities;
    }

    /**
     * @internal
     */
    public function setCiDetector(CiDetector $ciDetector): void
    {
        $this->ciDetector = $ciDetector;
    }

    /**
     * Setup capabilities specific for continuous integration server
     */
    protected function setupCiCapabilities(
        DesiredCapabilities $capabilities,
        AbstractTestCase $test
    ): DesiredCapabilities {
        $ci = $this->ciDetector->detect();
        $capabilities->setCapability(
            'build',
            $this->config->env . '-' . $ci->getBuildNumber()
        );
        $capabilities->setCapability(
            'tags',
            [$this->config->env, $ci->getCiName(), get_class($test)]
        );

        return $capabilities;
    }

    protected function setupBrowserSpecificCapabilities(
        DesiredCapabilities $capabilities,
        string $browser
    ): DesiredCapabilities {
        switch ($browser) {
            case WebDriverBrowserType::FIREFOX:
                $capabilities = $this->setupFirefoxCapabilities($capabilities);
                break;
            case WebDriverBrowserType::IE:
                $capabilities = $this->setupInternetExplorerCapabilities($capabilities);
                break;
        }

        return $capabilities;
    }

    protected function setupFirefoxCapabilities(DesiredCapabilities $capabilities): DesiredCapabilities
    {
        // Firefox does not (as a intended feature) trigger "change" and "focus" events in javascript if not in active
        // (focused) window. This would be a problem for concurrent testing - solution is to use focusmanager.testmode.
        // See https://code.google.com/p/selenium/issues/detail?id=157
        $profile = new FirefoxProfile(); // see https://github.com/facebook/php-webdriver/wiki/FirefoxProfile
        $profile->setPreference('focusmanager.testmode', true);

        $capabilities->setCapability(FirefoxDriver::PROFILE, $profile);

        return $capabilities;
    }

    protected function setupInternetExplorerCapabilities(DesiredCapabilities $capabilities): DesiredCapabilities
    {
        // Clears cache, cookies, history, and saved form data of MSIE.
        $capabilities->setCapability('ie.ensureCleanSession', true);

        return $capabilities;
    }
}


<?php declare(strict_types=1);

namespace Lmc\Steward;

use Doctrine\Common\Inflector\Inflector;
use FlorianWolters\Component\Util\Singleton\SingletonTrait;

/**
 * Provide access to global configuration (from within the tests).
 * The configuration is immutable - ie. cannot be altered after it is used for the first time.
 *
 * @method static ConfigProvider getInstance()
 * @property-read string $browserName
 * @property-read string $env
 * @property-read string $serverUrl
 * @property-read string $capability
 * @property-read string $capabilitiesResolver
 * @property-read string $logsDir
 * @property-read string $debug
 */
class ConfigProvider
{
    use SingletonTrait;

    /** @var array Configuration options and theirs values */
    private $config = null;
    /** @var array Array of custom configuration options that should be added to the default ones */
    private $customConfigurationOptions = [];

    public function __get(string $name)
    {
        $this->initialize();

        if (isset($this->config[$name])) {
            return $this->config[$name];
        }

        throw new \DomainException(sprintf('Configuration option "%s" was not defined', $name));
    }

    public function __set(string $name, $value): void
    {
        throw new \LogicException('Configuration values are immutable after initialization and cannot be changed');
    }

    public function __unset(string $name): void
    {
        throw new \LogicException('Configuration values are immutable after initialization and cannot be changed');
    }

    public function __isset(string $name): bool
    {
        $this->initialize();

        return isset($this->config[$name]);
    }

    /**
     * Add custom configuration options that should be added to the default ones.
     * Can be set only before initialization, because the configuration is then immutable.
     *
     * Note you cannot override the default configuration options.
     *
     * @param array $customConfigurationOptions Array with values = configuration options (environment variables)
     */
    public function setCustomConfigurationOptions(array $customConfigurationOptions): void
    {
        if ($this->config !== null) {
            throw new \RuntimeException(
                'Custom configuration options can be set only before initialization of configuration'
            );
        }

        $this->customConfigurationOptions = $customConfigurationOptions;
    }

    private function initialize(): void
    {
        if ($this->config !== null) {
            return;
        }

        $options = $this->assembleConfigurationOptions();

        $this->config = $this->retrieveConfigurationValues($options);
    }

    /**
     * Assemble map of configuration options and its default values.
     *
     * @return array Default required configuration options
     */
    private function assembleConfigurationOptions(): array
    {
        $defaultConfigurationOptions = [
            'BROWSER_NAME',
            'ENV',
            'SERVER_URL',
            'CAPABILITY',
            'CAPABILITIES_RESOLVER',
            'LOGS_DIR',
            'DEBUG',
        ];

        // Merge defaults with possible custom options
        $options = array_merge($defaultConfigurationOptions, $this->customConfigurationOptions);

        return $options;
    }

    /**
     * Retrieve given configuration options values from environment. If value is not found, throw and exception.
     *
     * @throws \RuntimeException
     * @return array Option => value option name is converted from CAPS_WITH_UNDERSCORES to camelCase
     */
    private function retrieveConfigurationValues(array $options): array
    {
        $outputValues = [];

        foreach ($options as $option) {
            $value = getenv($option);

            // value was not retrieved => fail
            if ($value === false) {
                throw new \RuntimeException(sprintf('%s environment variable must be defined', $option));
            }

            $outputValues[Inflector::camelize(mb_strtolower($option))] = $value;
        }

        return $outputValues;
    }
}


<?php declare(strict_types=1);

namespace Lmc\Steward\Process;

use Lmc\Steward\Console\Style\StewardStyle;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Stopwatch\Stopwatch;

class ExecutionLoop
{
    /** @var ProcessSet */
    private $processSet;
    /** @var StewardStyle */
    private $io;
    /** @var OptimizeOrderInterface */
    private $optimizeStrategy;
    /** @var int */
    private $parallelLimit;
    /** @var Stopwatch */
    protected $stopwatch;

    public function __construct(
        ProcessSet $processSet,
        StewardStyle $io,
        OptimizeOrderInterface $optimizeStrategy,
        int $parallelLimit = 50
    ) {
        $this->processSet = $processSet;
        $this->io = $io;
        $this->optimizeStrategy = $optimizeStrategy;
        $this->parallelLimit = $parallelLimit;
        $this->stopwatch = new Stopwatch();
    }

    public function start(): bool
    {
        $this->initialize();

        $this->io->isVeryVerbose() ? $this->io->section('Starting execution of testcases') : $this->io->newLine();

        $counterWaitingOutput = 1;
        $statusesCountLast = [];

        // Iterate over prepared and queued until everything is done
        while (true) {
            $prepared = $this->processSet->get(ProcessWrapper::PROCESS_STATUS_PREPARED);
            $queued = $this->processSet->get(ProcessWrapper::PROCESS_STATUS_QUEUED);

            if (count($prepared) === 0 && count($queued) === 0) {
                break;
            }
            // Start all prepared tasks and set status of not running as finished
            foreach ($prepared as $testClass => $processWrapper) {
                if (!$processWrapper->getProcess()->isStarted()) {
                    if ($this->io->isVeryVerbose()) {
                        $this->io->runStatus(
                            sprintf(
                                'Execution of testcase "%s" started%s',
                                $testClass,
                                $this->io->isDebug() ?
                                    " with command:\n" . $processWrapper->getProcess()->getCommandLine() : ''
                            )
                        );
                    }

                    $this->stopwatch->start($testClass);
                    $processWrapper->getProcess()->start();
                    usleep(50000); // wait for a while (0,05 sec) to let processes be started in intended order

                    continue;
                }

                if ($timeoutError = $processWrapper->checkProcessTimeout()) {
                    if ($this->io->isVeryVerbose()) {
                        $this->io->error($timeoutError);
                    }
                }

                if ($this->io->isDebug()) { // In debug mode print all output as it comes
                    $this->flushProcessOutput($processWrapper);
                }

                if (!$processWrapper->getProcess()->isRunning()) {
                    $testcaseEnd = $this->stopwatch->stop($testClass);
                    // Mark no longer running processes as finished
                    $processWrapper->setStatus(ProcessWrapper::PROCESS_STATUS_DONE);

                    $hasProcessPassed = $processWrapper->getResult() === ProcessWrapper::PROCESS_RESULT_PASSED;

                    if ($this->io->isDebug()) { // There could be new output since the previous flush
                        $this->flushProcessOutput($processWrapper);
                    }

                    if ($this->io->isVeryVerbose()) {
                        $processOutput = $processErrorOutput = '';
                        if (!$hasProcessPassed) { // If process failed, collect its output
                            $processOutput = $processWrapper->getProcess()->getIncrementalOutput();
                            $processErrorOutput = $processWrapper->getProcess()->getIncrementalErrorOutput();
                        }

                        $testcaseFinishedMessage = sprintf(
                            'Finished execution of testcase "%s" (result: %s, time: %.1F sec)%s',
                            $testClass,
                            $processWrapper->getResult(),
                            $testcaseEnd->getDuration() / 1000,
                            (!empty($processOutput) || !empty($processErrorOutput) ? ', output:' : '')
                        );
                        $hasProcessPassed ? $this->io->runStatusSuccess($testcaseFinishedMessage)
                            : $this->io->runStatusError($testcaseFinishedMessage);

                        $this->io->output($processOutput, $processWrapper->getClassName());
                        $this->io->errorOutput($processErrorOutput, $processWrapper->getClassName());
                    } elseif ($this->io->isVerbose() && !$hasProcessPassed) {
                        $this->io->runStatusError(
                            sprintf('Testcase "%s" %s', $testClass, $processWrapper->getResult())
                        );
                    }

                    // Fail also process dependencies
                    if (!$hasProcessPassed) {
                        $this->failDependants($testClass);
                    }
                }
            }
            $this->dequeueParallelProcesses();
            $this->dequeueDependentProcesses();

            $statusesCount = $this->processSet->countStatuses();

            // if the output didn't change, wait 100 iterations (10 seconds) before printing it again
            if ($statusesCount === $statusesCountLast && $counterWaitingOutput % 100 !== 0) {
                $counterWaitingOutput++;
            } else {
                $this->printExecutionLoopStatus($statusesCount);
                $counterWaitingOutput = 1;
            }

            $statusesCountLast = $statusesCount;
            usleep(100000); // 0,1 sec
        }

        $doneCount = count($this->processSet->get(ProcessWrapper::PROCESS_STATUS_DONE));
        $resultsCount = $this->processSet->countResults();
        $allTestsPassed = ($resultsCount[ProcessWrapper::PROCESS_RESULT_PASSED] === $doneCount);
        $resultsInfo = [];
        foreach (ProcessWrapper::PROCESS_RESULTS as $resultType) {
            if ($resultsCount[$resultType] > 0) {
                $resultsInfo[] = sprintf('%s: %d', $resultType, $resultsCount[$resultType]);
            }
        }

        $event = $this->stopwatch->stop('run');
        $this->io->runStatus(sprintf('All testcases done in %.1F seconds', $event->getDuration() / 1000));

        $resultMessage = sprintf('Testcases executed: %d (%s)', $doneCount, implode(', ', $resultsInfo));
        $allTestsPassed ? $this->io->success($resultMessage) : $this->io->error($resultMessage);

        return $allTestsPassed;
    }

    protected function initialize(): void
    {
        $this->stopwatch->start('run');

        // Optimize processes order
        $this->processSet->optimizeOrder($this->optimizeStrategy);

        // Initialize first processes that should be run
        $this->dequeueProcessesWithoutDelay();
    }

    /**
     * Set queued processes without delay as prepared
     */
    protected function dequeueProcessesWithoutDelay(): void
    {
        $queuedProcesses = $this->processSet->get(ProcessWrapper::PROCESS_STATUS_QUEUED);

        foreach ($queuedProcesses as $className => $processWrapper) {
            if ($processWrapper->isDelayed()) {
                $this->io->writeln(
                    sprintf(
                        'Testcase "%s" is queued to be run %01.1f minutes after testcase "%s" is finished',
                        $className,
                        $processWrapper->getDelayMinutes(),
                        $processWrapper->getDelayAfter()
                    ),
                    OutputInterface::VERBOSITY_DEBUG
                );
            } elseif ($this->parallelLimitReached()) {
                $this->io->writeln(
                    sprintf('Max parallel limit reached, testcase "%s" is queued', $className),
                    OutputInterface::VERBOSITY_QUIET
                );
            } else {
                $this->io->writeln(
                    sprintf('Testcase "%s" is prepared to be run', $className),
                    OutputInterface::VERBOSITY_DEBUG
                );
                $processWrapper->setStatus(ProcessWrapper::PROCESS_STATUS_PREPARED);
            }
        }
    }

    protected function dequeueDependentProcesses(): void
    {
        // Retrieve names of done tests
        $done = $this->processSet->get(ProcessWrapper::PROCESS_STATUS_DONE);
        $doneClasses = [];
        foreach ($done as $testClass => $processWrapper) {
            $doneClasses[] = $testClass;
        }

        // Set queued tasks as prepared if their dependent task is done and delay has passed
        $queued = $this->processSet->get(ProcessWrapper::PROCESS_STATUS_QUEUED);
        foreach ($queued as $testClass => $processWrapper) {
            $delaySeconds = $processWrapper->getDelayMinutes() * 60;

            if (!$this->parallelLimitReached()
                && in_array($processWrapper->getDelayAfter(), $doneClasses, true)
                && (time() - $done[$processWrapper->getDelayAfter()]->getFinishedTime()) > $delaySeconds
            ) {
                if ($this->io->isVeryVerbose()) {
                    $this->io->runStatus(sprintf('Dequeing testcase "%s"', $testClass));
                }
                $processWrapper->setStatus(ProcessWrapper::PROCESS_STATUS_PREPARED);
            }
        }
    }

    protected function dequeueParallelProcesses(): void
    {
        $queued = $this->processSet->get(ProcessWrapper::PROCESS_STATUS_QUEUED);

        foreach ($queued as $testClass => $processWrapper) {
            if (!$processWrapper->isDelayed() && !$this->parallelLimitReached()) {
                if ($this->io->isVeryVerbose()) {
                    $this->io->runStatus(
                        sprintf('Dequeing testcase "%s" which was queued because of parallel limit', $testClass)
                    );
                }
                $processWrapper->setStatus(ProcessWrapper::PROCESS_STATUS_PREPARED);
            }
        }
    }

    protected function printExecutionLoopStatus(array $statusesCount): void
    {
        $resultsInfo = [];
        $resultsCount = $this->processSet->countResults();
        if ($statusesCount[ProcessWrapper::PROCESS_STATUS_DONE] > 0) {
            foreach (ProcessWrapper::PROCESS_RESULTS as $resultType) {
                if ($resultsCount[$resultType] > 0) {
                    $resultsInfo[] = sprintf(
                        '%s: <fg=%s>%d</>',
                        $resultType,
                        $resultType === ProcessWrapper::PROCESS_RESULT_PASSED ? 'green' : 'red',
                        $resultsCount[$resultType]
                    );
                }
            }
        }

        $this->io->runStatus(
            sprintf(
                'Waiting (running: %d, queued: %d, done: %d%s)',
                $statusesCount[ProcessWrapper::PROCESS_STATUS_PREPARED],
                $statusesCount[ProcessWrapper::PROCESS_STATUS_QUEUED],
                $statusesCount[ProcessWrapper::PROCESS_STATUS_DONE],
                count($resultsInfo) ? ' [' . implode(', ', $resultsInfo) . ']' : ''
            )
        );
    }

    protected function failDependants(string $testClass): void
    {
        $failedDependants = $this->processSet->failDependants($testClass);

        if ($this->io->isVerbose()) {
            foreach ($failedDependants as $failedClass => $failedProcessWrapper) {
                $this->io->runStatusError(
                    sprintf(
                        'Failing testcase "%s", because it was depending on failed "%s"',
                        $failedClass,
                        $testClass
                    )
                );
            }
        }
    }

    /**
     * Flush output of the process
     */
    protected function flushProcessOutput(ProcessWrapper $processWrapper): void
    {
        $this->io->output(
            $processWrapper->getProcess()->getIncrementalOutput(),
            $processWrapper->getClassName()
        );
        $this->io->errorOutput(
            $processWrapper->getProcess()->getIncrementalErrorOutput(),
            $processWrapper->getClassName()
        );
    }

    protected function parallelLimitReached(): bool
    {
        return count($this->processSet->get(ProcessWrapper::PROCESS_STATUS_PREPARED)) >= $this->parallelLimit;
    }
}


<?php declare(strict_types=1);

namespace Lmc\Steward\Listener;

use Facebook\WebDriver\Exception\UnknownServerException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\WebDriverBrowserType;
use Lmc\Steward\ConfigProvider;
use Lmc\Steward\Selenium\CapabilitiesResolver;
use Lmc\Steward\Test\AbstractTestCase;
use Lmc\Steward\WebDriver\NullWebDriver;
use Lmc\Steward\WebDriver\RemoteWebDriver;
use Nette\Reflection\AnnotationsParser;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestListenerDefaultImplementation;
use PHPUnit\Framework\WarningTestCase;
use PHPUnit\Runner\BaseTestRunner;

/**
 * Listener for initialization and destruction of WebDriver before and after each test.
 *
 * Note: This is done as a listener rather then in setUp() and tearDown(), as a workaround
 * for the sequence in which PHPUnit executes tearDown() of tests and addFailure() on listeners.
 * If taking screenshot using addFailure(), tearDown() would have already been called and the
 * browser would be closed.
 */
class WebDriverListener implements TestListener
{
    use TestListenerDefaultImplementation;

    protected const NO_BROWSER_ANNOTATION = 'noBrowser';

    /** @var ConfigProvider */
    protected $config;
    /** @var CapabilitiesResolver */
    protected $capabilitiesResolver;

    public function __construct()
    {
        $this->config = ConfigProvider::getInstance();
    }

    protected function getCapabilitiesResolver(): CapabilitiesResolver
    {
        if ($this->capabilitiesResolver === null) {
            $this->capabilitiesResolver = new CapabilitiesResolver($this->config);
        }

        return $this->capabilitiesResolver;
    }

    public function startTest(Test $test): void
    {
        if ($test instanceof WarningTestCase) {
            return;
        }

        if (!$test instanceof AbstractTestCase) {
            throw new \InvalidArgumentException('Test case must be descendant of Lmc\Steward\Test\AbstractTestCase');
        }

        if ($test->getStatus() === BaseTestRunner::STATUS_SKIPPED) {
            return;
        }

        // Initialize NullWebDriver if self::NO_BROWSER_ANNOTATION is used on testcase class or test method
        $testCaseAnnotations = AnnotationsParser::getAll(new \ReflectionClass($test));
        $testAnnotations = AnnotationsParser::getAll(new \ReflectionMethod($test, $test->getName(false)));

        if (isset($testCaseAnnotations[self::NO_BROWSER_ANNOTATION])
            || isset($testAnnotations[self::NO_BROWSER_ANNOTATION])
        ) {
            $test->wd = new NullWebDriver();
            $test->log(
                'Initializing Null WebDriver (@%s annotation used %s)',
                self::NO_BROWSER_ANNOTATION,
                isset($testCaseAnnotations[self::NO_BROWSER_ANNOTATION]) ? 'on class' : 'on method'
            );

            return;
        }

        // Initialize real WebDriver otherwise
        $test->log('Initializing "%s" WebDriver', $this->config->browserName);

        $desiredCapabilities = $this->getCapabilitiesResolver()->resolveDesiredCapabilities($test);
        $requiredCapabilities = $this->getCapabilitiesResolver()->resolveRequiredCapabilities($test);

        $this->createWebDriver(
            $test,
            $this->config->serverUrl,
            $desiredCapabilities,
            $requiredCapabilities,
            $connectTimeoutMs = 2 * 60 * 1000,
            // How long could request to Selenium take (eg. how long could we wait in hub's queue to available node)
            $requestTimeoutMs = 60 * 60 * 1000 // 1 hour (same as timeout for the whole process)
        );
    }

    public function endTest(Test $test, float $time): void
    {
        if ($test instanceof WarningTestCase) {
            return;
        }

        if (!$test instanceof AbstractTestCase) {
            throw new \InvalidArgumentException('Test case must be descendant of Lmc\Steward\Test\AbstractTestCase');
        }

        if ($test->wd instanceof RemoteWebDriver) {
            // The endTest() method of WebDriverListener is called before endTest() of ResultPrinter, so any output
            // from this listener would overtake output from the tests itself, what would be confusing. Instead of this,
            // we append them to the output of the test itself, to print them in proper chronological order.
            $test->appendTestLog(
                'Destroying "%s" WebDriver for session "%s"',
                ConfigProvider::getInstance()->browserName,
                $test->wd->getSessionID()
            );

            ob_start(); // Capture any output from commands bellow to make them appended to output of the test.
            try {
                // Workaround for PhantomJS 1.x - see https://github.com/detro/ghostdriver/issues/343
                // Should be removed with PhantomJS 2
                if (ConfigProvider::getInstance()->browserName === WebDriverBrowserType::PHANTOMJS) {
                    $test->wd->execute('deleteAllCookies');
                }

                $test->wd->close();
                $test->wd->quit();
            } catch (WebDriverException $e) {
                $test->warn('Error closing the session, browser may died.');
            } finally {
                $output = ob_get_clean();
                $test->appendFormattedTestLog($output);
            }
        }
    }

    /**
     * Subroutine to encapsulate creation of real WebDriver. Handles some exceptions that may occur etc.
     * The WebDriver instance is stored to $test->wd when created.
     *
     * @throws UnknownServerException
     */
    protected function createWebDriver(
        AbstractTestCase $test,
        string $remoteServerUrl,
        DesiredCapabilities $desiredCapabilities,
        DesiredCapabilities $requiredCapabilities,
        int $connectTimeoutMs,
        int $requestTimeoutMs
    ): void {
        $browserName = ConfigProvider::getInstance()->browserName;

        for ($startAttempts = 0; $startAttempts < 4; $startAttempts++) {
            try {
                $test->wd = RemoteWebDriver::create(
                    $remoteServerUrl,
                    $desiredCapabilities,
                    $connectTimeoutMs,
                    $requestTimeoutMs,
                    null,
                    null,
                    $requiredCapabilities
                );

                return;
            } catch (UnknownServerException $e) {
                if ($this->cannotBindToFirefoxLockingPort($browserName, $e->getMessage())) {
                    // As a consequence of Selenium issue #5172 (cannot change locking port), Firefox may on CI server
                    // collide with other FF instance. As a workaround, we try to start it again after a short delay.
                    $test->warn(
                        'Firefox locking port is occupied; beginning attempt #%d to start it ("%s")',
                        $startAttempts + 2,
                        $e->getMessage()
                    );
                    sleep(1);
                    continue;
                }

                if (mb_strpos($e->getMessage(), 'Error forwarding the new session') !== false) {
                    $test->warn('Cannot execute test on the node. Maybe you started just the hub and not the node?');
                }

                throw $e;
            } catch (WebDriverException $e) {
                if ($this->mayBeMissingFullServerUrl($remoteServerUrl, $e->getMessage())) {
                    $test->warn('Unable to initialize new WebDriver session.');
                    $test->warn(
                        'Server URL ("%s") appears to be missing "/wd/hub" part, so make sure you are ussing full URL'
                        . ' of the server (not just a hostname and port).',
                        $remoteServerUrl
                    );
                }

                throw $e;
            }
        }

        $test->warn('All %d attempts to instantiate Firefox WebDriver failed', $startAttempts + 1);
        throw $e;
    }

    private function cannotBindToFirefoxLockingPort(string $browserName, string $exceptionMessage): bool
    {
        return $browserName === 'firefox'
            && mb_strpos($exceptionMessage, 'Unable to bind to locking port') !== false;
    }

    private function mayBeMissingFullServerUrl(string $remoteServerUrl, string $exceptionMessage): bool
    {
        return mb_strpos($exceptionMessage, 'JSON decoding of remote response failed.') !== false
            && mb_strpos($remoteServerUrl, 'wd/hub') === false;
    }
}


<?php

namespace ChristianKuri\LaravelFavorite\Traits;

use ChristianKuri\LaravelFavorite\Models\Favorite;

/**
 * This file is part of Laravel Favorite,
 *
 * @license MIT
 * @package ChristianKuri/laravel-favorite
 *
 * Copyright (c) 2016 Christian Kuri
 */
trait Favoriteability
{
	/**
     * Define a one-to-many relationship.
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
	public function favorites()
    {
        return $this->hasMany(Favorite::class, 'user_id');
    }

    /**
     * Return a collection with the User favorited Model.
     * The Model needs to have the Favoriteable trait
     *
     * @param  $class *** Accepts for example: Post::class or 'App\Post' ****
     * @return Collection
     */
    public function favorite($class)
    {
        return $this->favorites()->where('favoriteable_type', $class)->with('favoriteable')->get()->mapWithKeys(function ($item) {
            return [$item['favoriteable']->id=>$item['favoriteable']];
        });
    }

    /**
     * Add the object to the User favorites.
     * The Model needs to have the Favoriteable trai
     *
     * @param Object $object
     */
    public function addFavorite($object)
    {
    	$object->addFavorite($this->id);
    }

    /**
     * Remove the Object from the user favorites.
     * The Model needs to have the Favoriteable trai
     *
     * @param Object $object
     */
    public function removeFavorite($object)
    {
        $object->removeFavorite($this->id);
    }

    /**
     * Toggle the favorite status from this Object from the user favorites.
     * The Model needs to have the Favoriteable trai
     *
     * @param Object $object
     */
    public function toggleFavorite($object)
    {
        $object->toggleFavorite($this->id);
    }

    /**
     * Check if the user has favorited this Object
     * The Model needs to have the Favoriteable trai
     *
     * @param Object $object
     * @return boolean
     */
    public function isFavorited($object)
    {
        return $object->isFavorited($this->id);
    }

    /**
     * Check if the user has favorited this Object
     * The Model needs to have the Favoriteable trai
     *
     * @param Object $object
     * @return boolean
     */
    public function hasFavorited($object)
    {
        return $object->isFavorited($this->id);
    }
}


<?php
namespace Codeception\Task;

use Robo\Contract\TaskInterface;
use Robo\Exception\TaskException;
use Robo\Task\BaseTask;

trait MergeReports
{
    protected function taskMergeXmlReports($src = [])
    {
        return $this->task(MergeXmlReportsTask::class, $src);
    }

    protected function taskMergeHTMLReports($src = [])
    {
        return $this->task(MergeHTMLReportsTask::class, $src);
    }
}

interface MergeReportsTaskInterface
{
    public function from($fileName);

    public function into($fileName);
}

class MergeXmlReportsTask extends BaseTask implements TaskInterface, MergeReportsTaskInterface
{
    protected $src = [];
    protected $dst;
    protected $summarizeTime = true;

    protected $mergeRewrite = false;
    /** @var \DOMElement[][] */
    protected $suites = [];

    public function __construct($src = [])
    {
        $this->src = $src;
    }

    public function sumTime()
    {
        $this->summarizeTime = true;
    }

    public function maxTime()
    {
        $this->summarizeTime = false;
    }

    public function mergeRewrite()
    {
        $this->mergeRewrite = true;
        return $this;
    }

    public function from($fileName)
    {
        if (is_array($fileName)) {
            $this->src = array_merge($fileName, $this->src);
        } else {
            $this->src[] = $fileName;
        }
        return $this;
    }

    public function into($fileName)
    {
        $this->dst = $fileName;
        return $this;
    }

    public function run()
    {
        if (!$this->dst) {
            throw new TaskException($this, "No destination file is set. Use `->into()` method to set result xml");
        }
        $this->printTaskInfo("Merging JUnit XML reports into {$this->dst}");
        $dstXml = new \DOMDocument();
        $dstXml->appendChild($dstXml->createElement('testsuites'));

        $this->suites = [];
        foreach ($this->src as $src) {
            $this->printTaskInfo("Processing $src");

            $srcXml = new \DOMDocument();
            if (!file_exists($src)) {
                throw new TaskException($this, "XML file $src does not exist");
            }
            $loaded = $srcXml->load($src);
            if (!$loaded) {
                $this->printTaskInfo("<error>File $src can't be loaded as XML</error>");
                continue;
            }
            $suiteNodes = (new \DOMXPath($srcXml))->query('//testsuites/testsuite');
            foreach ($suiteNodes as $suiteNode) {
                $suiteNode = $dstXml->importNode($suiteNode, true);
                /** @var $suiteNode \DOMElement  **/
                $this->loadSuites($suiteNode);
            }
        }

        $this->mergeSuites($dstXml);

        $dstXml->save($this->dst);
        $this->printTaskInfo("File <info>{$this->dst}</info> saved. ".count($this->suites).' suites added');
    }

    protected function loadSuites(\DOMElement $current)
    {
        /** @var \DOMNode $node */
        foreach ($current->childNodes as $node) {
            if ($node instanceof \DOMElement) {
                if ($this->mergeRewrite) {
                    $this->suites[$current->getAttribute('name')][$node->getAttribute('class') . '::' . $node->getAttribute('name')] = $node->cloneNode(true);
                } else {
                    $this->suites[$current->getAttribute('name')][] = $node->cloneNode(true);
                }
            }
        }
    }

    protected function mergeSuites(\DOMDocument $dstXml)
    {
        foreach ($this->suites as $suiteName => $tests) {
            $resultNode = $dstXml->createElement("testsuite");
            $resultNode->setAttribute('name', $suiteName);
            $data = [
                'tests' => count($tests),
                'assertions' => 0,
                'failures' => 0,
                'errors' => 0,
                'time' => 0,
            ];
            foreach ($tests as $test) {
                $resultNode->appendChild($test);

                $data['assertions'] += (int)$test->getAttribute('assertions');
                $data['time'] = $this->summarizeTime
                    ? ((float) $test->getAttribute('time') + $data['time'])
                    : max($test->getAttribute('time'), $data['time']);

                $data['failures'] += $test->getElementsByTagName('failure')->length;
                $data['errors'] += $test->getElementsByTagName('error')->length;
            }
            foreach ($data as $key => $value) {
                $resultNode->setAttribute($key, $value);
            }
            $dstXml->firstChild->appendChild($resultNode);
        }
    }
}

/**
 * Generate common HTML report
 * Class MergeHTMLReportsTask
 * @author Kerimov Asif
 */
class MergeHTMLReportsTask extends BaseTask implements TaskInterface, MergeReportsTaskInterface
{
    protected $src = [];
    protected $dst;
    protected $countSuccess = 0;
    protected $countFailed = 0;
    protected $countSkipped = 0;
    protected $countIncomplete = 0;
    protected $previousLibXmlUseErrors;

    public function __construct($src = [])
    {
        $this->src = $src;
    }

    public function from($fileName)
    {
        if (is_array($fileName)) {
            $this->src = array_merge($fileName, $this->src);
        } else {
            $this->src[] = $fileName;
        }
        return $this;
    }

    public function into($fileName)
    {
        $this->dst = $fileName;
        return $this;
    }

    public function run()
    {
        //save initial statament and switch on use_internal_errors mode
        $this->previousLibXmlUseErrors = libxml_use_internal_errors(true);

        if (!$this->dst) {
            libxml_use_internal_errors($this->previousLibXmlUseErrors);
            throw new TaskException($this, "No destination file is set. Use `->into()` method to set result HTML");
        }

        $this->printTaskInfo("Merging HTML reports into {$this->dst}");

        //read first source file as main
        $dstHTML = new \DOMDocument();
        $dstHTML->loadHTMLFile($this->src[0],LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

        //main node for all table rows
        $table = (new \DOMXPath($dstHTML))->query("//table")->item(0);

        //prepare reference nodes for envs
        $refnodes = (new \DOMXPath($dstHTML))->query("//div[@class='layout']/table/tr[not(@class)]");

        for($k=1;$k<count($this->src);$k++){
            $srcHTML = new \DOMDocument();
            $src = $this->src[$k];
            $srcHTML->loadHTMLFile($src,LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
            $suiteNodes = (new \DOMXPath($srcHTML))->query("//div[@class='layout']/table/tr");
            $j=0;
            foreach($suiteNodes as $suiteNode){
                if($suiteNode->getAttribute('class') == ''){
                    //move to next reference node
                    $j++;
                    if($j > $refnodes->length-1) break;
                    continue;
                }
                //insert nodes before current reference node
                $suiteNode = $dstHTML->importNode($suiteNode, true);
                $table->insertBefore($suiteNode, $refnodes->item($j));
            }
        }

        /**
         * The next 5 functions correct our almost finished final report
         */
        $this->countSummary($dstHTML);
        $this->moveSummaryTable($dstHTML,$table);
        $this->updateSummaryTable($dstHTML);
        $this->updateToolbarTable($dstHTML);
        $this->updateButtons($dstHTML);

        //save final report
        file_put_contents($this->dst,$dstHTML->saveHTML());

        //return to initial statement
        libxml_use_internal_errors($this->previousLibXmlUseErrors);
    }

    /**
     * This function counts all types of tests' scenarios and writes in class members
     * @param $dstFile \DOMDocument - destination file
     */
    private function countSummary($dstFile){
        $tests = (new \DOMXPath($dstFile))->query("//table/tr[contains(@class,'scenarioRow')]");
        foreach($tests as $test){
            $class = str_replace('scenarioRow ', '', $test->getAttribute('class'));
            switch($class){
                case 'scenarioSuccess':
                    $this->countSuccess += 0.5;
                    break;
                case 'scenarioFailed':
                    $this->countFailed += 0.5;
                    break;
                case 'scenarioSkipped':
                    $this->countSkipped += 0.5;
                    break;
                case 'scenarioIncomplete':
                    $this->countIncomplete += 0.5;
                    break;
            }
        }
    }

    /**
     * This function updates values in Summary block for each type of scenarios
     * @param $dstFile \DOMDocument - destination file
     */
    private function updateSummaryTable($dstFile){
        $dstFile = new \DOMXPath($dstFile);
        $pathFor = function ($type) { return "//div[@id='stepContainerSummary']//td[@class='$type']";};
        $dstFile->query($pathFor('scenarioSuccessValue'))->item(0)->nodeValue = $this->countSuccess;
        $dstFile->query($pathFor('scenarioFailedValue'))->item(0)->nodeValue = $this->countFailed;
        $dstFile->query($pathFor('scenarioSkippedValue'))->item(0)->nodeValue = $this->countSkipped;
        $dstFile->query($pathFor('scenarioIncompleteValue'))->item(0)->nodeValue = $this->countIncomplete;
    }

    /**
     * This function moves Summary block in the bottom of result report
     * @param $dstFile \DOMDocument - destination file
     * @param $node \DOMNode - parent node of Summary table
     */
    private function moveSummaryTable($dstFile,$node){
        $summaryTable = (new \DOMXPath($dstFile))->query("//div[@id='stepContainerSummary']")
            ->item(0)->parentNode->parentNode;
        $node->appendChild($dstFile->importNode($summaryTable,true));
    }

    /**
     * This function updates values in Toolbar block for each type of scenarios (blue block on the left side of the report)
     * @param $dstFile \DOMDocument - destination file
     */
    private function updateToolbarTable($dstFile){
        $dstFile = new \DOMXPath($dstFile);
        $pathFor = function ($type) {return "//ul[@id='toolbar-filter']//a[@title='$type']";};
        $dstFile->query($pathFor('Successful'))->item(0)->nodeValue = '✔ '.$this->countSuccess;
        $dstFile->query($pathFor('Failed'))->item(0)->nodeValue = '✗ '.$this->countFailed;
        $dstFile->query($pathFor('Skipped'))->item(0)->nodeValue = 'S '.$this->countSkipped;
        $dstFile->query($pathFor('Incomplete'))->item(0)->nodeValue= 'I '.$this->countIncomplete;
    }

    /**
     * This function updates "+" and "-" button for viewing test steps in final report
     * @param $dstFile \DOMDocument - destination file
     */
    private function updateButtons($dstFile){
        $nodes = (new \DOMXPath($dstFile))->query("//div[@class='layout']/table/tr[contains(@class, 'scenarioRow')]");
        for($i=2;$i<$nodes->length;$i+=2){
            $n = $i/2 + 1;
            $p = $nodes->item($i)->childNodes->item(1)->childNodes->item(1);
            $table = $nodes->item($i+1)->childNodes->item(1)->childNodes->item(1);
            $p->setAttribute('onclick',"showHide('$n', this)");
            $table->setAttribute('id',"stepContainer".$n);
        }
    }

}


<?php

namespace ChristianKuri\LaravelFavorite\Traits;

use ChristianKuri\LaravelFavorite\Models\Favorite;
use Illuminate\Support\Facades\Auth;

/**
 * This file is part of Laravel Favorite,
 *
 * @license MIT
 * @package ChristianKuri/laravel-favorite
 *
 * Copyright (c) 2016 Christian Kuri
 */
trait Favoriteable
{
	/**
     * Define a polymorphic one-to-many relationship.
     *
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
     */
	public function favorites()
	{
		return $this->morphMany(Favorite::class, 'favoriteable');
	}

	/**
	 * Add this Object to the user favorites
	 *
	 * @param  int $user_id  [if  null its added to the auth user]
	 */
	public function addFavorite($user_id = null)
	{
		$favorite = new Favorite(['user_id' => ($user_id) ? $user_id : Auth::id()]);
		$this->favorites()->save($favorite);
	}

	/**
	 * Remove this Object from the user favorites
	 *
	 * @param  int $user_id  [if  null its added to the auth user]
	 *
	 */
	public function removeFavorite($user_id = null)
	{
		$this->favorites()->where('user_id', ($user_id) ? $user_id : Auth::id())->delete();
	}

	/**
	 * Toggle the favorite status from this Object
	 *
	 * @param  int $user_id  [if  null its added to the auth user]
	 */
	public function toggleFavorite($user_id = null)
	{
		$this->isFavorited($user_id) ? $this->removeFavorite($user_id) : $this->addFavorite($user_id) ;
	}

	/**
	 * Check if the user has favorited this Object
	 *
	 * @param  int $user_id  [if  null its added to the auth user]
	 * @return boolean
	 */
	public function isFavorited($user_id = null)
	{
		return $this->favorites()->where('user_id', ($user_id) ? $user_id : Auth::id())->exists();
	}

	/**
     * Return a collection with the Users who marked as favorite this Object.
     *
     * @return Collection
     */
	public function favoritedBy()
	{
		return $this->favorites()->with('user')->get()->mapWithKeys(function ($item) {
            return [$item['user']->id => $item['user']];
        });
	}

	/**
	 * Count the number of favorites
	 *
	 * @return int
	 */
	public function getFavoritesCountAttribute()
	{
		return $this->favorites()->count();
	}

	/**
	 * @return favoritesCount attribute
	 */
	public function favoritesCount()
	{
		return $this->favoritesCount;
	}
}


<?php

namespace Overblog\GraphiQLBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;

class OverblogGraphiQLExtension extends Extension
{
    /**
     * {@inheritdoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.xml');

        $container->setParameter('overblog_graphiql.endpoint_resolver', $config['endpoint_resolver']);

        $graphiQLViewConfigJSLibraries = $container->getDefinition('overblog_graphiql.view.config.javascript_libraries');
        $graphiQLViewConfigJSLibraries->setArguments([
            $config['javascript_libraries']['graphiql'],
            $config['javascript_libraries']['react'],
            $config['javascript_libraries']['fetch'],
        ]);

        $graphiQLViewConfig = $container->getDefinition('overblog_graphiql.view.config');
        $graphiQLViewConfig->setArguments([
            new Reference('overblog_graphiql.view.config.javascript_libraries'),
            $config['template'],
        ]);
    }

    public function getAlias()
    {
        return 'overblog_graphiql';
    }

    /**
     * {@inheritdoc}
     */
    public function getXsdValidationBasePath()
    {
        return __DIR__.'/../Resources/config/schema';
    }

    /**
     * {@inheritdoc}
     */
    public function getNamespace()
    {
        return 'http://over-blog.com/schema/dic/overblog_graphiql';
    }
}


<?php

namespace Bvipul\Generator\Controllers;

use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;

/**
 * Class Generator.
 *
 * @author Vipul Basapati <basapativipulkumar@gmail.com | https://github.com/bvipul>
 */
class Generator
{
    /**
     * Original Name
     */
    protected $originalName;

    /**
     * Module Name.
     */
    protected $module;

    /**
     * Files Object.
     */
    protected $files;

    /**
     * Directory Name.
     */
    protected $directory;

    /**
     * ----------------------------------------------------------------------------------------------
     * Model Related Files
     * -----------------------------------------------------------------------------------------------
     * 1. Model Name
     * 2. Attribute Name
     * 3. Relationship Name
     * 4. Attribute Namespace
     * 5. Relationship Namespace
     * 6. Traits directory
     * 7. Model Namespace.
     */
    protected $model;
    protected $attribute;
    protected $relationship;
    protected $attribute_namespace;
    protected $relationship_namespace;
    protected $trait_directory = 'Traits';
    protected $model_namespace = 'App\\Models\\';

    /**
     * Responses
     * 1. EditResponse Name
     * 2. CreateResponse Name
     * 3. EditResponse Namespace
     * 4. CreateResponse Namespace
     * 5. Responses Namespace
     */
    protected $edit_response = 'EditResponse';
    protected $create_response = 'CreateResponse';
    protected $edit_response_namespace;
    protected $create_response_namespace;
    protected $responses_namespace = 'App\\Http\\Responses\\';

    /**
     * Controllers
     * 1. Controlller Name
     * 2. Table Controller Name
     * 3. Controller Namespace
     * 4. Table Controller Namespace.
     */
    protected $controller;
    protected $table_controller;
    protected $controller_namespace = 'App\\Http\\Controllers\\';
    protected $table_controller_namespace = 'App\\Http\\Controllers\\';

    /**
     * Requests
     * 1. Edit Request Name
     * 2. Store Request Name
     * 3. Create Request Name
     * 4. Update Request Name
     * 5. Delete Request Name
     * 6. Manage Request Name
     * 7. Edit Request Namespace
     * 8. Store Request Namespace
     * 9. Manage Request Namespace
     * 10. Create Request Namespace
     * 11. Update Request Namespace
     * 12. Delete Request Namespace
     * 13. Request Namespace.
     */
    protected $edit_request;
    protected $store_request;
    protected $create_request;
    protected $update_request;
    protected $delete_request;
    protected $manage_request;
    protected $edit_request_namespace;
    protected $store_request_namespace;
    protected $manage_request_namespace;
    protected $create_request_namespace;
    protected $update_request_namespace;
    protected $delete_request_namespace;
    protected $request_namespace = 'App\\Http\\Requests\\';

    /**
     * Permissions
     * 1. Edit Permission
     * 2. Store Permission
     * 3. Manage Permission
     * 4. Create Permission
     * 5. Update Permission
     * 6. Delete Permission.
     */
    protected $edit_permission;
    protected $store_permission;
    protected $manage_permission;
    protected $create_permission;
    protected $update_permission;
    protected $delete_permission;

    /**
     * Routes
     * 1. Edit Route
     * 2. Store Route
     * 3. Manage Route
     * 4. Create Route
     * 5. Update Route
     * 6. Delete Route.
     */
    protected $edit_route;
    protected $store_route;
    protected $index_route;
    protected $create_route;
    protected $update_route;
    protected $delete_route;

    /**
     * Repository
     * 1. Repository Name
     * 2. Repository Namespace.
     */
    protected $repository;
    protected $repo_namespace = 'App\\Repositories\\';

    /**
     * Table Name.
     */
    protected $table;

    /**
     * Events.
     *
     * @var array
     */
    protected $events = [];

    /**
     * Route Path.
     */
    protected $route_path = 'routes\\Generator\\';

    /**
     * View Path.
     */
    protected $view_path = 'resources\\views\\';

    /**
     * Event Namespace.
     */
    protected $event_namespace = 'Backend\\';

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->files = new Filesystem();
    }

    /**
     * Initialization.
     *
     * @param array $input
     */
    public function initialize($input)
    {
        // Original Name entered
        $this->originalName = Str::title($input['name']);

        //Module
        $this->module = Str::camel($this->originalName);

        //Directory
        $this->directory = Str::camel(Str::singular(title_case($input['directory_name'])));

        //Model
        $this->model = Str::singular(Str::title($input['model_name']));

        //Table
        $this->table = strtolower($input['table_name']);

        //Controller
        $this->controller = Str::plural($this->model).'Controller';

        //Table Controller
        $this->table_controller = Str::plural($this->model).'TableController';

        //Attributes
        $this->attribute = $this->model.'Attribute';
        $this->attribute_namespace = $this->model_namespace;

        //Relationship
        $this->relationship = $this->model.'Relationship';
        $this->relationship_namespace = $this->model_namespace;

        //Repository
        $this->repository = $this->model.'Repository';

        //Requests
        $this->edit_request = 'Edit'.$this->model.'Request';
        $this->store_request = 'Store'.$this->model.'Request';
        $this->create_request = 'Create'.$this->model.'Request';
        $this->update_request = 'Update'.$this->model.'Request';
        $this->delete_request = 'Delete'.$this->model.'Request';
        $this->manage_request = 'Manage'.$this->model.'Request';

        //CRUD Options
        $this->edit = !empty($input['model_edit']) ? true : false;
        $this->create = !empty($input['model_create']) ? true : false;
        $this->delete = !empty($input['model_delete']) ? true : false;

        $model_singular = strtolower(Str::singular($this->model));

        //Permissions
        $this->edit_permission = 'edit-'.$model_singular;
        $this->store_permission = 'store-'.$model_singular;
        $this->manage_permission = 'manage-'.$model_singular;
        $this->create_permission = 'create-'.$model_singular;
        $this->update_permission = 'update-'.$model_singular;
        $this->delete_permission = 'delete-'.$model_singular;

        $model_plural = strtolower(Str::plural($this->model));

        //Routes
        $this->index_route = 'admin.'.$model_plural.'.index';
        $this->create_route = 'admin.'.$model_plural.'.create';
        $this->store_route = 'admin.'.$model_plural.'.store';
        $this->edit_route = 'admin.'.$model_plural.'.edit';
        $this->update_route = 'admin.'.$model_plural.'.update';
        $this->delete_route = 'admin.'.$model_plural.'.destroy';

        //Events
        $this->events = array_filter($input['event']);

        //Generate Namespaces
        $this->createNamespacesAndValues();
    }

    /**
     * @return void
     */
    private function createNamespacesAndValues()
    {
        //Model Namespace
        $this->model_namespace .= $this->getFullNamespace($this->model);

        //Controller Namespace
        $this->controller_namespace .= config('generator.controller_namespace').'\\'.$this->getFullNamespace($this->controller);

        //Table Controller Namespace
        $this->table_controller_namespace .= config('generator.controller_namespace').'\\'.$this->getFullNamespace($this->table_controller);

        //Attribute Namespace
        $this->attribute_namespace .= $this->getFullNamespace($this->attribute, $this->trait_directory);

        //Relationship Namespace
        $this->relationship_namespace .= $this->getFullNamespace($this->relationship, $this->trait_directory);

        //View Path
        $this->view_path .= config('generator.views_folder').'\\'.$this->getFullNamespace('');

        //Requests
        $this->request_namespace .= config('generator.request_namespace'). '\\';
        $this->edit_request_namespace = $this->request_namespace.$this->getFullNamespace($this->edit_request);
        $this->store_request_namespace = $this->request_namespace.$this->getFullNamespace($this->store_request);
        $this->manage_request_namespace = $this->request_namespace.$this->getFullNamespace($this->manage_request);
        $this->create_request_namespace = $this->request_namespace.$this->getFullNamespace($this->create_request);
        $this->update_request_namespace = $this->request_namespace.$this->getFullNamespace($this->update_request);
        $this->delete_request_namespace = $this->request_namespace.$this->getFullNamespace($this->delete_request);

        //Responses
        $this->responses_namespace .= config('generator.response_namespace'). '\\';

        $this->create_response_namespace = $this->responses_namespace.$this->getFullNamespace($this->create_response);
        $this->edit_response_namespace = $this->responses_namespace.$this->getFullNamespace($this->edit_response);

        //Repository Namespace
        $this->repo_namespace .= config('generator.request_namespace'). '\\' .$this->getFullNamespace($this->repository);

        //Events Namespace
        $this->event_namespace .= $this->getFullNamespace('');
    }

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

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

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

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

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

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

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

    /**
     * Return the permissions used in the module.
     *
     * @return array
     */
    public function getPermissions()
    {
        $permissions = [
            $this->manage_permission
        ];

        if ($this->create) {
            $permissions[] = $this->create_permission;
            $permissions[] = $this->store_permission;
        }
        if ($this->edit) {
            $permissions[] = $this->edit_permission;
            $permissions[] = $this->update_permission;
        }
        if ($this->delete) {
            $permissions[] = $this->delete_permission;
        }

        return $permissions;
    }

    /**
     * @return string
     */
    public function getFullNamespace($name, $inside_directory = null)
    {
        if (empty($name)) {
            return $this->directory;
        } elseif ($inside_directory) {
            return $this->directory.'\\'.$inside_directory.'\\'.$name;
        } else {
            return $this->directory.'\\'.$name;
        }
    }

    /**
     * @return void
     */
    public function createModel()
    {
        $this->createDirectory($this->getBasePath($this->attribute_namespace, true));
        //Generate Attribute File
        $this->generateFile('Attribute', [
            'AttributeNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->attribute_namespace)),
            'AttributeClass'     => $this->attribute,
            'editPermission'     => $this->edit_permission,
            'editRoute'          => $this->edit_route,
            'deletePermission'   => $this->delete_permission,
            'deleteRoute'        => $this->delete_route,
        ], lcfirst($this->attribute_namespace));

        //Generate Relationship File
        $this->generateFile('Relationship', [
            'RelationshipNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->relationship_namespace)),
            'RelationshipClass'     => $this->relationship,
        ], lcfirst($this->relationship_namespace));

        //Generate Model File
        $this->generateFile('Model', [
            'DummyNamespace'    => ucfirst($this->removeFileNameFromEndOfNamespace($this->model_namespace)),
            'DummyAttribute'    => $this->attribute_namespace,
            'DummyRelationship' => $this->relationship_namespace,
            'AttributeName'     => $this->attribute,
            'RelationshipName'  => $this->relationship,
            'DummyModel'        => $this->model,
            'table_name'        => $this->table,
        ], lcfirst($this->model_namespace));
    }

    /**
     * @return void
     */
    public function createDirectory($path)
    {
        $this->files->makeDirectory($path, 0777, true, true);
    }

    /**
     * @return void
     */
    public function createRequests()
    {
        $this->request_namespace .= $this->getFullNamespace('');
        $this->createDirectory($this->getBasePath($this->request_namespace));

        //Generate Manage Request File
        $this->generateFile('Request', [
                'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->manage_request_namespace)),
                'DummyClass'     => $this->manage_request,
                'permission'     => $this->manage_permission,
            ], lcfirst($this->manage_request_namespace));

        if ($this->create) {
            //Generate Create Request File
            $this->generateFile('Request', [
                    'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->create_request_namespace)),
                    'DummyClass'     => $this->create_request,
                    'permission'     => $this->create_permission,
                ], lcfirst($this->create_request_namespace));

            //Generate Store Request File
            $this->generateFile('Request', [
                    'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->store_request_namespace)),
                    'DummyClass'     => $this->store_request,
                    'permission'     => $this->store_permission,
                ], lcfirst($this->store_request_namespace));
        }

        if ($this->edit) {
            //Generate Edit Request File
            $this->generateFile('Request', [
                    'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->edit_request_namespace)),
                    'DummyClass'     => $this->edit_request,
                    'permission'     => $this->edit_permission,
                ], lcfirst($this->edit_request_namespace));

            //Generate Update Request File
            $this->generateFile('Request', [
                    'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->update_request_namespace)),
                    'DummyClass'     => $this->update_request,
                    'permission'     => $this->update_permission,
                ], lcfirst($this->update_request_namespace));
        }

        if ($this->delete) {
            //Generate Delete Request File
            $this->generateFile('Request', [
                    'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->delete_request_namespace)),
                    'DummyClass'     => $this->delete_request,
                    'permission'     => $this->delete_permission,
                ], lcfirst($this->delete_request_namespace));
        }
    }

    /**
     * @return void
     */
    public function createRepository()
    {
        $this->createDirectory($this->getBasePath($this->repo_namespace, true));
        //Getting stub file content
        $file_contents = $this->files->get($this->getStubPath().'Repository.stub');
        //If Model Create is checked
        if (!$this->create) {
            $file_contents = $this->delete_all_between('@startCreate', '@endCreate', $file_contents);
        } else {//If it isn't
            $file_contents = $this->delete_all_between('@startCreate', '@startCreate', $file_contents);
            $file_contents = $this->delete_all_between('@endCreate', '@endCreate', $file_contents);
        }
        //If Model Edit is Checked
        if (!$this->edit) {
            $file_contents = $this->delete_all_between('@startEdit', '@endEdit', $file_contents);
        } else {//If it isn't
            $file_contents = $this->delete_all_between('@startEdit', '@startEdit', $file_contents);
            $file_contents = $this->delete_all_between('@endEdit', '@endEdit', $file_contents);
        }
        //If Model Delete is Checked
        if (!$this->delete) {
            $file_contents = $this->delete_all_between('@startDelete', '@endDelete', $file_contents);
        } else {//If it isn't
            $file_contents = $this->delete_all_between('@startDelete', '@startDelete', $file_contents);
            $file_contents = $this->delete_all_between('@endDelete', '@endDelete', $file_contents);
        }
        //Replacements to be done in repository stub file
        $replacements = [
                'DummyNamespace'                => ucfirst($this->removeFileNameFromEndOfNamespace($this->repo_namespace)),
                'DummyModelNamespace'           => $this->model_namespace,
                'DummyRepoName'                 => $this->repository,
                'dummy_model_name'              => $this->model,
                'dummy_small_model_name'        => strtolower($this->model),
                'model_small_plural'            => strtolower(Str::plural($this->model)),
                'dummy_small_plural_model_name' => strtolower(Str::plural($this->model)),
        ];
        //Generating the repo file
        $this->generateFile(false, $replacements, lcfirst($this->repo_namespace), $file_contents);
    }

    /**
     * @return void
     */
    public function createResponses()
    {
        $this->responses_namespace .= $this->getFullNamespace('');
        $this->createDirectory($this->getBasePath($this->responses_namespace));

        if ($this->create) {
            //Generate CreateResponse File
            $this->generateFile('CreateResponse', [
                'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->create_response_namespace)),
                'dummy_small_plural_model' => strtolower(Str::plural($this->model)),
            ], lcfirst($this->create_response_namespace));
        }

        if ($this->edit) {
            //Generate EditResponse File
            $this->generateFile('EditResponse', [
                'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->edit_response_namespace)),
                'DummyModelNamespace'         => $this->model_namespace,
                'dummy_small_plural_model' => strtolower(Str::plural($this->model)),
            ], lcfirst($this->edit_response_namespace));
        }

        // if ($this->edit) {
        //     //Generate Create Request File
        //     $this->generateFile('Request', [
        //             'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->create_request_namespace)),
        //             'DummyClass'     => $this->create_request,
        //             'permission'     => $this->create_permission,
        //         ], lcfirst($this->create_request_namespace));

        //     //Generate Store Request File
        //     $this->generateFile('Request', [
        //             'DummyNamespace' => ucfirst($this->removeFileNameFromEndOfNamespace($this->store_request_namespace)),
        //             'DummyClass'     => $this->store_request,
        //             'permission'     => $this->store_permission,
        //         ], lcfirst($this->store_request_namespace));
        // }
    }

    /**
     * @return void
     */
    public function createController()
    {
        $this->createDirectory($this->getBasePath($this->controller_namespace, true));
        //Getting stub file content
        $file_contents = $this->files->get($this->getStubPath().'Controller.stub');
        //Replacements to be done in controller stub
        $replacements = [
            'DummyModelNamespace'         => $this->model_namespace,
            'DummyCreateResponseNamespace' => $this->create_response_namespace,
            'DummyEditResponseNamespace'   => $this->edit_response_namespace,
            'DummyModel'                  => $this->model,
            'DummyArgumentName'           => strtolower($this->model),
            'DummyManageRequestNamespace' => $this->manage_request_namespace,
            'DummyManageRequest'          => $this->manage_request,
            'DummyController'             => $this->controller,
            'DummyNamespace'              => ucfirst($this->removeFileNameFromEndOfNamespace($this->controller_namespace)),
            'DummyRepositoryNamespace'    => $this->repo_namespace,
            'dummy_repository'            => $this->repository,
            'dummy_small_plural_model'    => strtolower(Str::plural($this->model)),
        ];
        $namespaces = '';
        if (!$this->create) {
            $file_contents = $this->delete_all_between('@startCreate', '@endCreate', $file_contents);
        } else {
            $file_contents = $this->delete_all_between('@startCreate', '@startCreate', $file_contents);
            $file_contents = $this->delete_all_between('@endCreate', '@endCreate', $file_contents);

            //replacements
            $namespaces .= 'use '.$this->create_request_namespace.";\n";
            $namespaces .= 'use '.$this->store_request_namespace.";\n";
            $replacements['DummyCreateRequest'] = $this->create_request;
            $replacements['DummyStoreRequest'] = $this->store_request;
        }

        if (!$this->edit) {
            $file_contents = $this->delete_all_between('@startEdit', '@endEdit', $file_contents);
        } else {
            $file_contents = $this->delete_all_between('@startEdit', '@startEdit', $file_contents);
            $file_contents = $this->delete_all_between('@endEdit', '@endEdit', $file_contents);
            //replacements
            $namespaces .= 'use '.$this->edit_request_namespace.";\n";
            $namespaces .= 'use '.$this->update_request_namespace.";\n";
            $replacements['DummyEditRequest'] = $this->edit_request;
            $replacements['DummyUpdateRequest'] = $this->update_request;
        }

        if (!$this->delete) {
            $file_contents = $this->delete_all_between('@startDelete', '@endDelete', $file_contents);
        } else {
            $file_contents = $this->delete_all_between('@startDelete', '@startDelete', $file_contents);
            $file_contents = $this->delete_all_between('@endDelete', '@endDelete', $file_contents);
            //replacements
            $namespaces .= 'use '.$this->delete_request_namespace.";\n";
            $replacements['DummyDeleteRequest'] = $this->delete_request;
        }
        //Putting Namespaces in Controller
        $file_contents = str_replace('@Namespaces', $namespaces, $file_contents);

        $this->generateFile(false, $replacements, lcfirst($this->controller_namespace), $file_contents);
    }

    /**
     * @return void
     */
    public function createTableController()
    {
        $this->createDirectory($this->getBasePath($this->table_controller_namespace, true));
        //replacements to be done in table controller stub
        $replacements = [
            'DummyNamespace'              => ucfirst($this->removeFileNameFromEndOfNamespace($this->table_controller_namespace)),
            'DummyRepositoryNamespace'    => $this->repo_namespace,
            'DummyManageRequestNamespace' => $this->manage_request_namespace,
            'DummyTableController'        => $this->table_controller,
            'dummy_repository'            => $this->repository,
            'dummy_small_repo_name'       => strtolower($this->model),
            'dummy_manage_request_name'   => $this->manage_request,
        ];
        //generating the file
        $this->generateFile('TableController', $replacements, lcfirst($this->table_controller_namespace));
    }

    /**
     * @return void
     */
    public function createRouteFiles()
    {
        $this->createDirectory($this->getBasePath($this->route_path));

        if ($this->create && $this->edit && $this->delete) {//Then get the resourceRoute stub
            //Getting stub file content
            $file_contents = $this->files->get($this->getStubPath().'resourceRoute.stub');
            $file_contents = $this->delete_all_between('@startNamespace', '@startNamespace', $file_contents);
            $file_contents = $this->delete_all_between('@endNamespace', '@endNamespace', $file_contents);
            $file_contents = $this->delete_all_between('@startWithoutNamespace', '@endWithoutNamespace', $file_contents);
        } else {//Get the basic route stub
            //Getting stub file content
            $file_contents = $this->files->get($this->getStubPath().'route.stub');
            $file_contents = $this->delete_all_between('@startNamespace', '@startNamespace', $file_contents);
            $file_contents = $this->delete_all_between('@endNamespace', '@endNamespace', $file_contents);
            $file_contents = $this->delete_all_between('@startWithoutNamespace', '@endWithoutNamespace', $file_contents);
            //If create is checked
            if ($this->create) {
                $file_contents = $this->delete_all_between('@startCreate', '@startCreate', $file_contents);
                $file_contents = $this->delete_all_between('@endCreate', '@endCreate', $file_contents);
            } else {//If it isn't
                $file_contents = $this->delete_all_between('@startCreate', '@endCreate', $file_contents);
            }
            //If Edit is checked
            if ($this->edit) {
                $file_contents = $this->delete_all_between('@startEdit', '@startEdit', $file_contents);
                $file_contents = $this->delete_all_between('@endEdit', '@endEdit', $file_contents);
            } else {//if it isn't
                $file_contents = $this->delete_all_between('@startEdit', '@endEdit', $file_contents);
            }
            //If delete is checked
            if ($this->delete) {
                $file_contents = $this->delete_all_between('@startDelete', '@startDelete', $file_contents);
                $file_contents = $this->delete_all_between('@endDelete', '@endDelete', $file_contents);
            } else {//If it isn't
                $file_contents = $this->delete_all_between('@startDelete', '@endDelete', $file_contents);
            }
        }
        //Generate the Route file
        $this->generateFile(false, [
            'route_namespace'      => config('generator.controller_namespace'),
            'DummyModuleName'      => $this->module,
            'DummyModel'           => $this->directory,
            'dummy_name'           => strtolower(Str::plural($this->model)),
            'DummyController'      => $this->controller,
            'DummyTableController' => $this->table_controller,
            'dummy_argument_name'  => strtolower($this->model),
        ], $this->route_path.$this->model, $file_contents);

        //Routes web.php file
        $web_file = base_path('routes/web.php');
        //file_contents of Backend.php
        $file_contents = file_get_contents($web_file);
        //If this is already not there, then only append
        if (!strpos($file_contents, "includeRouteFiles(__DIR__.'/Generator/');")) {
            $content = "\n/*\n* Routes From Module Generator\n*/\nincludeRouteFiles(__DIR__.'/Generator/');";
            //Appending into web.php file
            file_put_contents($web_file, $content, FILE_APPEND);
        }
    }

    /**
     * This would enter the necessary language file contents to respective language files.
     *
     * @param [array] $input
     */
    public function insertToLanguageFiles()
    {
        //Model singular version
        $model_singular = Str::singular($this->originalName);
        //Model Plural version
        $model_plural = Str::plural($this->originalName);
        //Model Plural key
        $model_plural_key = strtolower(Str::plural($this->model));
        //Path to that language files
        $path = resource_path('lang'.DIRECTORY_SEPARATOR.'en');
        //config folder path
        $config_path = config_path('module.php');
        //Creating directory if it isn't
        $this->createDirectory($path);
        //Labels file
        $labels = [
            'create'     => "Create $model_singular",
            'edit'       => "Edit $model_singular",
            'management' => "$model_singular Management",
            'title'      => "$model_plural",

            'table' => [
                'id'        => 'Id',
                'createdat' => 'Created At',
            ],
        ];
        //Pushing values to labels
        add_key_value_in_file($path.'/labels.php', [$model_plural_key => $labels], 'backend');
        //Menus file
        $menus = [
            'all'        => "All $model_plural",
            'create'     => "Create $model_singular",
            'edit'       => "Edit $model_singular",
            'management' => "$model_singular Management",
            'main'       => "$model_plural",
        ];
        //Pushing to menus file
        add_key_value_in_file($path.'/menus.php', [$model_plural_key => $menus], 'backend');
        //Exceptions file
        $exceptions = [
            'already_exists' => "That $model_singular already exists. Please choose a different name.",
            'create_error'   => "There was a problem creating this $model_singular. Please try again.",
            'delete_error'   => "There was a problem deleting this $model_singular. Please try again.",
            'not_found'      => "That $model_singular does not exist.",
            'update_error'   => "There was a problem updating this $model_singular. Please try again.",
        ];
        //Alerts File
        $alerts = [
            'created' => "The $model_singular was successfully created.",
            'deleted' => "The $model_singular was successfully deleted.",
            'updated' => "The $model_singular was successfully updated.",
        ];
        //Pushing to menus file
        add_key_value_in_file($path.'/alerts.php', [$model_plural_key => $alerts], 'backend');
        //Pushing to exceptions file
        add_key_value_in_file($path.'/exceptions.php', [$model_plural_key => $exceptions], 'backend');
        //config file "module.php"
        $config = [
            $model_plural_key => [
                'table' => $this->table,
            ],
        ];
        //Pushing to config file
        add_key_value_in_file($config_path, $config);
    }

    /**
     * Creating View Files.
     *
     * @param array $input
     */
    public function createViewFiles()
    {
        //Getiing model
        $model = $this->model;
        //lowercase version of model
        $model_lower = strtolower($model);
        //lowercase and plural version of model
        $model_lower_plural = Str::plural($model_lower);
        //View folder name
        $view_folder_name = $model_lower_plural;
        //View path
        $path = escapeSlashes(strtolower(Str::plural($this->view_path)));
        //Header buttons folder
        $header_button_path = $path.DIRECTORY_SEPARATOR.'partials';
        //This would create both the directory name as well as partials inside of that directory
        $this->createDirectory(base_path($header_button_path));
        //Header button full path
        $header_button_file_path = $header_button_path.DIRECTORY_SEPARATOR."$model_lower_plural-header-buttons.blade";
        //Getting stub file content
        $header_button_contents = $this->files->get($this->getStubPath().'header-buttons.stub');
        if (!$this->create) {
            $header_button_contents = $this->delete_all_between('@create', '@endCreate', $header_button_contents);
        } else {
            $header_button_contents = $this->delete_all_between('@create', '@create', $header_button_contents);
            $header_button_contents = $this->delete_all_between('@endCreate', '@endCreate', $header_button_contents);
        }
        //Generate Header buttons file
        $this->generateFile(false, ['dummy_small_plural_model' => $model_lower_plural, 'dummy_small_model' => $model_lower], $header_button_file_path, $header_button_contents);
        //Index blade
        $index_path = $path.DIRECTORY_SEPARATOR.'index.blade';
        //Generate the Index blade file
        $this->generateFile('index_view', ['dummy_small_plural_model' => $model_lower_plural], $index_path);
        //Create Blade
        if ($this->create) {
            //Create Blade
            $create_path = $path.DIRECTORY_SEPARATOR.'create.blade';
            //Generate Create Blade
            $this->generateFile('create_view', ['dummy_small_plural_model' => $model_lower_plural, 'dummy_small_model' => $model_lower], $create_path);
        }
        //Edit Blade
        if ($this->edit) {
            //Edit Blade
            $edit_path = $path.DIRECTORY_SEPARATOR.'edit.blade';
            //Generate Edit Blade
            $this->generateFile('edit_view', ['dummy_small_plural_model' => $model_lower_plural, 'dummy_small_model' => $model_lower], $edit_path);
        }
        //Form Blade
        if ($this->create || $this->edit) {
            //Form Blade
            $form_path = $path.DIRECTORY_SEPARATOR.'form.blade';
            //Generate Form Blade
            $this->generateFile('form_view', [], $form_path);
        }
        //BreadCrumbs Folder Path
        $breadcrumbs_path = escapeSlashes('app\\Http\\Breadcrumbs\\Backend');
        //Breadcrumb File path
        $breadcrumb_file_path = $breadcrumbs_path.DIRECTORY_SEPARATOR.$this->model;
        //Generate BreadCrumb File
        $this->generateFile('Breadcrumbs', ['dummy_small_plural_model' => $model_lower_plural], $breadcrumb_file_path);
        //Backend File of Breadcrumb
        $breadcrumb_backend_file = $breadcrumbs_path.DIRECTORY_SEPARATOR.'Backend.php';
        //file_contents of Backend.php
        $file_contents = file_get_contents(base_path($breadcrumb_backend_file));
        //If this is already not there, then only append
        if (!strpos($file_contents, "require __DIR__.'/$this->model.php';")) {
            //Appending into BreadCrumb backend file
            file_put_contents(base_path($breadcrumb_backend_file), "\nrequire __DIR__.'/$this->model.php';", FILE_APPEND);
        }
    }

    /**
     * Creating Table File.
     *
     * @param array $input
     */
    public function createMigration()
    {
        $table = $this->table;

        if (Schema::hasTable($table)) {
            return 'Table Already Exists!';
        } else {
            //Calling Artisan command to create table
            Artisan::call('make:migration', [
                'name'     => 'create_'.$table.'_table',
                '--create' => $table,
            ]);

            return Artisan::output();
        }
    }

    /**
     * Creating Event Files.
     *
     * @param array $input
     */
    public function createEvents()
    {
        if (!empty($this->events)) {
            $base_path = $this->event_namespace;

            foreach ($this->events as $event) {
                $path = escapeSlashes($base_path.DIRECTORY_SEPARATOR.$event);
                $model = str_replace(DIRECTORY_SEPARATOR, '\\', $path);

                Artisan::call('make:event', [
                    'name' => $model,
                ]);

                Artisan::call('make:listener', [
                    'name'    => $model.'Listener',
                    '--event' => $model,
                ]);
            }
        }
    }

    /**
     * Generating the file by
     * replacing placeholders in stub file.
     *
     * @param $stub_name Name of the Stub File
     * @param $replacements [array] Array of the replacement to be done in stub file
     * @param $file [string] full path of the file
     * @param $contents [string][optional] file contents
     */
    public function generateFile($stub_name, $replacements, $file, $contents = null)
    {
        if ($stub_name) {
            //Getting the Stub Files Content
            $file_contents = $this->files->get($this->getStubPath().$stub_name.'.stub');
        } else {
            //Getting the Stub Files Content
            $file_contents = $contents;
        }
        //Replacing the stub
        $file_contents = str_replace(
            array_keys($replacements),
            array_values($replacements),
            $file_contents
        );
        $this->files->put(base_path(escapeSlashes($file)).'.php', $file_contents);
    }

    /**
     * getting the stub folder path.
     *
     * @return string
     */
    public function getStubPath()
    {
        $path = resource_path('views'.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'generator'.DIRECTORY_SEPARATOR.'Stubs'.DIRECTORY_SEPARATOR);
        $package_stubs_path = base_path('vendor'.DIRECTORY_SEPARATOR.'bvipul'.DIRECTORY_SEPARATOR.'generator'.DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.'Stubs'.DIRECTORY_SEPARATOR);
        if($this->files->exists($path))
            return $path;
        else
            return $package_stubs_path;
    }

    /**
     * getBasePath
     *
     * @param string $namespace
     * @param bool $status
     * @return string
     */
    public function getBasePath($namespace, $status = false)
    {
        if ($status) {
            return base_path(escapeSlashes($this->removeFileNameFromEndOfNamespace($namespace, $status)));
        }

        return base_path(lcfirst(escapeSlashes($namespace)));
    }

    /**
     * Removes the filename from the passed the namespace
     *
     * @param string $namespace
     * @return string
     */
    public function removeFileNameFromEndOfNamespace($namespace)
    {
        $namespace = explode('\\', $namespace);

        unset($namespace[count($namespace) - 1]);

        return lcfirst(implode('\\', $namespace));
    }

    /**
     * Modify strings by removing content between $beginning and $end.
     *
     * @param string $beginning
     * @param string $end
     * @param string $string
     *
     * @return string
     */
    public function delete_all_between($beginning, $end, $string)
    {
        $beginningPos = strpos($string, $beginning);
        $endPos = strpos($string, $end);
        if ($beginningPos === false || $endPos === false) {
            return $string;
        }

        $textToDelete = substr($string, $beginningPos, ($endPos + strlen($end)) - $beginningPos);

        return str_replace($textToDelete, '', $string);
    }
}


<?php

namespace Bvipul\Generator\Repositories;

use Bvipul\Generator\Module;
use App\Exceptions\GeneralException;
use App\Repositories\BaseRepository;
use App\Models\Access\Permission\Permission;

/**
 * Class ModuleRepository.
 */
class ModuleRepository extends BaseRepository
{
    /**
     * Associated Repository Model.
     */
    const MODEL = Module::class;

    /**
     * @return mixed
     */
    public function getForDataTable()
    {
        return $this->query()
            ->leftjoin('users', 'users.id', '=', 'modules.created_by')
            ->select([
                'modules.id',
                'modules.name',
                'modules.url',
                'modules.view_permission_id',
                'modules.created_by',
                'modules.updated_by',
                'users.first_name as created_by',
            ]);
    }

    /**
     * @param array $input
     *
     * @throws GeneralException
     *
     * @return bool
     */
    public function create(array $input, array $permissions)
    {
        $module = Module::where('name', $input['name'])->first();

        if (!$module) {
            $name = $input['model_name'];
            $model = strtolower($name);

            foreach ($permissions as $permission) {
                $perm = [
                    'name'         => $permission,
                    'display_name' => title_case(str_replace('-', ' ', $permission)).' Permission',
                ];
                //Creating Permission
                $per = Permission::firstOrCreate($perm);
            }

            $mod = [
                'view_permission_id' => "view-$model-permission",
                'name'               => $input['name'],
                'url'                => 'admin.'.str_plural($model).'.index',
                'created_by'         => access()->user()->id,
            ];

            $create = Module::create($mod);

            return $create;
        }
        else {
            return $module;
        }

        throw new GeneralException('There was some error in creating the Module. Please Try Again.');
    }
}


<?php

namespace Bvipul\Generator\Provider;

use Illuminate\Support\ServiceProvider;

class CrudGeneratorServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->loadViewsFrom(__DIR__.'/../views', 'generator');
        $this->loadMigrationsFrom(__DIR__.'/../migrations');
        $this->loadTranslationsFrom(__DIR__.'/../lang', 'generator');
        $this->mergeConfigFrom(__DIR__ . '/../config/generator.php', 'generator');

        $this->publishes([
            __DIR__ . '/../config/generator.php' => config_path('generator.php'),
        ], 'generator');
        $this->publishes([
            __DIR__.'/../views' => base_path('resources/views/vendor/generator'),
        ], 'generator_views');

        // Load the Breadcrumbs for the package
        if (class_exists('Breadcrumbs')) {
            require __DIR__ . '/../breadcrumbs.php';
        }
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        include __DIR__.'/../routes.php';
        require_once(__DIR__.'/../helpers.php');
        $this->app->make('Bvipul\Generator\Module');
        $this->app->make('Bvipul\Generator\Controllers\Generator');
        $this->app->make('Bvipul\Generator\Controllers\ModuleController');
        $this->app->make('Bvipul\Generator\Repositories\ModuleRepository');
        $this->app->make('Bvipul\Generator\Controllers\ModuleTableController');
    }
}