/** * * Fetches the roles for a username. * * @param string $handle User handle to get roles for. * * @return array An array of discovered roles. * */ public function fetch($handle) { // force the full, real path to the file $file = realpath($this->_config['file']); // does the file exist? if (!Solar_File::exists($file)) { throw $this->_exception('ERR_FILE_NOT_READABLE', array('file' => $file)); } // load the file as an array of lines $lines = file($file); // the discovered roles $roles = array(); // loop through each line, find the group, then see if the user // is on the line anywhere foreach ($lines as $line) { // eliminate all spaces, which cause mismatches when we get to // in_array() later $line = preg_replace("/\\s/", '', $line); // break apart at first ':' $pos = strpos($line, ':'); // the group name is the part before the ':' $group = substr($line, 0, $pos); // the list of user handles comes after $tmp = substr($line, $pos + 1); $list = explode(',', $tmp); // is the user part of the group? if (in_array($handle, $list)) { $roles[] = $group; } } // done! return $roles; }
/** * * Write out a series of dirs and symlinks for a new Vendor source. * * @param string $vendor The Vendor name. * * @return void * */ protected function _exec($vendor = null) { // we need a vendor name, at least if (!$vendor) { throw $this->_exception('ERR_NO_VENDOR_NAME'); } $this->_outln("Removing links for vendor '{$vendor}' ..."); // build "foo-bar" and "FooBar" versions of the vendor name. $this->_inflect = Solar_Registry::get('inflect'); $this->_dashes = $this->_inflect->camelToDashes($vendor); $this->_studly = $this->_inflect->dashesToStudly($this->_dashes); // the base system dir $system = Solar::$system; // the links to remove (reverse order from make-vendor) $links = array("script/{$this->_dashes}", "include/Mock/{$this->_studly}", "include/Test/{$this->_studly}", "include/{$this->_studly}"); // remove the links foreach ($links as $link) { $this->_out(" Removing '{$link}' ... "); $path = "{$system}/{$link}"; if (Solar_File::exists($path)) { unlink($path); $this->_outln("done."); } else { $this->_outln("missing."); } } // done! $this->_outln("... done."); // reminders $this->_outln("Remember to remove '{$this->_studly}_App' from the " . "['Solar_Controller_Front']['classes'] element " . "in your config file."); $this->_outln("Remember to remove '{$this->_studly}_Model' from the " . "['Solar_Sql_Model_Catalog']['classes'] element " . "in your config file."); // need to write a recursive-remove method for Solar_Dir that will // delete only the symlinked files (not the real files) $this->_outln("You will need to remove the " . "'docroot/public/{$this->_studly}' directory yourself, as it " . "may contain copies of public assets (not links)."); }
/** * * Makes a symbolic link to a file or directory. * * @param string $src The source path of the real file or directory. * * @param string $tgt The target path for where to put the symlink. * * @param string $dir Change to this directory before creating the * symlink, typically the target directory; this helps when making * relative symlinks. * * @return string The last line from the [[php::exec() | ]] call to * create the symlink. * */ public static function make($src, $tgt, $dir = null) { // are we on a windows system prior to NT6? $is_win = strtolower(substr(PHP_OS, 0, 3)) == 'win'; if ($is_win && php_uname('r') < 6) { throw Solar_Symlink::_exception('ERR_WINDOWS_VERSION'); } // massage the change-dir a bit $dir = trim($dir); if ($dir) { $dir = Solar_Dir::fix($dir); } // is the source a directory or a file? $path = $dir . $src; $is_dir = Solar_Dir::exists($path); $is_file = Solar_File::exists($path); if (!$is_dir && !$is_file) { // no source found throw Solar_Symlink::_exception('ERR_SOURCE_NOT_FOUND', array('src' => $src, 'tgt' => $tgt, 'dir' => $dir, 'path' => $path)); } // find any existing path to the target if ($is_dir) { $path = Solar_Dir::exists($dir . $tgt); } else { $path = Solar_File::exists($dir . $tgt); } // does the target exist already? if ($path) { throw Solar_Symlink::_exception('ERR_TARGET_EXISTS', array('src' => $src, 'tgt' => $tgt, 'dir' => $dir, 'path' => $path)); } // escape arguments for the command $src = escapeshellarg($src); $tgt = escapeshellarg($tgt); $dir = escapeshellarg($dir); if ($is_win && $is_dir) { // windows directory return Solar_Symlink::_makeWinDir($src, $tgt, $dir); } elseif ($is_win && $is_file) { // windows file return Solar_Symlink::_makeWinFile($src, $tgt, $dir); } else { // non-windows return Solar_Symlink::_make($src, $tgt, $dir); } }
/** * * Fetch access privileges for a user handle and roles. * * @param string $handle The user handle. * * @param array $roles The user roles. * * @return array * */ public function fetch($handle, $roles) { // force the full, real path to the file $file = realpath($this->_config['file']); // does the file exist? if (!Solar_File::exists($file)) { throw $this->_exception('ERR_FILE_NOT_READABLE', array('file' => $this->_config['file'], 'realpath' => $file)); } $handle = trim($handle); // eventual access list for the handle and roles $list = array(); // get the access source and split into lines $src = file_get_contents($this->_config['file']); $src = preg_replace('/[ \\t]{2,}/', ' ', trim($src)); $lines = explode("\n", $src); foreach ($lines as $line) { $line = trim($line); // allow blank lines if ($line == '') { continue; } // allow comment lines $char = substr($line, 0, 1); if ($char == '#') { continue; } // $info keys are ... // 0 => "allow" or "deny" // 1 => "handle", "role", or "owner" // 2 => handle/role name (not used by 'owner' type) // 3 => class name // 4 => action name $info = explode(' ', $line); if ($info[1] == 'handle' && $info[2] == $handle || $info[1] == 'handle' && $info[2] == '+' && $handle || $info[1] == 'handle' && $info[2] == '*' || $info[1] == 'role' && in_array($info[2], $roles) || $info[1] == 'role' && $info[2] == '*' || $info[1] == 'owner') { // content owner // keep the line $list[] = array('allow' => $info[0] == 'allow' ? true : false, 'type' => $info[1], 'name' => $info[2], 'class' => $info[3], 'action' => $info[4]); } } return $list; }
/** * * Loads a class using the class stack prefixes. * * {{code: php * $stack = Solar::factory('Solar_Class_Stack'); * $stack->add('Base1'); * $stack->add('Base2'); * $stack->add('Base3'); * * $class = $stack->load('Name'); * // $class is now the first instance of '*_Name' found from the * // class stack, looking first for 'Base3_Name', then * // 'Base2_Name', then finally 'Base1_Name'. * }} * * @param string $name The class to load using the class stack. * * @param bool $throw Throw an exception if no matching class is found * in the stack (default true). When false, returns boolean false if no * matching class is found. * * @return string The full name of the loaded class. * * @throws Solar_Exception_ClassNotFound * */ public function load($name, $throw = true) { // some preliminary checks for valid class names if (!$name || $name != trim($name) || !ctype_alpha($name[0])) { if ($throw) { throw $this->_exception('ERR_CLASS_NOT_VALID', array('name' => $name, 'stack' => $this->_stack)); } else { return false; } } // make sure the name is upper-cased, then loop through the stack // to find it. $name = ucfirst($name); foreach ($this->_stack as $prefix) { // the full class name $class = "{$prefix}{$name}"; // pre-empt searching. // don't use autoload. if (class_exists($class, false)) { return $class; } // the related file $file = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; // does the file exist? if (!Solar_File::exists($file)) { continue; } // include it in a limited scope. we don't use Solar_File::load() // because we want to avoid exceptions. $this->_run($file); // did the class exist within the file? // don't use autoload. if (class_exists($class, false)) { // yes, we're done return $class; } } // failed to find the class in the stack if ($throw) { throw $this->_exception('ERR_CLASS_NOT_FOUND', array('class' => $name, 'stack' => $this->_stack)); } else { return false; } }
/** * * Loads the translation array for a given class. * * @param string $class The class name to load translations for. * * @return void * */ protected function _load($class) { // build the file name. note that we use the fixdir() // method, which automatically replaces '/' with the // correct directory separator. $base = str_replace('_', '/', $class); $file = Solar_Dir::fix($base . '/Locale/') . $this->_code . '.php'; // can we find the file? $target = Solar_File::exists($file); if ($target) { // put the locale values into the shared locale array $this->trans[$class] = (array) Solar_File::load($target); } else { // could not find file. // fail silently, as it's often the case that the // translation file simply doesn't exist. $this->trans[$class] = array(); } }
/** * * Finds a file in the path stack. * * Relative paths are honored as part of the include_path. * * {{code: php * $stack = Solar::factory('Solar_Path_Stack'); * $stack->add('path/1'); * $stack->add('path/2'); * $stack->add('path/3'); * * $file = $stack->find('file.php'); * // $file is now the first instance of 'file.php' found from the * // directory stack, looking first in 'path/3/file.php', then * // 'path/2/file.php', then finally 'path/1/file.php'. * }} * * @param string $file The file to find using the directory stack * and the include_path. * * @return mixed The relative path to the file, or false if not * found using the stack. * */ public function find($file) { foreach ($this->_stack as $dir) { $spec = $dir . $file; if (Solar_File::exists($spec)) { return $spec; } } return false; }
/** * * Finds the config file for a test case. * * The order of precedence is: * * 1. Use the value of --test-config when not empty. * * 2. Look for `$system/config/test/Vendor.config.php` and use that if it * exists. * * 3. Look for `$system/source/vendor/tests/config.php` and use that if it * exists. * * 4. No config for the test case. * * @param string $class The test case class to find configs for. * * @return string The config file location for the test case. * */ protected function _fetchTestCaseConfig($class) { // explicit test-config if ($this->_config['test_config']) { return $this->_config['test_config']; } // convenience var $system = Solar::$system; // strip the 'Test_' prefix, then get the vendor name $vendor = Solar_Class::vendor(substr($class, 5)); // look for a config/test/Vendor.config.php file $path = "{$system}/config/test/{$vendor}.config.php"; $file = Solar_File::exists($path); if ($file) { return $file; } // look for a source/vendor/tests/config.php file $dash = Solar_Registry::get('inflect')->camelToDashes($vendor); $path = "{$system}/source/{$dash}/tests/config.php"; $file = Solar_File::exists($path); if ($file) { return $file; } // no test config return null; }
/** * * Gets the option settings from the class hierarchy. * * @return array * */ protected function _getOptionSettings() { // the options to be set $options = array(); // find the parents of this class, including this class $parents = Solar_Class::parents(get_class($this), true); array_shift($parents); array_shift($parents); // get options.php for each parent class, as well as this class foreach ($parents as $class) { $file = str_replace('_', DIRECTORY_SEPARATOR, $class) . DIRECTORY_SEPARATOR . 'Info' . DIRECTORY_SEPARATOR . 'options.php'; $file = Solar_File::exists($file); if ($file) { $options = array_merge($options, (array) (include $file)); } } return $options; }
/** * * Finds the base directory from the include-path to the requested class. * * @param string $class The requested class file. * * @return string The base directory. * */ protected function _findBaseByClass($class) { $file = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; $base = Solar_File::exists($file); if ($base) { $neglen = -1 * strlen($file); $base = substr($base, 0, $neglen); return $base; } // no base yet, look for a dir (drop the .php, add a separator) $dir = substr($file, 0, -4); $base = Solar_Dir::exists($dir); if ($base) { $neglen = -1 * strlen($dir); $base = substr($base, 0, $neglen); return $base; } // still no base, we have a problem throw $this->_exception('ERR_NO_BASE_DIR', array('class' => $class)); }
/** * * Uses [[php::include() | ]] to run a script in a limited scope. * * @param string $file The file to include. * * @return mixed The return value of the included file. * */ public static function load($file) { Solar_File::$_file = Solar_File::exists($file); if (!Solar_File::$_file) { // could not open the file for reading $code = 'ERR_FILE_NOT_READABLE'; $text = Solar_Registry::get('locale')->fetch('Solar', $code); throw Solar::exception('Solar', $code, $text, array('file' => $file)); } // clean up the local scope, then include the file and // return its results. unset($file); return include Solar_File::$_file; }
/** * * Returns the path to a file under a specific class. * * @param string|object $spec The class or object to use as the base path. * * @param string $file Append this file path. * * @return string The path to the file under the class. * */ public static function file($spec, $file) { $dir = Solar_Class::dir($spec); return Solar_File::exists($dir . $file); }
/** * * Removes a file or directory symbolic link. * * Actually, this will remove the file or directory even if it's not * a symbolic link, so be careful with it. * * @param string $path The symbolic link to remove. * * @return void * */ public static function remove($path) { // are we on a windows system prior to NT6? $is_win = strtolower(substr(PHP_OS, 0, 3)) == 'win'; if ($is_win && php_uname('r') < 6) { throw Solar_Symlink::_exception('ERR_WINDOWS_VERSION'); } // does the path exist for removal? $is_dir = Solar_Dir::exists($path); $is_file = Solar_File::exists($path); if (!$is_dir && !$is_file) { throw Solar_Symlink::_exception('ERR_PATH_NOT_FOUND', array('path' => $path)); } // how to remove? if ($is_win) { // have to double up on removals because windows reports all // symlinks as files, even if they point to dirs. @unlink($path); Solar_Dir::rmdir($path); } else { // unix file or dir @unlink($path); } }