/** * Set upt the test environment * * @return null */ public function setUp() { $this->dataDirs = array(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'Data'); // get the objects we need $config = new Config(); $config->setValue('autoload/dirs', $this->dataDirs); $config->setValue('enforcement/dirs', array()); $this->structureMap = new StructureMap($config->getValue('autoload/dirs'), $config->getValue('enforcement/dirs'), $config); // fill the map $this->structureMap->fill(); }
/** * Will register our autoloading method at the beginning of the spl autoloader stack * * @param boolean $throw Should we throw an exception on error? * @param boolean $prepend If you want to NOT prepend you might, but you should not * * @return null */ public function register($throw = true, $prepend = true) { // Now we have a config no matter what, we can store any instance we might need $this->config->storeInstances(); // We want to let our autoloader be the first in line so we can react on loads // and create/return our contracted definitions. // So lets use the prepend parameter here. spl_autoload_register(array($this, self::OUR_LOADER), $throw, $prepend); }
/** * Will get the conditions for a certain assertion indicating keyword like @requires or, if configured, @param * * @param string $docBlock The DocBlock to search in * @param string $conditionKeyword The keyword we are searching for, use assertion defining tags here! * @param boolean|null $privateContext If we have to mark the parsed annotations as having a private context * as we would have trouble finding out for ourselves. * * @return boolean|\AppserverIo\Doppelgaenger\Entities\Lists\AssertionList */ public function getConditions($docBlock, $conditionKeyword, $privateContext = null) { // There are only 3 valid condition types if ($conditionKeyword !== Requires::ANNOTATION && $conditionKeyword !== Ensures::ANNOTATION && $conditionKeyword !== Invariant::ANNOTATION) { return false; } // get the annotations for the passed condition keyword $annotations = $this->getAnnotationsByType($docBlock, $conditionKeyword); // if we have to enforce basic type safety we need some more annotations if ($this->config->getValue('enforcement/enforce-default-type-safety') === true) { // lets switch the switch ($conditionKeyword) { case Ensures::ANNOTATION: // we have to consider @return annotations as well $annotations = array_merge($annotations, $this->getAnnotationsByType($docBlock, 'return')); break; case Requires::ANNOTATION: // we have to consider @param annotations as well $annotations = array_merge($annotations, $this->getAnnotationsByType($docBlock, 'param')); break; default: break; } } // lets build up the result array $assertionFactory = new AssertionFactory(); $result = new AssertionList(); foreach ($annotations as $annotation) { // try to create assertion instances for all annotations try { $assertion = $assertionFactory->getInstance($annotation); } catch (\Exception $e) { error_log($e->getMessage()); continue; } if ($assertion !== false) { // Do we already got a private context we can set? If not we have to find out four ourselves if ($privateContext !== null) { // Add the context (wether private or not) $assertion->setPrivateContext($privateContext); } else { // Add the context (private or not) $this->determinePrivateContext($assertion); } // finally determine the minimal scope of this assertion and add it to our result $this->determineMinimalScope($assertion); $result->add($assertion); } } return $result; }
/** * Visitor method that registers the class loaders in the application. * * @param \AppserverIo\Psr\Application\ApplicationInterface $application The application instance to register the class loader with * @param \AppserverIo\Appserver\Core\Api\Node\ClassLoaderNodeInterface $configuration The class loader configuration * * @return void */ public static function visit(ApplicationInterface $application, ClassLoaderNodeInterface $configuration) { // load the web application path we want to register the class loader for $webappPath = $application->getWebappPath(); // initialize the class path and the enforcement directories $classPath = array(); $enforcementDirs = array(); // add the possible class path if folder is available foreach ($configuration->getDirectories() as $directory) { if (is_dir($webappPath . $directory->getNodeValue())) { array_push($classPath, $webappPath . $directory->getNodeValue()); if ($directory->isEnforced()) { array_push($enforcementDirs, $webappPath . $directory->getNodeValue()); } } } // initialize the arrays of different omit possibilities $omittedEnforcement = array(); $omittedAutoLoading = array(); // iterate over all namespaces and check if they are omitted in one or the other way foreach ($configuration->getNamespaces() as $namespace) { // is the enforcement omitted for this namespace? if ($namespace->omitEnforcement()) { $omittedEnforcement[] = $namespace->getNodeValue()->__toString(); } // is the autoloading omitted for this namespace? if ($namespace->omitAutoLoading()) { $omittedAutoLoading[] = $namespace->getNodeValue()->__toString(); } } // initialize the class loader configuration $config = new Config(); // set the environment mode we want to use $config->setValue('environment', $configuration->getEnvironment()); // set the cache directory $config->setValue('cache/dir', $application->getCacheDir()); // set the default autoloader values $config->setValue('autoloader/dirs', $classPath); // collect the omitted namespaces (if any) $config->setValue('autoloader/omit', $omittedAutoLoading); $config->setValue('enforcement/omit', $omittedEnforcement); // set the default enforcement configuration values $config->setValue('enforcement/dirs', $enforcementDirs); $config->setValue('enforcement/enforce-default-type-safety', $configuration->getTypeSafety()); $config->setValue('enforcement/processing', $configuration->getProcessing()); $config->setValue('enforcement/level', $configuration->getEnforcementLevel()); $config->setValue('enforcement/logger', $application->getInitialContext()->getSystemLogger()); // create a autoloader instance and initialize it $classLoader = new DgClassLoader($config); $classLoader->init(new StackableStructureMap($config->getValue('autoloader/dirs'), $config->getValue('enforcement/dirs'), $config), new AspectRegister()); // add the autoloader to the manager $application->addClassLoader($classLoader, $configuration); }
/** * Will generate the structure map within the specified root path. * * @return void */ protected function generate() { // first of all we will get the version, so we will later know about changes made DURING indexing $this->version = $this->findVersion(); // get the iterator over our project files and create a regex iterator to filter what we got $recursiveIterator = $this->getProjectIterator(); $regexIterator = new \RegexIterator($recursiveIterator, '/^.+\\.php$/i', \RecursiveRegexIterator::GET_MATCH); // get the list of enforced files $enforcedFiles = $this->getEnforcedFiles(); // if we got namespaces which are omitted from enforcement we have to mark them as such $omittedNamespaces = array(); if ($this->config->hasValue('enforcement/omit')) { $omittedNamespaces = $this->config->getValue('enforcement/omit'); } // iterator over our project files and add array based structure representations foreach ($regexIterator as $file) { // get the identifiers if any. $identifier = $this->findIdentifier($file[0]); // if we got an identifier we can build up a new map entry if ($identifier !== false) { // We need to get our array of needles $needles = array(Invariant::ANNOTATION, Ensures::ANNOTATION, Requires::ANNOTATION, After::ANNOTATION, AfterReturning::ANNOTATION, AfterThrowing::ANNOTATION, Around::ANNOTATION, Before::ANNOTATION, Introduce::ANNOTATION, Pointcut::ANNOTATION); // If we have to enforce things like @param or @returns, we have to be more sensitive if ($this->config->getValue('enforcement/enforce-default-type-safety') === true) { $needles[] = '@var'; $needles[] = '@param'; $needles[] = '@return'; } // check if the file has contracts and if it should be enforced $hasAnnotations = $this->findAnnotations($file[0], $needles); // create the entry $this->map[$identifier[1]] = array('cTime' => filectime($file[0]), 'identifier' => $identifier[1], 'path' => $file[0], 'type' => $identifier[0], 'hasAnnotations' => $hasAnnotations, 'enforced' => $this->isFileEnforced($file[0], $identifier[1], $hasAnnotations, $enforcedFiles, $omittedNamespaces)); } } // save for later reuse. $this->save(); }
* PHP version 5 * * @author Bernhard Wick <*****@*****.**> * @copyright 2015 TechDivision GmbH - <*****@*****.**> * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * @link https://github.com/appserver-io/doppelgaenger * @link http://www.appserver.io/ */ use AppserverIo\Doppelgaenger\Config; // Get the vendor dir $vendorDir = __DIR__ . "/vendor"; // Include the composer autoloader as a fallback $loader = (require $vendorDir . DIRECTORY_SEPARATOR . 'autoload.php'); $loader->add('AppserverIo\\Doppelgaenger', array(__DIR__ . '/src', __DIR__ . '/tests')); // Load the test config file $config = new Config(); $config->load(__DIR__ . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'Tests' . DIRECTORY_SEPARATOR . 'Data' . DIRECTORY_SEPARATOR . 'tests.conf.json'); // We have to register our autoLoader to put our proxies in place $autoLoader = new AppserverIo\Doppelgaenger\AutoLoader($config); $autoLoader->register(); // we should clear the cache directory from the result of former runs $cacheDir = $config->getValue('cache/dir'); foreach (scandir($cacheDir) as $cachedFile) { // clean the files but do not delete the .gitignore file if ($cachedFile === '.gitignore' || $cachedFile === '.' || $cachedFile === '..') { continue; } unlink($cacheDir . DIRECTORY_SEPARATOR . $cachedFile); } // make the aspects known new \AppserverIo\Doppelgaenger\Tests\Data\Aspects\PointcutReferencingTestAspect();
/** * Default constructor * * @param string $file The path of the file we want to parse * @param \AppserverIo\Doppelgaenger\Config $config Configuration * @param StructureDefinitionHierarchy $structureDefinitionHierarchy List of already parsed structures * @param \AppserverIo\Doppelgaenger\StructureMap|null $structureMap Our structure map instance * @param StructureDefinitionInterface|null $currentDefinition The current definition we are working on * @param array $tokens The array of tokens taken from the file * * @throws \AppserverIo\Doppelgaenger\Exceptions\ParserException */ public function __construct($file, Config $config, StructureDefinitionHierarchy $structureDefinitionHierarchy = null, StructureMap $structureMap = null, StructureDefinitionInterface $currentDefinition = null, array $tokens = array()) { $this->config = $config; if (empty($tokens)) { // Check if we can use the file if (!is_readable($file)) { throw new ParserException(sprintf('Could not read input file %s', $file)); } // Get all the tokens and count them $this->tokens = token_get_all(file_get_contents($file)); } else { $this->tokens = $tokens; } // We need the file saved $this->file = $file; // We also need the token count $this->tokenCount = count($this->tokens); $this->currentDefinition = $currentDefinition; $this->structureMap = is_null($structureMap) ? new StructureMap($config->getValue('autoloader/dirs'), $config->getValue('enforcement/dirs'), $config) : $structureMap; $this->structureDefinitionHierarchy = is_null($structureDefinitionHierarchy) ? new StructureDefinitionHierarchy() : $structureDefinitionHierarchy; }
/** * Test the unsetValue() method * * @return void */ public function testUnsetValue() { // Get our config $config = new Config(); // Unset some values and test if they do not exist anymore $this->assertTrue($config->hasValue('environment')); $config->unsetValue('environment'); $this->assertFalse($config->hasValue('environment')); // Do the same for a more "complex" index $this->assertTrue($config->hasValue('enforcement/enforce-default-type-safety')); $config->unsetValue('enforcement/enforce-default-type-safety'); $this->assertFalse($config->hasValue('enforcement/enforce-default-type-safety')); }
/** * Will append all needed filters based on the enforcement level stated in the configuration file. * * @param resource $res The resource we will append the filters to * @param \AppserverIo\Doppelgaenger\Interfaces\StructureDefinitionInterface $structureDefinition Structure definition providing needed information * * @return array */ protected function appendDefaultFilters(&$res, StructureDefinitionInterface $structureDefinition) { // resulting array with resources of appended filters $filters = array(); // Lets get the enforcement level $levelArray = array(); if ($this->config->hasValue('enforcement/level')) { $levelArray = array_reverse(str_split(decbin($this->config->getValue('enforcement/level')))); } // Whatever the enforcement level is, we will always need the skeleton filter. $filters['SkeletonFilter'] = $this->appendFilter($res, 'AppserverIo\\Doppelgaenger\\StreamFilters\\SkeletonFilter', $structureDefinition); // Now lets register and append the filters if they are mapped to a 1 // Lets have a look at the precondition filter first if (isset($levelArray[0]) && $levelArray[0] == 1) { // Do we even got any preconditions? $filterNeeded = false; $iterator = $structureDefinition->getFunctionDefinitions()->getIterator(); foreach ($iterator as $functionDefinition) { if ($functionDefinition->getAllPreconditions()->count() !== 0) { $filterNeeded = true; break; } } if ($filterNeeded) { $filters['PreconditionFilter'] = $this->appendFilter($res, 'AppserverIo\\Doppelgaenger\\StreamFilters\\PreconditionFilter', $structureDefinition->getFunctionDefinitions()); } } // What about the post-condition filter? if (isset($levelArray[1]) && $levelArray[1] == 1) { // Do we even got any post-conditions? $filterNeeded = false; $iterator = $structureDefinition->getFunctionDefinitions()->getIterator(); foreach ($iterator as $functionDefinition) { if ($functionDefinition->getAllPostconditions()->count() !== 0) { $filterNeeded = true; break; } } if ($filterNeeded) { $filters['PostconditionFilter'] = $this->appendFilter($res, 'AppserverIo\\Doppelgaenger\\StreamFilters\\PostconditionFilter', $structureDefinition->getFunctionDefinitions()); } } // What about the invariant filter? if (isset($levelArray[2]) && $levelArray[2] == 1) { // Do we even got any invariants? if ($structureDefinition->getInvariants()->count(true) !== 0) { $filters['InvariantFilter'] = $this->appendFilter($res, 'AppserverIo\\Doppelgaenger\\StreamFilters\\InvariantFilter', $structureDefinition); } } // introductions make only sense for classes if ($structureDefinition instanceof ClassDefinition) { // add the filter used for introductions $filters['IntroductionFilter'] = $this->appendFilter($res, 'AppserverIo\\Doppelgaenger\\StreamFilters\\IntroductionFilter', $structureDefinition->getIntroductions()); } // add the filter we need for our AOP advices $filters['AdviceFilter'] = $this->appendFilter($res, 'AppserverIo\\Doppelgaenger\\StreamFilters\\AdviceFilter', array('functionDefinitions' => $structureDefinition->getFunctionDefinitions(), 'aspectRegister' => $this->aspectRegister)); // add the filter used to proxy to the actual implementation $filters['ProcessingFilter'] = $this->appendFilter($res, 'AppserverIo\\Doppelgaenger\\StreamFilters\\ProcessingFilter', $structureDefinition->getFunctionDefinitions()); // We ALWAYS need the enforcement filter. Everything else would not make any sense $filters['EnforcementFilter'] = $this->appendFilter($res, 'AppserverIo\\Doppelgaenger\\StreamFilters\\EnforcementFilter', array('structureDefinition' => $structureDefinition, 'config' => $this->config)); // at last we want to make the output beatiful and detect sysntax errors // $filters['BeautifyFilter'] = $this->appendFilter($res, 'AppserverIo\Doppelgaenger\StreamFilters\BeautifyFilter', array()); return $filters; }
/** * Will test if a configuration using regexed paths can be used properly * * @return void */ public function testRegexMappingWithoutException() { // We have to load the config for regular expressions in the project dirs $config = new Config(); $config->load(str_replace(DIRECTORY_SEPARATOR . 'Functional', '', __DIR__) . DIRECTORY_SEPARATOR . 'Data' . DIRECTORY_SEPARATOR . 'RegexTest' . DIRECTORY_SEPARATOR . 'regextest.conf.json'); $e = null; try { $regexTestClass1 = new RegexTestClass1(); } catch (\Exception $e) { } // Did we get the right $e? $this->assertNull($e); $e = null; try { $regexTestClass2 = new RegexTestClass2(); } catch (\Exception $e) { } // Did we get the right $e? $this->assertNull($e); $e = null; try { $regexTestClass = new RegexTestClass(); } catch (\Exception $e) { } // Did we get the right $e? $this->assertNull($e); }