/** * Render an error page. */ public function render() { $error = $this->getError(); $messageBox = new Template('sledgehammer/mvc/templates/httperror.php', $error); $messageBox->render(); foreach ($this->options as $option => $value) { switch ((string) $option) { case 'notice': case 'warning': $function = $option; if (is_array($value)) { call_user_func_array($function, $value); } else { call_user_func($function, $value); } break; case 'exception': \Sledgehammer\report_exception($value); break; default: \Sledgehammer\notice('Unknown option: "' . $option . '"', array('value' => $value)); break; } } }
/** * Constructor. * * @param object/iterator/array $data * @param array $options */ public function __construct($data, $options = []) { $this->_data = $data; foreach ($options as $name => $value) { $property = '_' . $name; if (property_exists($this, $property)) { $this->{$property} = $value; } else { \Sledgehammer\notice('Invalid option: "' . $name . '"'); } } }
/** * Constructor. * * @param array $options Hiermee kun je de "table" en "dbLink" overschrijven */ public function __construct($options = []) { $availableOptions = array('table', 'dbLink'); foreach ($options as $property => $value) { if (in_array($property, $availableOptions)) { $this->{$property} = $value; } else { \Sledgehammer\notice('Invalid option "' . $property . '"', 'Use: ' . \Sledgehammer\human_implode(' or ', $availableOptions)); } } if ($this->table === null) { $this->table = session_name(); } }
/** * Trigger an event. * * @param string $event * @param stdClass $sender * @param mixed $args (optional) */ public function trigger($event, $sender, $args = null) { if (isset($this->events[$event])) { $method = 'on' . ucfirst($event); $arguments = func_get_args(); array_shift($arguments); if (method_exists($this, $method)) { call_user_func_array(array($this, $method), $arguments); } foreach ($this->events[$event] as $callback) { call_user_func_array($callback, $arguments); } } else { \Sledgehammer\notice('Event: "' . $event . '" not registered', 'Available events: ' . \Sledgehammer\quoted_human_implode(', ', array_keys($this->events))); } }
/** * Generate a HTML element. * * @param string $name Name of the element. Example "div", "img", "script", etc * @param array $attributes array('type'=> 'checkbox', 'checked' => true, 'disabled') * @param bool|string|array $contents true: Only generate the opentag, string html, array with sub elements * * @return Html */ public static function element($name, $attributes, $contents = '') { $name = strtolower($name); $element = new self('<' . $name); foreach ($attributes as $key => $value) { if (is_int($key)) { if (preg_match('/^[a-z\\-_:]+$/i', $value)) { $element->html .= ' ' . strtolower($value); } else { \Sledgehammer\notice('Invalid attribute: ' . $key, $value); } } elseif (is_bool($value)) { if ($value) { $element->html .= ' ' . strtolower($key); } } else { $element->html .= ' ' . strtolower($key) . '="' . self::escape($value) . '"'; } } if ($contents === true) { // Only generate the open tag? $element->html .= '>'; return $element; } if ($contents === '') { // Close the tag? if (in_array($name, array('area', 'base', 'br', 'hr', 'input', 'img', 'link', 'meta'))) { $element->html .= ' />'; } else { $element->html .= '></' . $name . '>'; } return $element; } $element->html .= '>'; if (is_array($contents)) { foreach ($contents as $sub_element) { $element->html .= $sub_element; } } else { $element->html .= $contents; } $element->html .= '</' . $name . '>'; return $element; }
/** * Constructor. * * @param string|array $label_or_options * @param array $options */ public function __construct($label_or_options, $options = array()) { if (is_array($label_or_options) === false) { $options['label'] = $label_or_options; } else { if (count($options) !== 0) { \Sledgehammer\notice('Second parameter $options is ignored'); } $options = $label_or_options; } // Set attributes and properties foreach ($options as $option => $value) { if (property_exists($this, $option)) { $this->{$option} = $value; } else { $this->attributes[$option] = $value; } } }
/** * Construct the Text object and convert $text to UTF-8. * * @param string $text The text * @param string|array $charset string: The charset of $text; array: Autodetect encoding, example: array('ASCII', 'UTF-8', 'ISO-8859-15'); null: defaults to Framework::$charset */ public function __construct($text, $charset = null) { if ($text instanceof self) { $this->text = $text->text; if ($charset !== null && $charset !== 'UTF-8') { \Sledgehammer\notice('Invalid charset given, an Text object will alway be UTF-8 encoded'); } return; } if ($charset === null) { $charset = Framework::$charset; } elseif (is_array($charset)) { $charset = mb_detect_encoding($text, $charset, true); if ($charset === false) { \Sledgehammer\notice('Unable to detect charset'); $this->text = mb_convert_encoding($text, 'UTF-8'); return; } } $this->text = mb_convert_encoding($text, 'UTF-8', $charset); }
public function import(&$errors = null, $request = null) { $this->state = 'NOT_SENT'; if ($request === null) { if (strtolower($this->getAttribute('method')) === 'post') { $request = $_POST; } elseif (strtolower($this->getAttribute('method')) === 'get') { $request = $_GET; } else { \Sledgehammer\notice('Unexpected method: "' . $this->getAttribute('method') . '"'); $request = $_REQUEST; } } if (count($request) == 0) { return $this->getValue(); } $this->state = 'SENT'; $data = []; foreach ($this->fields as $key => $field) { if (is_object($field) && method_exists($field, 'import')) { $data[$key] = $field->import($error, $request); if ($error) { $errors[$key] = $error; } } } if (empty($data['actions'])) { // import which input type="submit" is sent. foreach ($this->actions as $component) { if (is_object($component) && method_exists($component, 'import')) { if (empty($data['actions'])) { $data['actions'] = []; } $data['actions'][$key] = $component->import($_, $request); } } } return $data; }
} $targets = array('php' => \Sledgehammer\PATH . 'vendor/pear/php', 'data' => \Sledgehammer\PATH . 'vendor/pear/data', 'script' => \Sledgehammer\PATH . 'vendor/pear/script', 'bin' => \Sledgehammer\PATH . 'vendor/pear/bin', 'doc' => \Sledgehammer\PATH . 'vendor/pear/docs', 'www' => APP_DIR . 'vendor/pear/www'); $pear = new PearInstaller($targets); $pear->on('channelAdded', function ($sender, $domain, $channel) { echo 'Channel "' . $domain . '" loaded. (' . count($channel['packages']) . " packages)\n"; }); $pear->on('installed', function ($sender, $package, $version) { echo ' ' . $package . ' [' . $version . "] installed.\n"; }); unset($argv[0]); foreach ($argv as $arg) { if (preg_match('/^((?P<channel>[^\\/]+)\\/){0,1}(?P<package>[^\\/-]+){1}(\\-(?P<version>[0-9\\.]+|alpha|beta|stable)){0,1}$/', $arg, $matches)) { //|-stable|-alpha|-beta if ($matches['channel'] == '' && empty($matches['version']) && preg_match('/^([a-z0-9]+\\.)+[a-z]{2,4}$/i', $arg)) { // "pear.php.net" $pear->addChannel($arg); } else { $options = []; if (empty($matches['channel']) == false) { $options['channel'] = $matches['channel']; } if (isset($matches['version'])) { $options['version'] = $matches['version']; } $pear->install($matches['package'], $options); } } else { \Sledgehammer\notice('Unable to determine package-name in "' . $arg . '"'); // A package name containing a "/" or -" ? } }
/** * Extract class and interface definitions from a file. * * @param string $filename Fullpath to the php-file. */ public function open($filename) { $tokens = new PhpTokenizer(file_get_contents($filename)); $namespace = ''; $uses = []; $definitions = []; $definition = array('level' => -1); $globalFunctions = []; $functions =& $globalFunctions; $level = 0; $parameterLevel = -1; // skip parameters in anonymous functions foreach ($tokens as $token) { $type = $token[0]; $value = $token[1]; if ($value == '' && $type != 'T_NAMESPACE') { \Sledgehammer\notice('Empty token', $token); } if ($type == 'T_PHP' || $type == 'T_HTML') { continue; } switch ($type) { case 'T_NAMESPACE': $namespace = $value; break; case 'T_USE': $pos = strrpos($value, '\\'); $namespaceAlias = $pos === false ? $value : substr($value, $pos + 1); $uses[$namespaceAlias] = $value; break; case 'T_USE_ALIAS': $uses[$value] = $uses[$namespaceAlias]; unset($uses[$namespaceAlias]); break; case 'T_INTERFACE': $definitions[] = array('type' => 'INTERFACE', 'namespace' => $namespace, 'interface' => $value, 'identifier' => $this->prefixNamespace($namespace, $value, $uses), 'extends' => [], 'methods' => [], 'level' => $level); $definition =& $definitions[count($definitions) - 1]; break; case 'T_CLASS': $definitions[] = array('type' => 'CLASS', 'namespace' => $namespace, 'class' => $value, 'identifier' => $this->prefixNamespace($namespace, $value, $uses), 'extends' => [], 'implements' => [], 'methods' => [], 'level' => $level); $definition =& $definitions[count($definitions) - 1]; break; case 'T_EXTENDS': $extends = $this->prefixNamespace($namespace, $value, $uses); $definition['extends'][] = $extends; $this->addUsedIn($extends, $filename, $token[2]); break; case 'T_IMPLEMENTS': $interface = $this->prefixNamespace($namespace, $value, $uses); $definition['implements'][] = $interface; $this->addUsedIn($interface, $filename, $token[2]); break; case 'T_FUNCTION': $function = $value; $parameter = null; if ($level == $definition['level'] + 1) { $definition['methods'][$function] = []; $functions =& $definition['methods']; } else { $functions =& $globalFunctions; } $parameterLevel = $level; break; case 'T_TYPE_HINT': if (strtolower($value) !== 'array') { $this->addUsedIn($this->prefixNamespace($namespace, $value, $uses), $filename, $token[2]); } break; case 'T_PARAMETER': if ($parameterLevel != $level) { // Doesn't this parameter belong to the function? break; // Propably a catch () parameter } $parameter = substr($value, 1); // strip '$' $functions[$function][$parameter] = null; break; case 'T_PARAMETER_VALUE': $functions[$function][$parameter] = $value; $parameter = null; break; case 'T_OPEN_BRACKET': $level++; break; case 'T_CLOSE_BRACKET': $level--; break; case 'T_CALL': $this->addCalledIn($value, $filename, $token[2]); break; case 'T_METHOD_CALL': break; case 'T_OBJECT': $this->addUsedIn($this->prefixNamespace($namespace, $value, $uses), $filename, $token[2]); break; default: \Sledgehammer\notice('Unexpected tokenType: "' . $type . '"'); break; } } if ($level != 0) { \Sledgehammer\notice('Level: ' . $level . ' Number of "{" doesn\'t match the number of "}"'); } unset($definition); // Add definitions to de loader foreach ($definitions as $index => $definition) { $identifier = $definition['identifier']; unset($definition['identifier'], $definition['level']); $definition['filename'] = $filename; // $duplicate = false; // if (isset($this->classes[$identifier])) { // $duplicate = $this->classes[$identifier]; // } elseif (isset($this->interfaces[$identifier])) { // $duplicate = $this->interfaces[$identifier]; // } // if ($duplicate) { // $this->parserNotice('"'.$identifier.'" is ambiguous, it\'s found in multiple files: "'.$duplicate['filename'].'" and "'.$definition['filename'].'"'); // } switch ($definition['type']) { case 'CLASS': unset($definition['type']); if (count($definition['extends']) > 1) { \Sledgehammer\notice('Class: "' . $definition['class'] . '" Multiple inheritance is not allowed for classes'); $definition['extends'] = $definition['extends'][0]; } elseif (count($definition['extends']) == 1) { $definition['extends'] = $definition['extends'][0]; } else { unset($definition['extends']); } if (count($definition['implements']) == 0) { unset($definition['implements']); } $this->classes[$identifier] = $definition; break; case 'INTERFACE': unset($definition['type']); $this->interfaces[$identifier] = $definition; break; default: throw new Exception('Unsupported type: "' . $definition['type'] . '"'); } } }
/** * The object is used as an string. * * @return string */ public function __toString() { \Sledgehammer\notice('Object: "' . get_class($this) . '" is used as string'); return 'Object(' . get_class($this) . ')'; }
/** * De rij inlezen en naar de volgende regel gaan. * * @link http://php.net/manual/en/iterator.next.php */ public function next() { $this->values = []; $row = fgetcsv($this->fp, 0, $this->delimiter, $this->enclosure); if ($row) { // Is het einde (eof) nog niet bereikt? if ($row === array(null)) { // Empty row? return $this->next(); // Skip row } ++$this->index; foreach ($this->keys as $index => $key) { if (isset($row[$index])) { // Is er voor deze kolom een waarde? $this->values[$key] = $row[$index]; } else { $filename = strpos($this->filename, \Sledgehammer\PATH) === 0 ? substr($this->filename, strlen(\Sledgehammer\PATH)) : $this->filename; // Waar mogelijk het PATH er van af halen \Sledgehammer\notice('Row too short, missing column #' . ($index + 1) . ': "' . $this->keys[$index] . '" in ' . $filename . ' on line ' . ($this->index + 2), $row); // @todo Calculate line offset compared to the index () } } } else { $this->index = null; } }
/** * Constructor. * * @param string $contents The contents of a php script/file */ public function __construct($contents) { $previousError = error_get_last(); $this->tokens = token_get_all($contents); $error = error_get_last(); if ($error !== $previousError) { \Sledgehammer\notice($error['type'], $error['message']); } }
/** * Store the value in the cache. * * @param mixed $value The value * @param string|int $expires expires 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. */ protected function write($value, $expires = false) { if ($expires !== false) { if (is_string($expires)) { $expires = strtotime($expires); } if ($expires <= 3600) { // Is a ttl? $expires += time(); } elseif ($expires < time()) { \Sledgehammer\notice('Writing an expired cache entry', 'Use Cache->clear() to invalidate a cache entry'); } } $method = $this->_backend . '_write'; $this->{$method}($value, $expires); }
/** * Converts the tokens into a parsed path. * * @param string $tokens * * @return array */ public static function parse($path) { // Check if the path is cached static $cache = []; if (isset($cache[$path])) { return $cache[$path]; } $tokens = self::tokenize($path); if (count($tokens) === 0) { \Sledgehammer\notice('Path is empty'); return false; } $compiled = []; $length = count($tokens); $first = true; for ($i = 0; $i < $length; ++$i) { $token = $tokens[$i]; if ($i + 1 === $length) { $nextToken = array('T_END', ''); } else { $nextToken = $tokens[$i + 1]; } switch ($token[0]) { // TYPE_ANY case self::T_STRING: if ($first === false) { // Invalid chain? "[el]any" instead of "[el].any" \Sledgehammer\notice('Invalid chain, expecting a ".", "->" or "[" before "' . $token[1] . '"'); return false; } if ($nextToken[0] === self::T_OPTIONAL) { $compiled[] = array(self::TYPE_OPTIONAL, $token[1]); ++$i; } else { $compiled[] = array(self::TYPE_ANY, $token[1]); } break; // Chained T_ANY // Chained T_ANY case self::T_DOT: if ($first) { if ($nextToken[0] !== 'T_END') { \Sledgehammer\notice('Invalid "." in the path', 'Use "." for chaining, not at the beginning of a path'); return false; } $compiled[] = array(self::TYPE_SELF, $token[1]); break; } if ($nextToken[0] !== self::T_STRING) { \Sledgehammer\notice('Invalid "' . $token[1] . '" in path, expecting an identifier after "."'); return false; } if ($i + 2 !== $length && $tokens[$i + 2][0] === self::T_OPTIONAL) { $compiled[] = array(self::TYPE_OPTIONAL, $nextToken[1]); $i += 2; } else { $compiled[] = array(self::TYPE_ANY, $nextToken[1]); ++$i; } break; // TYPE_PROPERTY // TYPE_PROPERTY case self::T_ARROW: if ($nextToken[0] !== self::T_STRING) { \Sledgehammer\notice('Invalid "' . $token[1] . '" in path, expecting an identifier after an "->"'); return false; } if ($i + 2 !== $length && $tokens[$i + 2][0] === self::T_OPTIONAL) { $compiled[] = array(self::TYPE_OPTIONAL_PROPERTY, $nextToken[1]); $i += 2; } else { if (preg_match('/^[a-z_]{1}[a-z_0-9]*$/i', $nextToken[1]) != 1) { \Sledgehammer\notice('Invalid property identifier "' . $nextToken[1] . '" in path "' . $path . '"'); } $compiled[] = array(self::TYPE_PROPERTY, $nextToken[1]); ++$i; } break; // TYPE_ELEMENT // TYPE_ELEMENT case self::T_BRACKET_OPEN: if ($nextToken[0] !== self::T_STRING) { \Sledgehammer\notice('Unexpected token "' . $token[0] . '" in path, expecting T_STRING after ".["', $token); return false; } if ($i + 2 === $length) { \Sledgehammer\notice('Unmatched brackets, missing a "]" in path after "' . $nextToken[1] . '"'); return false; } if ($tokens[$i + 2][0] === self::T_OPTIONAL) { if ($i + 2 === $length || $tokens[$i + 3][0] !== self::T_BRACKET_CLOSE) { \Sledgehammer\notice('Unmatched brackets, missing a "]" in path after "' . $nextToken[1] . '?"'); return false; } $compiled[] = array(self::TYPE_OPTIONAL_ELEMENT, $nextToken[1]); $i += 3; } else { if ($tokens[$i + 2][0] !== self::T_BRACKET_CLOSE) { \Sledgehammer\notice('Unmatched brackets, missing a "]" in path after "' . $nextToken[1] . '"'); return false; } $compiled[] = array(self::TYPE_ELEMENT, $nextToken[1]); $i += 2; } break; case self::T_ALL_ELEMENTS: // [*] if ($nextToken[0] === self::T_STRING) { \Sledgehammer\notice('Invalid chain, expecting a ".", "->" or "[" before "' . $nextToken[1] . '"'); return false; } $offset = $i + 1; if ($nextToken[0] === self::T_DOT) { ++$offset; // skip the dot to prevent "Invalid beginning error" } // Merge remaining tokens as subpath $subpath = ''; $tokens = array_slice($tokens, $offset); foreach ($tokens as $token) { $subpath .= $token[1]; } $compiled[] = array(self::TYPE_SUBPATH, $subpath); return $compiled; default: \Sledgehammer\notice('Unexpected token: "' . $token[0] . '"'); return false; } $first = false; } $cache[$path] = $compiled; return $compiled; }
/** * Report the notice but prevent the (Laravel) error_handler to throw an exception. */ private static function hint($message, $information = null) { $handler = set_error_handler('error_log'); restore_error_handler(); if ($handler !== null) { if (is_array($handler) && is_object($handler[0]) && $handler[0] instanceof \Illuminate\Foundation\Bootstrap\HandleExceptions) { \Illuminate\Support\Facades\Log::notice($message); } else { \Sledgehammer\notice($message, $information); } } }
/** * Callback voor exceptions die buiten een try/catch block ge-throw-t worden. * * @param Exception $exception */ public function exceptionCallback($exception) { if (self::isThrowable($exception)) { if (count(debug_backtrace()) == 1) { // An uncaught exception? via the set_exception_handler() self::instance()->report($exception, '__UNCAUGHT_EXCEPTION__'); } else { \Sledgehammer\notice('Only the set_exception_handler() should call ErrorHandler->exceptionCallback. use \\Sledgehammer\\report_exception()', 'Use the <b>report_exception</b>($exception) for reporting to the default Errorhandler.<br />Or call the ErrorHander->report($exception) to target a specific instance.'); self::instance()->report($exception); } } else { self::report(E_USER_ERROR, 'Parameter $exception must be an Exception, instead of a ' . gettype($exception)); } }
/** * Build a closure which validates an item with the gives $conditions. * * @param mixed $conditions array|Closure|expression See Collection::where() for condition options * * @return callable */ protected function buildFilter($conditions) { if (\Sledgehammer\is_closure($conditions)) { return $conditions; } if (is_array($conditions)) { // Create filter that checks all conditions $logicalOperator = \Sledgehammer\extract_logical_operator($conditions); if ($logicalOperator === false) { if (count($conditions) > 1) { \Sledgehammer\notice('Conditions with multiple conditions require a logical operator.', "Example: array('AND', 'x' => 1, 'y' => 5)"); } $logicalOperator = 'AND'; } else { unset($conditions[0]); } $operators = []; foreach ($conditions as $path => $expectation) { if (preg_match('/^(.*) (' . \Sledgehammer\COMPARE_OPERATORS . ')$/', $path, $matches)) { unset($conditions[$path]); $conditions[$matches[1]] = $expectation; $operators[$matches[1]] = $matches[2]; } else { $operators[$path] = false; } } // @todo Build an optimized closure for when a single conditions is given. if ($logicalOperator === 'AND') { return function ($item) use($conditions, $operators) { foreach ($conditions as $path => $expectation) { $actual = PropertyPath::get($path, $item); $operator = $operators[$path]; if ($operator) { if (\Sledgehammer\compare($actual, $operator, $expectation) === false) { return false; } } elseif (\Sledgehammer\equals($actual, $expectation) === false) { return false; } } return true; // All conditions are met. }; } elseif ($logicalOperator === 'OR') { return function ($item) use($conditions, $operators) { foreach ($conditions as $path => $expectation) { $actual = PropertyPath::get($path, $item); $operator = $operators[$path]; if ($operator) { if (\Sledgehammer\compare($actual, $operator, $expectation) !== false) { return true; } } elseif (\Sledgehammer\equals($actual, $expectation) !== false) { return true; } } return false; // None of conditions are met. }; } else { throw new Exception('Unsupported logical operator "' . $logicalOperator . '", expecting "AND" or "OR"'); } } //'<= 5' or '10' // Compare the item directly with value given as $condition. if (is_string($conditions) && preg_match('/^(' . \Sledgehammer\COMPARE_OPERATORS . ') (.*)$/', $conditions, $matches)) { $operator = $matches[1]; $expectation = $matches[2]; } else { $expectation = $conditions; $operator = '=='; } return function ($value) use($expectation, $operator) { return \Sledgehammer\compare($value, $operator, $expectation); }; }
/** * 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); }
/** * Return a subsection of the collection based on the conditions. * * Convert the $conditions to SQL object when appropriate. * * auto converts * ['x_id' => null] to "x_id IS NULL" * ['x_id !=' => null] to "x_id IS NOT NULL" * 'hits' => 0] to "hits = '0'" (Because in mysql '' = 0 evaluates to true, '' = '0' to false) * * @param array $conditions * * @return Collection */ public function where($conditions) { if ($this->data !== null || is_string($this->sql) || is_object($conditions) && is_callable($conditions) || $this->sql->limit !== false || $this->sql->offset != 0) { return parent::where($conditions); } $db = Connection::instance($this->dbLink); $sql = $this->sql; $logicalOperator = \Sledgehammer\extract_logical_operator($conditions); if ($logicalOperator === false) { if (count($conditions) > 1) { \Sledgehammer\notice('Conditions with multiple conditions require a logical operator.', "Example: array('AND', 'x' => 1, 'y' => 5)"); } $logicalOperator = 'AND'; } else { unset($conditions[0]); } if ($logicalOperator === 'AND') { $method = 'andWhere'; } elseif ($logicalOperator === 'OR') { $method = 'orWhere'; } else { throw new Exception('Unsupported logical operator "' . $logicalOperator . '", expecting "AND" or "OR"'); } // The result are rows(fetch_assoc arrays), all conditions must be columnnames (or invalid) foreach ($conditions as $path => $value) { if (preg_match('/^(.*) (' . \Sledgehammer\COMPARE_OPERATORS . ')$/', $path, $matches)) { $column = $this->convertPathToColumn($matches[1]); $operator = $matches[2]; } else { $column = $this->convertPathToColumn($path); $operator = '=='; } if ($column === false) { // Converting to path failed? \Sledgehammer\array_key_unshift($conditions, 0, $logicalOperator); return parent::where($conditions); } if ($value === null) { switch ($operator) { case '==': $operator = 'IS'; $expectation = 'NULL'; break; case '!=': $operator = 'IS NOT '; $expectation = 'NULL'; break; case '>': case '<': case '>=': case '<=': $expectation = "''"; break; default: \Sledgehammer\warning('Unknown behavior for NULL values with operator "' . $operator . '"'); $expectation = $db->quote($expectation); break; } $sql = $sql->{$method}($column . ' ' . $operator . ' ' . $expectation); } else { if ($operator === '!=') { $sql = $sql->{$method}('(' . $column . ' != ' . $db->quote($value, PDO::PARAM_STR) . ' OR ' . $column . ' IS NULL)'); } elseif ($operator === 'IN') { if ((is_array($value) || $value instanceof Traversable) === false) { \Sledgehammer\notice('Operator IN expects an array or Traversable', $value); $value = explode(',', $value); } $quoted = []; foreach ($value as $val) { $quoted[] = $this->quote($db, $column, $val); } $sql = $sql->{$method}($column . ' ' . $operator . ' (' . implode(', ', $quoted) . ')'); } else { if ($operator === '==') { $operator = '='; } $sql = $sql->{$method}($column . ' ' . $operator . ' ' . $this->quote($db, $column, $value)); } } } return new self($sql, $this->dbLink); }
/** * Returns a new SQL with the $sql->where set to the given $where. * * @param string|array $where * * @return Sql */ public function where($where) { $sql = clone $this; if ($sql->where !== '') { \Sledgehammer\notice('Overruling where'); } $sql->where = $where; return $sql; }
/** * @param string $label * @param string $message * @param string $overwrite */ public static function send($label, $message, $overwrite = false) { if (self::isEnabled() === false) { return; } if (preg_match('/^(?<label>[a-z0-9\\-]+)(?<suffix>\\.[0-9]+)?$/i', $label, $match) == false) { \Sledgehammer\notice('Label: "' . $label . '" in invalid', 'A label may contain number, letters and "-"'); return; } $number = \Sledgehammer\array_value(self::$increments, $match['label']); if (isset($match['suffix'])) { // Has a suffix? $labelSuffix = $match[0]; if ($overwrite === false) { \Sledgehammer\notice('Overwrite flag required for label: "' . $label . '"'); return; } if ($number <= substr($match['suffix'], 1)) { self::$increments[$match['label']] = substr($match['suffix'], 1) + 1; } } elseif ($overwrite === false) { if ($number) { $label .= '.' . $number; } self::$increments[$match['label']] = $number + 1; } if (headers_sent($file, $line)) { if ($file == '' && $line == 0) { $location = ''; } else { $location = ', output started in ' . $file . ' on line ' . $line; } \Sledgehammer\notice('Couldn\'t sent header(s)' . $location); return; } $value = base64_encode($message); $length = strlen($value); // Prevent 325 net::ERR_RESPONSE_HEADERS_TOO_BIG in Google Chrome. if (self::$bytesSent + $length >= 240000) { // >= 235KiB? if (self::$bytesSent < 239950) { call_user_func(self::$headerAdd, 'DebugR-' . $label . ': ' . base64_encode('DebugR: TOO_MUCH_DATA')); } return; } if ($length <= 4000) { // Under 4KB? (96B for the label) $header = 'DebugR-' . $label . ': '; call_user_func(self::$headerAdd, $header . $value); self::$bytesSent += strlen($header) + $length; } else { // Send in 4KB chunks. call_user_func(self::$headerRemove, 'DebugR-' . $label); $chunks = str_split($value, 4000); foreach ($chunks as $index => $chunk) { $header = 'DebugR-' . $label . '.chunk' . $index . ': '; call_user_func(self::$headerAdd, $header . $chunk); self::$bytesSent += strlen($header); } self::$bytesSent += $length; } }
/** * Report MySQL warnings and notes if any. * * @param Sql|string $statement */ public function checkWarnings($statement) { if (isset($this->reportWarnings) && $this->reportWarnings === true) { $info = []; if ($statement instanceof Sql) { $info['SQL'] = (string) $statement; } $start = microtime(true); $this->previousInsertId = parent::lastInsertId(); $warnings = parent::query('SHOW WARNINGS'); if ($warnings === false) { $this->reportError('SHOW WARNINGS'); return; } $this->logger->totalDuration += microtime(true) - $start; if ($warnings->rowCount()) { foreach ($warnings->fetchAll(PDO::FETCH_ASSOC) as $warning) { \Sledgehammer\notice('SQL ' . strtolower($warning['Level']) . ' [' . $warning['Code'] . '] ' . $warning['Message'], $info); } // @todo Clear warnings // PDO/MySQL doesn't clear the warnings before CREATE/DROP DATABASE queries. } } }
/** * Limit the number of active transfers. * * @param int $max The allowed number of active connections. * * @throws Exception */ private static function throttle($max) { if (self::$pool === null) { return; } $max = intval($max); if ($max < 0) { \Sledgehammer\notice('Invalid throttle value: ' . $max); $max = 0; } if (count(self::$requests) <= $max) { return; } do { // Wait for (incomming) data if (curl_multi_select(self::$pool, 0.2) === -1) { usleep(100000); // wait 0.1 second } $activeTransfers = 0; foreach (self::$requests as $curl) { if ($curl->isComplete() == false) { ++$activeTransfers; } } } while ($activeTransfers > $max); }