/** * 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(); }
/** * Will load any given structure based on it's availability in our structure map which depends on the configured * project directories. * If the structure cannot be found we will redirect to the composer autoloader which we registered as a fallback * * @param string $className The name of the structure we will try to load * * @return boolean */ public function loadClass($className) { // Might the class be a omitted one? If so we can require the original. if ($this->config->hasValue('autoloader/omit')) { $omittedNamespaces = $this->config->getValue('autoloader/omit'); foreach ($omittedNamespaces as $omitted) { // If our class name begins with the omitted part e.g. it's namespace if (strpos($className, str_replace('\\\\', '\\', $omitted)) === 0) { return false; } } } // Do we have the file in our cache dir? If we are in development mode we have to ignore this. if ($this->config->getValue('environment') !== 'development') { $cachePath = $this->config->getValue('cache/dir') . DIRECTORY_SEPARATOR . str_replace('\\', '_', $className) . '.php'; if (is_readable($cachePath)) { $res = fopen($cachePath, 'r'); $str = fread($res, 384); $success = preg_match('/' . Dictionaries\Placeholders::ORIGINAL_PATH_HINT . '(.+)' . Dictionaries\Placeholders::ORIGINAL_PATH_HINT . '/', $str, $tmp); if ($success > 0) { $tmp = explode('#', $tmp[1]); $path = $tmp[0]; $mTime = $tmp[1]; if (filemtime($path) == $mTime) { // the cached file is recent, load it require $cachePath; return true; } } } } // If we are loading something that the autoloader needs to function, then we have to skip to composer if (strpos($className, 'AppserverIo\\Doppelgaenger') === 0 && strpos($className, 'AppserverIo\\Doppelgaenger\\Tests') === false || strpos($className, 'PHP') === 0 || strpos($className, 'AppserverIo\\Psr\\MetaobjectProtocol') === 0 || strpos($className, 'AppserverIo\\Lang\\') === 0) { return false; } // If the structure map did not get filled by now we will do so here if ($this->structureMap->isEmpty()) { $this->structureMap->fill(); } // Get the file from the map $file = $this->structureMap->getEntry($className); // Did we get something? If not return false. if ($file === false) { return false; } // We are still here, so we know the class and it is not omitted. Does it contain annotations then? if (!$file->hasAnnotations() || !$file->isEnforced()) { // on un-enforced classes we will require the original require $file->getPath(); return true; } // So we have to create a new class definition for this original class. // Get a current cache instance if we do not have one already. if ($this->cache === null) { // We also require the classes of our maps as we do not have proper autoloading in place $this->cache = new CacheMap($this->getConfig()->getValue('cache/dir'), array(), $this->config); } $this->generator = new Generator($this->structureMap, $this->cache, $this->config, $this->aspectRegister); // Create the new class definition if ($this->generator->create($file, $this->config->getValue('enforcement/contract-inheritance')) === true) { // Require the new class, it should have been created now $file = $this->generator->getFileName($className); if ($file !== false && is_readable($file) === true) { require $file; return true; } } else { return false; } // Still here? That sounds like bad news! return false; }
/** * 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; }