public function folder($folder, $filename = null) { if (!preg_match('/^[0-9]+x[0-9]+$/', $folder)) { // Zijn er geen afmetingen meegegeven? return $this->onFolderNotFound(); } if (!$filename) { // Komt de afbeelding uit een subfolder($recursive)? $url = Url::getCurrentURL(); $path = array('folders' => $url->getFolders(), 'filename' => $url->getFilename()); $subfolders = array_slice($path['folders'], $this->depth + 1); $filename = implode('/', $subfolders) . '/' . $path['filename']; } $source = $this->imagesFolder . $filename; if (!file_exists($source)) { return new HttpError(404, array('warning' => 'Image "' . $filename . '" not found in "' . $this->imagesFolder . '"')); } $target = $this->targetFolder . $folder . '/' . $filename; if (!file_exists($target) || filemtime($source) > filemtime($target)) { $dimensions = explode('x', $folder); \Sledgehammer\mkdirs(dirname($target)); $image = new Image($source); $image->saveThumbnail($target, $dimensions[0], $dimensions[1]); } return new File($target); }
public function test_startup() { \Sledgehammer\mkdirs(\Sledgehammer\TMP_DIR . 'Cache'); $cache = Cache::rootNode(); $this->assertInstanceOf(Cache::class, $cache); $this->apcSupported = function_exists('apc_fetch'); if ($this->apcSupported === false) { $this->markTestSkipped('Skipping tests for "apc" backend, the php-extension "apc" is not installed.'); } }
/** * Register all models from the backend. * Aslo validates and corrects the model configurations. * * @param RepositoryBackend $backend */ public function registerBackend($backend) { if ($backend->identifier === null) { throw new Exception('RepositoryBackend->identifier is required'); } if (count($backend->configs) === 0) { notice(get_class($backend) . ': "' . $backend->identifier . '" doesn\'t have any ModelConfigs'); return; } if (isset($this->backends[$backend->identifier])) { throw new Exception('RepositoryBackend "' . $backend->identifier . '" already registered'); } $this->backends[$backend->identifier] = $backend; // Pass 1: Register configs foreach ($backend->configs as $config) { if ($config->backend === null) { $config->backend = $backend->identifier; } $this->registerModel($config); } // Pass 2: Register junctions foreach ($backend->junctions as $junction) { if ($junction->backend === null) { $junction->backend = $backend->identifier; } if (isset($this->junctions[$junction->name])) { notice('overwriting junction ' . $junction->name); } $this->junctions[$junction->name] = $junction; } // Pass 3: Auto detect id's foreach ($backend->configs as $backendConfig) { $config = $this->configs[$backendConfig->name]; if (count($config->id) === 0) { if (isset($config->properties['id'])) { // No id set, but the column 'id' exists? $config->id = array('id'); } else { warning('Invalid config: ' . $config->name . '->id is not configured and could not detect an "id" element'); } } } // Pass 4: Validate and correct configs foreach ($backend->configs as $backendConfig) { $config = $this->configs[$backendConfig->name]; if (count($config->properties) === 0) { warning('Invalid config: ' . $config->name . '->properties array is not configured'); } foreach ($config->id as $idIndex => $idColumn) { if (isset($config->properties[$idColumn])) { $idProperty = $config->properties[$idColumn]; } else { warning('Invalid config: ' . $config->name . '->id[' . $idIndex . ']: "' . $idColumn . '" isn\'t mapped as a property'); } } foreach ($config->belongsTo as $property => $belongsTo) { $validationError = false; if (is_array($belongsTo) === false) { $validationError = 'Invalid config: ' . $config->name . '->belongsTo[' . $property . '] should be an array'; } if (empty($belongsTo['model'])) { $validationError = 'Invalid config: ' . $config->name . '->belongsTo[' . $property . '][model] not set'; } elseif (empty($belongsTo['reference']) && empty($belongsTo['convert'])) { $validationError = 'Invalid config: ' . $config->name . '->belongsTo[' . $property . '] is missing a [reference] or [convert] element'; } elseif (isset($belongsTo['convert']) && isset($belongsTo['reference'])) { $validationError = 'Invalid config: ' . $config->name . '->belongsTo[' . $property . '] can\'t contain both a [reference] and a [convert] element'; } if (isset($belongsTo['reference'])) { if (empty($belongsTo['id'])) { // id not set, but (target)model is configured? if (empty($this->configs[$belongsTo['model']])) { $validationError = 'Invalid config: ' . $config->name . '->belongsTo[' . $property . '][id] couldn\'t be inferred, because model "' . $belongsTo['model'] . '" isn\'t registered'; } else { $belongsToConfig = $this->_getConfig($belongsTo['model']); // Infer/Assume that the id is the ID from the model if (count($belongsToConfig->id) == 1) { $belongsTo['id'] = current($belongsToConfig->id); $config->belongsTo[$property]['id'] = $belongsTo['id']; // Update config } else { $validationError = 'Invalid config: ' . $config->name . '->belongsTo[' . $property . '][id] not set and can\'t be inferred (for a complex key)'; } } } if (isset($belongsTo['reference']) && isset($belongsTo['useIndex']) == false) { if (empty($this->configs[$belongsTo['model']])) { $validationError = 'Invalid config: ' . $config->name . '->belongsTo[' . $property . '][useIndex] couldn\'t be inferred, because model "' . $belongsTo['model'] . '" isn\'t registered'; } else { $belongsToConfig = $this->_getConfig($belongsTo['model']); // Is the foreign key is linked to the model id $belongsTo['useIndex'] = count($belongsToConfig->id) == 1 && $belongsTo['id'] == current($belongsToConfig->id); $config->belongsTo[$property]['useIndex'] = $belongsTo['useIndex']; // Update config } } if (isset($belongsTo['id'])) { // Add foreign key to the collection mapping $this->collectionMappings[$config->name][$property . '->' . $belongsToConfig->properties[$belongsTo['id']]] = $belongsTo['reference']; $this->collectionMappings[$config->name][$property . '.' . $belongsToConfig->properties[$belongsTo['id']]] = $belongsTo['reference']; } } // @todo Add collectionMapping for "convert" relations? if (empty($this->configs[$belongsTo['model']])) { // $validationError = 'Invalid config: '.$config->name.'->belongsTo['.$property.'][model] "'.$belongsTo['model'].'" isn\'t registered'; } // Remove invalid relations if ($validationError) { warning($validationError); unset($config->belongsTo[$property]); } } foreach ($config->hasMany as $property => $hasMany) { $validationError = false; if (empty($hasMany['model'])) { $validationError = 'Invalid config: ' . $config->name . '->hasMany[' . $property . '][model] not set'; } elseif (isset($hasMany['convert'])) { // no additional fields are needed. } elseif (empty($hasMany['reference'])) { // @todo Infer property (lookup belongsTo) $validationError = 'Invalid hasMany: ' . $config->name . '->hasMany[' . $property . '][reference] not set'; } elseif (isset($hasMany['reference']) && empty($hasMany['belongsTo'])) { $referencePath = PropertyPath::parse($hasMany['reference']); if (count($referencePath) == 1) { // The foreign key is linked directly } elseif (empty($this->configs[$hasMany['model']])) { $validationError = 'Invalid config: ' . $config->name . '->hasMany[' . $property . '][belongsTo] couldn\'t be inferred, because model "' . $hasMany['model'] . '" isn\'t registered'; } else { // Infer the belongsTo path based on the model and reference path. $hasManyConfig = $this->configs[$hasMany['model']]; $idProperty = array(array_value(array_pop($referencePath), 1)); if ($idProperty == $hasManyConfig->id) { $hasMany['belongsTo'] = PropertyPath::assemble($referencePath); $config->hasMany[$property]['belongsTo'] = $hasMany['belongsTo']; // update config } } } if (isset($hasMany['through'])) { if (array_key_exists('fields', $hasMany) === false) { $config->hasMany[$property]['fields'] = []; } elseif (array_key_exists('junctionClass', $hasMany) === false) { $config->hasMany[$property]['junctionClass'] = Junction::class; } $junctionConfig = @$this->junctions[$hasMany['through']]; if (array_key_exists('idPath', $hasMany) === false && $junctionConfig) { // dump($junctionConfig); } } // Remove invalid relations if ($validationError) { warning($validationError); unset($config->hasMany[$property]); } } // Validate read & write filters foreach ($config->readFilters as $column => $filter) { if (empty($config->properties[$column])) { notice('Invalid config: ' . $config->name . '->readFilters[' . $column . '] isn\'t mapped as property', $filter); } } foreach ($config->writeFilters as $column => $filter) { if (empty($config->properties[$column])) { notice('Invalid config: ' . $config->name . '->writeFilters[' . $column . '] isn\'t mapped as property', $filter); } } } // Pass 5: Generate classes based on properties when no class is detected/found. foreach ($backend->configs as $config) { if (substr($config->class, 0, 11) !== '\\Generated\\') { $this->validated[$config->name] = false; } else { $this->validated[$config->name] = true; // Generated classes are valid by design. if (class_exists($config->class, false)) { notice('Skipped generating class: "' . $config->class . '", a class with the same name exists'); continue; } $parts = explode('\\', $config->class); array_pop($parts); // remove class part. $namespace = implode('\\', array_slice($parts, 1)); // Generate class $use = ''; $aliases = []; $alias = $this->buildAlias(Object::class, $aliases, $use); $php = "\nclass " . $config->name . " extends " . $alias . "\n{\n"; $properties = []; foreach ($config->properties as $path) { $parsedPath = PropertyPath::parse($path); $property = $parsedPath[0][1]; if (!in_array($property, $properties)) { $php .= " public \$" . $property . ";\n"; $properties[] = $property; } } foreach ($config->belongsTo as $path => $belongsTo) { $parsedPath = PropertyPath::parse($path); $belongsToConfig = $this->_getConfig($belongsTo['model']); $alias = $this->buildAlias($belongsToConfig->class, $aliases, $use); $property = $parsedPath[0][1]; $php .= "\n"; $php .= " /**\n"; $php .= " * @var " . $alias . ' The associated ' . $belongsToConfig->name . "\n"; $php .= " */\n"; $php .= " public \$" . $property . ";\n"; } foreach ($config->hasMany as $path => $hasMany) { $parsedPath = PropertyPath::parse($path); $hasManyConfig = $this->_getConfig($hasMany['model']); $property = $parsedPath[0][1]; $aliasC = $this->buildAlias(Collection::class, $aliases, $use); $alias = $this->buildAlias($hasManyConfig->class, $aliases, $use); $php .= "\n"; $php .= " /**\n"; $php .= " * @var " . $aliasC . "|" . $alias . "[] A collection with the associated " . $hasManyConfig->plural . "\n"; $php .= " */\n"; $php .= " public \$" . $property . ";\n"; } $php .= '}'; $php = "namespace " . $namespace . ";\n\n" . $use . $php; if (\Sledgehammer\ENVIRONMENT === 'development' && $namespace === 'Generated') { // Write autoComplete helper // @todo Only write file when needed, aka validate $this->autoComplete \Sledgehammer\mkdirs(\Sledgehammer\TMP_DIR . 'AutoComplete'); file_put_contents(\Sledgehammer\TMP_DIR . 'AutoComplete/' . $config->name . '.php', "<?php\n\n" . $php . "\n"); } eval($php); } } // Pass 6: Generate or update the AutoComplete Helper for the default repository? if (\Sledgehammer\ENVIRONMENT == 'development' && isset(self::$instances['default']) && self::$instances['default'] === $this) { $autoCompleteFile = \Sledgehammer\TMP_DIR . 'AutoComplete/repository.ini'; if ($this->autoComplete === null) { if (file_exists($autoCompleteFile)) { $this->autoComplete = parse_ini_file($autoCompleteFile, true); } else { $this->autoComplete = []; } } // Validate AutoCompleteHelper foreach ($backend->configs as $config) { $autoComplete = array('class' => $config->class, 'properties' => implode(', ', $config->properties)); if (empty($this->autoComplete[$config->name]) || $this->autoComplete[$config->name] != $autoComplete) { $this->autoComplete[$config->name] = $autoComplete; \Sledgehammer\mkdirs(\Sledgehammer\TMP_DIR . 'AutoComplete'); \Sledgehammer\write_ini_file($autoCompleteFile, $this->autoComplete, 'Repository AutoComplete config'); $this->writeAutoCompleteHelper(\Sledgehammer\TMP_DIR . 'AutoComplete/DefaultRepository.php', 'DefaultRepository', 'Generated'); } } } }
/** * 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); }
echo " \n"; return false; } $modules = Framework::getModules(); $folders = []; foreach ($modules as $folder => $info) { $modulePath = $info['path']; if (is_dir($modulePath . 'public')) { if (\Sledgehammer\array_value($info, 'app')) { $folders[$modulePath . 'public'] = ''; } else { $folders[$modulePath . 'public'] = '/' . $folder; } } } foreach ($targetFolders as $targetFolder) { $fileCount = 0; echo "\nPopulating /" . $targetFolder . " ...\n"; foreach ($folders as $folder => $targetSuffix) { $targetPath = \Sledgehammer\PATH . $targetFolder . $targetSuffix; \Sledgehammer\mkdirs($targetPath); $fileCount += copydir($folder, $targetPath, array('.svn')); } echo ' ' . $fileCount . " files copied\n"; } if (isset($modules['minify'])) { include $modules['minify']['path'] . 'utils/minify_DocumentRoot.php'; } else { echo " done.\n"; } return true;
} define('Sledgehammer\\TMP_DIR', $__TMP_DIR); unset($__TMP_DIR); } // Case insensitive "natural order" sorting method for natcasesort() in Collection->orderBy() define('Sledgehammer\\SORT_NATURAL_CI', -2); // Regex for supported operators for the compare() function define('Sledgehammer\\COMPARE_OPERATORS', '==|!=|<|<=|>|>=|IN|NOT IN|LIKE|NOT LIKE'); /** * 2. Declare public functions. */ require_once __DIR__ . '/functions.php'; // Namespaced functions require_once __DIR__ . '/helpers.php'; // Global functions (but not guaranteed) \Sledgehammer\mkdirs(\Sledgehammer\TMP_DIR); /* * 3. Configure and register the AutoLoader */ require_once __DIR__ . '/Object.php'; require_once __DIR__ . '/Debug/Autoloader.php'; spl_autoload_register('Sledgehammer\\Core\\Debug\\AutoLoader::lazyRegister'); /* * 4. Set DebugR statusbar header. */ if (headers_sent() === false) { DebugR::send('sledgehammer-statusbar', 'no statusbar data.', true); } /* * Timestamp (in microseconds) for when the Sledgehammer Framework was initialized. */
/** * Return the rootnode for the app. * * @return Cache */ public static function rootNode() { if (self::$instance !== null) { return self::$instance; } $backend = function_exists('apc_fetch') ? 'apc' : 'file'; $backend = 'file'; self::$instance = new self('', $backend); if ($backend === 'file') { \Sledgehammer\mkdirs(\Sledgehammer\TMP_DIR . 'Cache'); if (rand(1, 25) === 1) { // Run gc only once in every X requests. self::file_gc(); } } return self::$instance; }
/** * Import definitions inside a folder. * Checks "autoloader.ini" for additional settings. * * @param string $path * @param array $settings */ public function importFolder($path, $settings = []) { $composer = false; if (file_exists($path . '/composer.json')) { if (substr($path, -1) !== '/') { $path .= '/'; } $composer = json_decode(file_get_contents($path . 'composer.json'), true); } if ($composer && isset($composer['sledgehammer'])) { $settings += ['matching_filename' => true, 'mandatory_definition' => true, 'mandatory_superclass' => true, 'one_definition_per_file' => true, 'detect_accidental_output' => true, 'notice_ambiguous' => true]; } $settings = $this->loadSettings($path, $settings); if ($composer) { $locations = []; $trace = []; $preventDefault = true; if (isset($composer['autoload']['classmap'])) { foreach ($composer['autoload']['classmap'] as $entry) { $paths = is_array($entry) ? $entry : array($entry); foreach ($paths as $entryPath) { if (empty($entryPath) || $entry === '.') { \Sledgehammer\notice('Empty autoload.classmap in "composer.json" isn\'t supported'); $preventDefault = false; break; } elseif (is_dir($path . $entryPath)) { $locations[] = $entryPath; $trace[] = 'classmap'; } } } } $namespaces = []; if (isset($composer['autoload']['psr-0'])) { $namespaces += $composer['autoload']['psr-0']; } if (isset($composer['autoload']['psr-4'])) { $namespaces += $composer['autoload']['psr-4']; } if (isset($composer['autoload-dev']['psr-0'])) { $namespaces += $composer['autoload-dev']['psr-0']; } if (isset($composer['autoload-dev']['psr-4'])) { $namespaces += $composer['autoload-dev']['psr-4']; } foreach ($namespaces as $namespace => $entry) { $paths = is_array($entry) ? $entry : array($entry); foreach ($paths as $entryPath) { if (in_array($entryPath, array('', '/', '.'))) { $preventDefault = false; break 2; } $locations[] = $entryPath; $trace[] = 'psr-0 (' . $namespace . ')'; } } if ($preventDefault) { foreach ($locations as $i => $entry) { if (is_dir($path . $entry)) { if (in_array($entry, $settings['ignore_folders'])) { continue; } $this->importFolder($path . $entry, $settings); } elseif (is_file($path . $entry)) { if (in_array($entry, $settings['ignore_files'])) { continue; } $this->importFile($path . $entry, $settings); } else { // Allow invalid composer.json configurations, not Sledgehammers problem. // \Sledgehammer\notice('Invalid "composer.json" entry: '.$trace[$i].': "'.$entry.'"', 'file or directory: "'.$path.$entry.'" not found'); } } return; } } $useCache = $this->enableCache && $settings['cache_level'] > 0; if ($useCache) { --$settings['cache_level']; $scanCount = false; $folder = basename($path); if ($folder == 'classes') { $folder = basename(dirname($path)); } $cacheFile = \Sledgehammer\TMP_DIR . 'AutoLoader/' . $folder . '_' . md5($path) . '.php'; if (!\Sledgehammer\mkdirs(dirname($cacheFile))) { $this->enableCache = false; $useCache = false; } elseif (file_exists($cacheFile)) { $mtimeCache = filemtime($cacheFile); $revalidateCache = $mtimeCache < time() - $settings['revalidate_cache_delay']; // Is er een delay ingesteld en is deze nog niet verstreken?; $mtimeFolder = $revalidateCache ? \Sledgehammer\mtime_folders($path, array('php'), $scanCount) : 0; if ($mtimeFolder !== false && $mtimeCache > $mtimeFolder) { // Is het cache bestand niet verouderd? if ($this->loadDatabase($cacheFile, true, $scanCount)) { if ($settings['revalidate_cache_delay'] && $revalidateCache) { // is het cache bestand opnieuw gevalideerd? touch($cacheFile); // de mtime van het cache-bestand aanpassen, (voor het bepalen of de delay is vertreken) } return; } } } } // Import files & subfolders try { $dir = new DirectoryIterator($path); } catch (Exception $e) { \Sledgehammer\notice($e->getMessage()); return; } foreach ($dir as $entry) { if ($entry->isDot()) { continue; } if ($entry->isDir()) { if (in_array($entry->getPathname(), $settings['ignore_folders'])) { continue; } $this->importFolder($entry->getPathname(), $settings); } if (\Sledgehammer\file_extension($entry->getFilename()) == 'php') { if (in_array($entry->getPathname(), $settings['ignore_files']) == false) { $this->importFile($entry->getPathname(), $settings); } } } if ($useCache) { $this->saveDatabase($cacheFile, $this->relativePath($path), $scanCount); } }