コード例 #1
0
 /**
  * Test the extendValue() method
  *
  * @return void
  */
 public function testExtendValue()
 {
     // Get our config
     $config = new Config();
     // Test string concatination
     $config->extendValue('environment', 'test');
     $this->assertEquals('productiontest', $config->getValue('environment'));
     // Test array extension
     $config->extendValue('enforcement/omit', array('Tests'));
     $this->assertEquals(array('PHPUnit', 'Psr\\Log', 'PHP', 'Tests'), $config->getValue('enforcement/omit'));
 }
コード例 #2
0
 /**
  * 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();
 }
コード例 #3
0
 /**
  * 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;
 }
コード例 #4
0
 /**
  * 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();
 }
コード例 #5
0
 *
 * @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();
new \AppserverIo\Doppelgaenger\Tests\Data\Aspects\MainAspectTestClass();
コード例 #6
0
 /**
  * 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;
 }
コード例 #7
0
 /**
  * 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;
 }
コード例 #8
0
 /**
  * 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;
 }
コード例 #9
0
 /**
  * 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);
 }