Beispiel #1
0
 /**
  * Get the singleton instance.
  *
  * @param string $identifier The identifier (string), 
  * @param static [$instance] New value for the identifier.
  *
  * @return static
  */
 public static function instance($identifier = 'default')
 {
     if ($identifier instanceof static) {
         // Given identfier already is an instance?
         return $identifier;
     }
     if (array_key_exists($identifier, static::$instances) === false) {
         if ($identifier === 'default') {
             $instance = static::defaultInstance();
             static::$instances['default'] = $instance;
             return $instance;
         }
         throw new InfoException(static::class . '::instances["' . $identifier . '"] is not configured', 'Available instances: ' . \Sledgehammer\quoted_human_implode(' or ', array_keys(static::$instances)));
     }
     $connection = static::$instances[$identifier];
     if ($identifier instanceof static) {
         return $identifier;
     }
     if (is_string($connection)) {
         // A reference to another instance?
         return static::instance($connection);
     }
     if (is_array($connection) || get_class($connection) === 'Closure') {
         // A callback which creates the instance?
         static::$instances[$identifier] = call_user_func($connection);
         return static::instance($identifier);
     }
     return $connection;
 }
 /**
  * Controleer of de php extenties geinstalleerd zijn.
  */
 public function test_missing_extentions()
 {
     if (!function_exists('token_get_all')) {
         $this->fail('PHP extention "tokenizer" is required for this UnitTest');
         return;
     }
     $whitelist = array();
     if ($this->onlyClassesFolder) {
         // Alleen de classes mappen van de modules inlezen
         $modules = Framework::getModules();
         foreach ($modules as $module) {
             $this->checkFolder($module['path'] . 'classes/');
         }
     } else {
         // check all php files within $path
         $this->checkFolder(\Sledgehammer\PATH);
     }
     foreach ($this->missingExtensions as $extension => $definition) {
         $files = $this->extensionUsedIn[$extension];
         foreach ($files as $i => $filename) {
             if (isset($whitelist[$extension]) && in_array($filename, $whitelist[$extension])) {
                 unset($files[$i]);
             }
         }
         if (count($files) > 0) {
             $this->fail('Missing php extension "' . $extension . '". Function or class "' . $definition . '" is used in ' . \Sledgehammer\quoted_human_implode(' and', $files));
         }
     }
     $this->assertTrue(true, 'All required extenstion are installed');
 }
Beispiel #3
0
 /**
  * Convert raw backend data into an object instance.
  *
  * @param mixed       $data
  * @param ModelConfig $config
  * @param string|null $index  (optional) speedoptim: Prevents resolving the index again.
  * @param bool        $reload true: Overwrite properties in the instance.
  *
  * @return stdClass
  */
 protected function convertToInstance($data, $config, $index = null, $reload = false)
 {
     if ($index === null) {
         $index = $this->resolveIndex($data, $config);
     } elseif (empty($this->objects[$config->name][$index])) {
         throw new Exception('Invalid index: "' . $index . '" for ' . $config->name);
     }
     if ($reload) {
         $instance = $this->objects[$config->name][$index]['instance'];
         if ($instance === null) {
             throw new Exception('No instance loaded');
         }
     } elseif ($this->objects[$config->name][$index]['instance'] !== null) {
         throw new Exception('Instance already loaded, use reload parameter to reload');
     } else {
         // new instance
         $class = $config->class;
         $instance = new $class();
     }
     // Validate the properties in the class.
     if ($this->validated[$config->name] === false) {
         // No validated?
         $properties = get_object_vars($instance);
         $paths = array_merge($config->properties, $config->ignoreProperties, array_keys($config->belongsTo), array_keys($config->hasMany));
         foreach ($paths as $path) {
             $tokens = PropertyPath::parse($path);
             if (in_array($tokens[0][0], array(PropertyPath::TYPE_ANY, PropertyPath::TYPE_ELEMENT))) {
                 unset($properties[$tokens[0][1]]);
             }
         }
         if (count($properties) !== 0) {
             $causes = array('1. The column is missing in the backend/database.', '2. The relation/foreign key is missing in the backend/database.', '3. The column has diffent name than the property. Set the ModelConfig->properties[columname] = propertyname.', '4. The property should be ignored by the repository. Add the property to the ModelConfig->ignoreProperties.', '5. The relation couldn\'t be detected. Add an entry to ModelConfig->hasMany or ModelConfig->belongsTo.');
             throw new InfoException('Unexpected property: ' . \Sledgehammer\quoted_human_implode(' and ', array_keys($properties)) . ' in ' . $config->class . ' class for "' . $config->name . '"', '<b>Possible causes:</b><br />' . implode('<br />', $causes));
         }
         $this->validated[$config->name] = true;
     }
     // Map the data onto the instance
     foreach ($config->properties as $sourcePath => $targetPath) {
         $value = PropertyPath::get($sourcePath, $data);
         if (isset($config->readFilters[$sourcePath])) {
             $value = \Sledgehammer\filter($value, $config->readFilters[$sourcePath]);
         }
         PropertyPath::set($targetPath, $value, $instance);
     }
     foreach ($config->belongsTo as $property => $relation) {
         if (isset($relation['convert'])) {
             $value = $this->convert($relation['model'], PropertyPath::get($relation['convert'], $data));
             PropertyPath::set($property, $value, $instance);
         } else {
             $belongsToId = $data[$relation['reference']];
             if ($belongsToId === null) {
                 PropertyPath::set($property, null, $instance);
             } else {
                 if (empty($relation['model'])) {
                     // No model given?
                     throw new Exception('Invalid config: ' . $config->name . '->belongsTo[' . $property . '][model] not set');
                 }
                 if ($relation['useIndex']) {
                     $belongsToIndex = $this->resolveIndex($belongsToId);
                     $belongsToInstance = @$this->objects[$relation['model']][$belongsToIndex]['instance'];
                 } else {
                     $belongsToInstance = null;
                 }
                 if ($belongsToInstance !== null) {
                     $instance->{$property} = $belongsToInstance;
                 } else {
                     $fields = array($relation['id'] => $belongsToId);
                     $instance->{$property} = new BelongsToPlaceholder($this->ref() . '/' . $config->name . '/' . $property, $instance, $fields);
                 }
             }
         }
     }
     foreach ($config->hasMany as $property => $relation) {
         if (isset($relation['convert'])) {
             $collection = new RepositoryCollection(PropertyPath::get($relation['convert'], $data), $relation['model'], $this->ref());
             PropertyPath::set($property, $collection, $instance);
         } else {
             $instance->{$property} = new HasManyPlaceholder($this->ref() . '/' . $config->name . '/' . $property, $instance);
         }
     }
     $this->_triggerEvent($instance, 'load', $instance, ['repository' => $this->ref(), 'model' => $config->name], $this);
     return $instance;
 }
Beispiel #4
0
 /**
  * Download and install a PEAR package.
  *
  * @throws Exceptions on failure
  *
  * @param string $package
  * @param string $version
  * @param array  $options array(
  *                        'version' = Install a specific version
  *                        'target' => alternative target directory
  *                        'channel' => specifiy the channel
  *                        )
  */
 public function install($package, $options = [])
 {
     $version = \Sledgehammer\array_value($options, 'version') ?: 'stable';
     if (isset($options['channel'])) {
         $channel = $options['channel'];
         $this->addChannel($channel);
         if (empty($this->channels[$channel]['packages'][$package])) {
             if (isset($this->channels[$channel]['packages'])) {
                 foreach ($this->channels[$channel]['packages'] as $name => $info) {
                     if (strcasecmp($name, $package) === 0) {
                         return $this->install($name, $options);
                     }
                 }
             }
             throw new InfoException('Package "' . $package . '" not found in channel: ' . $channel, \Sledgehammer\quoted_human_implode(' and ', array_keys($this->channels[$channel]['packages'])));
         }
         $packageLocation =& $this->channels[$channel]['packages'][$package];
     } else {
         if (count($this->channels) === 0) {
             $this->addChannel('pear.php.net');
         }
         if (empty($this->packages[$package])) {
             foreach ($this->packages as $name => $channel) {
                 if (strcasecmp($name, $package) === 0) {
                     return $this->install($name, $options);
                 }
             }
             throw new InfoException('Package "' . $package . '" not found in channels: ' . \Sledgehammer\quoted_human_implode(' and ', array_keys($this->channels)), 'Available packages: ' . \Sledgehammer\quoted_human_implode(' and ', array_keys($this->packages)));
         }
         $packageLocation =& $this->channels[$this->packages[$package]]['packages'][$package];
     }
     $release = $this->findRelease($packageLocation, $version);
     if (\Sledgehammer\array_value($packageLocation, 'installed') === $version) {
         return;
     }
     $this->trigger('installing', $this, $package, $version);
     $tmpFolder = \Sledgehammer\TMP_DIR . 'PearInstaller/';
     $folderName = $package . '-' . $version;
     $tarFile = $tmpFolder . $folderName . '/package.tar';
     \Sledgehammer\mkdirs(dirname($tarFile));
     if (file_exists($tarFile) === false) {
         // Is this package already in the tmp folder
         Curl::download($release->g . '.tar', $tarFile);
     }
     chdir(dirname($tarFile));
     system('tar xf ' . escapeshellarg($tarFile), $exit);
     if ($exit !== 0) {
         throw new Exception('Unable to untar "' . $tarFile . '"');
     }
     if (file_exists(dirname($tarFile) . '/package2.xml')) {
         $info = simplexml_load_file(dirname($tarFile) . '/package2.xml');
     } else {
         $info = simplexml_load_file(dirname($tarFile) . '/package.xml');
     }
     // Install dependencies first
     foreach ($info->dependencies->required->package as $dependancy) {
         if ($dependancy->conflicts) {
             //				\Sledgehammer\notice('Dependancy "'.$dependancy->name.'" for "'.$package.'" <conflicts />');
             continue;
         }
         $this->install((string) $dependancy->name, array('channel' => (string) $dependancy->channel));
     }
     $renames = [];
     foreach ($info->phprelease as $release) {
         if ($release->count() > 0) {
             foreach ($release->filelist->install as $move) {
                 $renames[(string) $move['name']] = (string) $move['as'];
             }
         }
     }
     $files = $this->extractFiles($info->contents->dir, '', '/', $renames);
     foreach ($files as $file) {
         if (isset($this->targets[$file['role']])) {
             $dir = $this->targets[$file['role']];
             if (in_array($file['role'], array('doc', 'www'))) {
                 if (\Sledgehammer\text($file['to'])->startsWith($package) == false) {
                     $dir = $this->makePath($dir, $package);
                 }
             }
             $target = $this->makePath($dir, $file['to']);
             if (\Sledgehammer\mkdirs(dirname($target)) == false || is_writable(dirname($target)) == false) {
                 throw new Exception('Target "' . $target . '" is not writable');
             }
             $source = $this->makePath($tmpFolder . $folderName . '/' . $folderName, $file['from']);
             if (isset($file['tasks'])) {
                 $contents = file_get_contents($source);
                 foreach ($file['tasks'] as $task) {
                     $value = null;
                     if ($task['type'] === 'package-info') {
                         if ($task['to'] == 'version') {
                             $value = $version;
                         } elseif ($task['to'] == 'state') {
                             $value = (string) $info->stability->release;
                         }
                     } elseif ($task['type'] == 'pear-config') {
                         if (substr($task['to'], -4) === '_dir') {
                             $role = substr($task['to'], 0, -4);
                             if (isset($this->targets[$role])) {
                                 $value = $this->targets[$role];
                                 // @todo calculate relative paths
                                 \Sledgehammer\notice('Harcoding path "' . $value . '" into "' . $file['to'] . '"', $file);
                             }
                         } elseif ($task['to'] == 'php_bin') {
                             $value = trim(`which php`);
                             \Sledgehammer\notice('Harcoding path "' . $value . '" into "' . $file['to'] . '"', $file);
                         }
                     }
                     if ($task['task'] === 'replace') {
                         if ($value != '') {
                             $contents = str_replace($task['from'], $value, $contents);
                         } else {
                             \Sledgehammer\notice($task['type'] . ' "' . $task['to'] . '" not yet supported');
                         }
                     } else {
                         \Sledgehammer\notice('task "' . $task['task'] . '" not implemented');
                     }
                 }
                 file_put_contents($target, $contents);
             } else {
                 copy($source, $target);
             }
         }
     }
     \Sledgehammer\rmdir_recursive($tmpFolder . $folderName . '/' . $folderName);
     $packageLocation['installed'] = $version;
     $this->trigger('installed', $this, $package, $version);
 }
Beispiel #5
0
 /**
  * Read meta records.
  *
  * @param string [$key] The name of the meta property, when ommitted getMeta() returns all meta fields in a assoc array.
  * @param mixed [$default] Default returnvalue, when ommitted getMeta() will throw an exception if the property doesn't exist.
  *
  * @return mixed
  */
 public function getMeta($key = null, $default = null)
 {
     if ($this->meta instanceof HasManyPlaceholder || $this->meta instanceof Collection) {
         $meta = $this->meta;
     } else {
         throw new Exception('implement support');
     }
     if ($key === null) {
         $data = [];
         foreach ($meta as $row) {
             if (array_key_exists($row->key, $data)) {
                 if (array_value($data, $row->key, 0) === '__MULTIRECORD__') {
                     $data[$row->key][] = $row->value;
                 } else {
                     $data[$row->key] = ['__MULTIRECORD__', $data[$row->key], $row->value];
                 }
             } else {
                 $data[$row->key] = $row->value;
             }
         }
         return $data;
     }
     $value = $meta->where(['key' => $key]);
     if (count($value) == 1) {
         foreach ($value as $metaField) {
             return $metaField->value;
         }
     } elseif (count($value) == 0) {
         if (func_num_args() > 1) {
             return $default;
         }
         throw new InfoException('Meta field: "' . $key . '" doesn\'t exist in ' . str_replace(__NAMESPACE__ . '\\Model\\', '', static::class) . ' ' . $this->id, 'Existing fields: ' . \Sledgehammer\quoted_human_implode(' or ', array_keys($meta->selectKey('key')->toArray())));
     }
     $data = ['__MULTIRECORD__'];
     foreach ($value as $row) {
         $data[] = $row->value;
     }
     return $data;
 }
Beispiel #6
0
 /**
  * Returns the cached value when valid cache entry was found. otherwise retrieves the value via the $closure, stores it in the cache and returns it.
  *
  * @param string|int|array $options A string or int is interpreted as a 'expires' option.
  *                                  array(
  *                                  'max_age' => int|string // The entry must be newer than the $maxAge. Example: "-5min", "2012-01-01"
  *                                  'expires' => int|string, // A string is parsed via strtotime(). Examples: '+5min' or '2020-01-01' int's larger than 3600 (1 hour) are interpreted as unix timestamp expire date. And int's smaller or equal to 3600 are interpreted used as ttl.
  *                                  'forever' => bool, // Default false (When true no )
  *                                  'lock' => (bool) // Default true, Prevents a cache stampede (http://en.wikipedia.org/wiki/Cache_stampede)
  *                                  )
  * @param callable         $closure The method to retrieve/calculate the value.
  *
  * @return mixed
  */
 public function value($options, $closure)
 {
     // Convert option to an array
     if (is_array($options) === false) {
         $options = array('expires' => $options);
     }
     // Merge default options
     $default = array('expires' => false, 'forever' => false, 'max_age' => false, 'lock' => true);
     $options = array_merge($default, $options);
     if (count($options) !== count($default)) {
         \Sledgehammer\notice('Option: ' . \Sledgehammer\quoted_human_implode(' and ', array_keys(array_diff_key($options, $default))) . ' is invalid');
     }
     if ($options['expires'] === false && $options['forever'] === false && $options['max_age'] === false) {
         throw new InfoException('Invalid options: "expires",  "max_age" or "forever" must be set', $options);
     }
     if ($options['forever'] && $options['expires'] !== false) {
         throw new InfoException('Invalid options: "expires" and "forever" can\'t both be set', $options);
     }
     // Read value from cache (without lock)
     $hit = $this->read($value, $options['max_age']);
     if ($hit) {
         return $value;
     }
     // Miss, obtain lock and try again.
     if ($options['lock']) {
         $this->lock();
         // Read value from cache
         try {
             $hit = $this->read($value, $options['max_age']);
         } catch (Exception $e) {
             // Reading cache failed
             $this->release();
             throw $e;
         }
         if ($hit) {
             $this->release();
             return $value;
         }
     }
     // Miss, obtain value.
     try {
         $value = call_user_func($closure);
     } catch (Exception $e) {
         if ($options['lock']) {
             $this->release();
         }
         throw $e;
     }
     // Store value
     $this->write($value, $options['expires']);
     if ($options['lock']) {
         $this->release();
     }
     return $value;
 }
Beispiel #7
0
 /**
  * Remove a callback from an event.
  *
  * @param string $event
  * @param string $identifier
  */
 public function off($event, $identifier)
 {
     if ($this->hasEvent($event) === false) {
         \Sledgehammer\warning('Event: "' . $event . '" not registered', 'Available events: ' . \Sledgehammer\quoted_human_implode(', ', array_keys($this->events)));
         return false;
     }
     if (empty($this->events[$event][$identifier])) {
         \Sledgehammer\warning('Identifier: "' . $identifier . '" not found in listeners for event: "' . $event . '"', 'Available identifiers: ' . \Sledgehammer\quoted_human_implode(', ', array_keys($this->events[$event])));
         return false;
     }
     unset($this->events[$event][$identifier]);
     return true;
 }
Beispiel #8
0
 /**
  * Import the definition in a file.
  *
  * @param string $filename
  * @param array  $settings
  *
  * @return array definitions
  */
 public function importFile($filename, $settings = [])
 {
     $setttings = $this->mergeSettings($settings);
     $previousError = error_get_last();
     if (filesize($filename) > $settings['filesize_limit']) {
         $this->hint('File ' . $filename . ' too big, skipping...', array('allowed size' => $settings['filesize_limit'], 'actual size' => filesize($filename)));
         return;
     }
     $tokens = token_get_all(file_get_contents($filename));
     $error = error_get_last();
     if ($error !== $previousError) {
         \Sledgehammer\notice($error['message'] . ' in "' . $filename . '"');
     }
     $definitions = [];
     $namespace = '';
     $state = 'DETECT';
     foreach ($tokens as $token) {
         if ($token[0] == T_WHITESPACE) {
             continue;
         }
         switch ($state) {
             case 'DETECT':
                 switch ($token[0]) {
                     case T_NAMESPACE:
                         $state = 'NAMESPACE';
                         $namespace = '';
                         break;
                     case T_CLASS:
                         $state = 'CLASS';
                         break;
                     case T_INTERFACE:
                         $state = 'INTERFACE';
                         break;
                     case T_TRAIT:
                         $state = 'INTERFACE';
                         break;
                     case T_DOUBLE_COLON:
                         $state = 'SKIP_ONE';
                         break;
                 }
                 break;
             case 'NAMESPACE':
                 if (in_array($token[0], array(T_STRING, T_NS_SEPARATOR))) {
                     $namespace .= $token[1];
                     break;
                 }
                 if (in_array($token, array(';', '{'))) {
                     $state = 'DETECT';
                     break;
                 }
                 $this->unexpectedToken($token);
                 $state = 'DETECT';
                 break;
             case 'CLASS':
                 if ($token[0] == T_STRING) {
                     if ($settings['matching_filename'] && substr(basename($filename), 0, -4) != $token[1]) {
                         \Sledgehammer\notice('Filename doesn\'t match classname "' . $token[1] . '" in "' . $filename . '"', array('settings' => $settings));
                     }
                     if ($namespace == '') {
                         $definition = $token[1];
                     } else {
                         $definition = $namespace . '\\' . $token[1];
                     }
                     $definitions[] = $definition;
                     break;
                 }
                 if ($token[0] == T_EXTENDS) {
                     $state = 'DETECT';
                     break;
                 } elseif ($settings['mandatory_superclass'] && !in_array($definition, array('Sledgehammer\\Core\\Object'))) {
                     \Sledgehammer\notice('Class: "' . $definition . '" has no superclass, expection "class X extends Y"');
                 }
                 if ($token == '{' || $token[0] == T_IMPLEMENTS) {
                     $state = 'DETECT';
                     break;
                 }
                 $this->unexpectedToken($token);
                 $state = 'DETECT';
                 break;
             case 'INTERFACE':
                 if ($token[0] == T_STRING) {
                     if ($settings['matching_filename'] && substr(basename($filename), 0, -4) != $token[1]) {
                         \Sledgehammer\notice('Filename doesn\'t match interface-name "' . $token[1] . '" in "' . $filename . '"', array('settings' => $settings));
                     }
                     if ($namespace == '') {
                         $definition = $token[1];
                     } else {
                         $definition = $namespace . '\\' . $token[1];
                     }
                     $definitions[] = $definition;
                     $state = 'DETECT';
                     break;
                 }
                 $this->unexpectedToken($token);
                 $state = 'DETECT';
                 break;
             case 'SKIP_ONE':
                 $state = 'DETECT';
                 break;
             default:
                 throw new Exception('Unexpected state: "' . $state . '"');
         }
     }
     if ($settings['detect_accidental_output'] && $token[0] == T_INLINE_HTML) {
         \Sledgehammer\notice('Invalid end of file. (html)output detected in "' . basename($filename) . '"');
     }
     /* elseif ($token[0] == T_CLOSE_TAG && $token[1] != '?>') {
        \Sledgehammer\notice('Invalid end of file, accidental newline detected in "'.basename($filename).'"'); // newline directly after the close tag doesn't cause problems
        } */
     if (count($definitions) > 1) {
         if ($settings['one_definition_per_file']) {
             \Sledgehammer\notice('Multiple definitions found in ' . $filename, $definitions);
         }
     } elseif ($settings['mandatory_definition'] && count($definitions) === 0 && basename($filename) !== strtolower(basename($filename))) {
         \Sledgehammer\notice('No classes or interfaces found in ' . $filename);
     }
     $filename = $this->relativePath($filename);
     foreach ($definitions as $definition) {
         if (isset($this->definitions[$definition]) && $this->definitions[$definition] != $filename) {
             if (empty($this->ambiguous[$definition])) {
                 $this->ambiguous[$definition] = array($this->definitions[$definition]);
             }
             unset($this->definitions[$definition]);
             // Ignore both definitions to prevent autoloading the wrong one.
         }
         if (isset($this->ambiguous[$definition])) {
             $this->ambiguous[$definition][] = $filename;
             if ($settings['notice_ambiguous']) {
                 \Sledgehammer\notice('"' . $definition . '" is ambiguous, it\'s found in multiple files: ' . \Sledgehammer\quoted_human_implode(' and ', $this->ambiguous[$definition]), array('settings' => $settings));
             }
         } else {
             $this->definitions[$definition] = $filename;
         }
     }
 }