/** * * Given a class or object, add its vendor and its parent vendors to the * stack; optionally, add a standard suffix base to the vendor name. * * @param string|object $spec The class or object to find vendors of. * * @param string $base The suffix base to append to each vendor name. * * @return void * */ public function addByVendors($spec, $base = null) { // get the list of parents; retain Solar_Base $parents = Solar_Class::parents($spec, true); // if we have a suffix, put a separator on it if ($base) { $base = "_{$base}"; } // look through vendor names $old = null; foreach ($parents as $class) { $new = Solar_Class::vendor($class); if ($new != $old) { // not the same, add the current vendor name and suffix $this->add("{$new}{$base}"); } // retain old vendor for next loop $old = $new; } }
/** * * Returns the translated locale string for a class and key. * * Loads translations as needed. * * You can also pass an array of replacement values. If the `$replace` * array is sequential, this method will use it with vsprintf(); if the * array is associative, this method will replace "{:key}" with the array * value. * * For example: * * {{code: php * * $locale = Solar_Registry('locale'); * * $page = 2; * $pages = 10; * * // given a class of 'Solar_Example' with a locale string * // TEXT_PAGES => 'Page %d of %d', uses vsprintf() internally: * $replace = array($page, $pages); * echo $locale->fetch('Solar_Example', 'TEXT_PAGES', $pages, $replace); * // echo "Page 2 of 10" * * // given a class of 'Solar_Example' with a locale string * // TEXT_PAGES => 'Page {:page} of {:pages}', uses str_replace() * // internally: * $replace = array('page' => $page, 'pages' => $pages); * echo $locale->fetch('Solar_Example', 'TEXT_PAGES', $pages, $replace); * // echo "Page 2 of 10" * }} * * @param string|object $spec The class name (or object) for the translation. * * @param string $key The translation key. * * @param mixed $num Helps determine whether to get a singular * or plural translation. * * @param array $replace An array of replacement values for the string. * * @return string A translated locale string. * * @see _trans() * * @see Solar_Base::locale() * * @see Manual::Solar/Using_locales * */ public function fetch($spec, $key, $num = 1, $replace = null) { // is the spec an object? if (is_object($spec)) { // yes, find its class $class = get_class($spec); } else { // no, assume the spec is a class name $class = (string) $spec; } // does the translation key exist for this class? // pre-empts the stack check. $string = $this->_trans($class, $key, $num, $replace); if ($string !== null) { return $string; } // find all parents of the class, including the class itself $parents = array_reverse(Solar_Class::parents($class, true)); // add the vendor namespace to the stack for vendor-wide strings $vendor = Solar_Class::vendor($class); $parents[] = $vendor; // add Solar as the final fallback. if ($vendor != 'Solar') { $parents[] = 'Solar'; } // go through all parents and find the first matching key foreach ($parents as $parent) { // do we need to load locale strings for the class? if (!array_key_exists($parent, $this->trans)) { $this->_load($parent); } // does the key exist for the parent? $string = $this->_trans($parent, $key, $num, $replace); if ($string !== null) { // save it for the class so we don't need to go through the // stack again, and then we're done. $this->trans[$class][$key] = $this->trans[$parent][$key]; return $string; } } // never found a translation, return the requested key. return $key; }
/** * * Sets the class this app will extend from. * * @param string $class The app class name. * * @return void * */ protected function _setExtends($class) { // explicit as cli option? $extends = $this->_options['extends']; if ($extends) { $this->_extends = $extends; return; } // explicit as config value? if ($this->_model_name) { $extends = $this->_config['extends_model']; } else { $extends = $this->_config['extends']; } if ($extends) { $this->_extends = $extends; return; } // look at the vendor name and find a controller class $vendor = Solar_Class::vendor($class); if ($this->_model_name) { $name = "{$vendor}_Controller_Bread"; $file = $this->_target . "{$vendor}/Controller/Bread.php"; } else { $name = "{$vendor}_Controller_Page"; $file = $this->_target . "{$vendor}/Controller/Page.php"; } if (file_exists($file)) { $this->_extends = $name; return; } // final fallback: Solar_Controller_Page $this->_extends = 'Solar_Controller_Page'; return; }
/** * * Public interface to execute the command. * * This method... * * - populates and validates the option values * - calls _preExec() * - calls _exec() with the numeric parameters from the options * - calls _postExec() * * @param array $argv The command-line arguments from the user. * * @return void * * @todo Accept a Getopt object in addition to $argv array? * */ public function exec($argv = null) { // get the command-line arguments if ($argv === null) { // use the $_SERVER values $argv = $this->_request->server['argv']; // remove the argument pointing to this command array_shift($argv); } else { $argv = (array) $argv; } // set options, populate values, and validate parameters $this->_getopt->populate($argv); if (!$this->_getopt->validate()) { // need a better way to throw exceptions with specific error // messages throw $this->_exception('ERR_INVALID_OPTIONS', array('invalid' => $this->_getopt->getInvalid(), 'options' => $this->_getopt->options)); } // retain the option values, minus the numeric params $this->_options = $this->_getopt->values(); $params = array(); foreach ($this->_options as $key => $val) { if (is_int($key)) { $params[] = $val; unset($this->_options[$key]); } } // special behavior for -V/--version if ($this->_options['version']) { $vendor = Solar_Class::vendor($this); $this->_out("{$vendor} command-line tool, Solar version "); $this->_outln(Solar::apiVersion() . '.'); return; } // call pre-exec $skip_exec = $this->_preExec(); // should we skip the main execution? if ($skip_exec !== true) { // call _exec() with the numeric params from getopt call_user_func_array(array($this, '_exec'), $params); } // call post-exec $this->_postExec(); // done, return terminal to normal colors $this->_out("%n"); }
/** * * Generates a simple exception, but does not throw it. * * This method attempts to automatically load an exception class * based on the error code, falling back to parent exceptions * when no specific exception classes exist. For example, if a * class named 'Vendor_Example' extended from 'Vendor_Base' throws an * exception or error coded as 'ERR_FILE_NOT_FOUND', the method will * attempt to return these exception classes in this order ... * * 1. Vendor_Example_Exception_FileNotFound (class specific) * * 2. Vendor_Base_Exception_FileNotFound (parent specific) * * 3. Vendor_Example_Exception (class generic) * * 4. Vendor_Base_Exception (parent generic) * * 5. Vendor_Exception (generic for all of vendor) * * The final fallback is always the generic Solar_Exception class. * * Note that this method only generates the object; it does not * throw the exception. * * {{code: php * $class = 'My_Example_Class'; * $code = 'ERR_SOMETHING_WRONG'; * $text = 'Something is wrong.'; * $info = array('foo' => 'bar'); * $exception = Solar::exception($class, $code, $text, $info); * throw $exception; * }} * * In general, you shouldn't need to use this directly in classes * extended from [[Class::Solar_Base]]. Instead, use * [[Solar_Base::_exception() | $this->_exception()]] for automated * picking of the right exception class from the $code, and * automated translation of the error message. * * @param string|object $spec The class name (or object) that generated * the exception. * * @param mixed $code A scalar error code, generally a string. * * @param string $text Any error message text. * * @param array $info Additional error information in an associative * array. * * @return Solar_Exception * */ public static function exception($spec, $code, $text = '', $info = array()) { // is the spec an object? if (is_object($spec)) { // yes, find its class $class = get_class($spec); } else { // no, assume the spec is a class name $class = (string) $spec; } // drop 'ERR_' and 'EXCEPTION_' prefixes from the code // to get a suffix for the exception class $suffix = $code; if (strpos($suffix, 'ERR_') === 0) { $suffix = substr($suffix, 4); } elseif (strpos($suffix, 'EXCEPTION_') === 0) { $suffix = substr($suffix, 10); } // convert "STUDLY_CAP_SUFFIX" to "Studly Cap Suffix" ... $suffix = ucwords(strtolower(str_replace('_', ' ', $suffix))); // ... then convert to "StudlyCapSuffix" $suffix = str_replace(' ', '', $suffix); // build config array from params $config = array('class' => $class, 'code' => $code, 'text' => $text, 'info' => (array) $info); // get all parent classes, including the class itself $stack = array_reverse(Solar_Class::parents($class, true)); // add the vendor namespace to the stack as a fallback, even though // it's not strictly part of the hierarchy, for generic vendor-wide // exceptions. $vendor = Solar_Class::vendor($class); if ($vendor != 'Solar') { $stack[] = $vendor; } // add Solar as the final fallback $stack[] = 'Solar'; // track through class stack and look for specific exceptions foreach ($stack as $class) { try { $obj = Solar::factory("{$class}_Exception_{$suffix}", $config); return $obj; } catch (Exception $e) { // do nothing } } // track through class stack and look for generic exceptions foreach ($stack as $class) { try { $obj = Solar::factory("{$class}_Exception", $config); return $obj; } catch (Exception $e) { // do nothing } } // last resort: a generic Solar exception return Solar::factory('Solar_Exception', $config); }
/** * * Sets the model stack. * * @param array $classes An array of class prefixes to use for the model * stack. * * @return void * */ protected function _setStack($classes) { if (!$classes) { // add per the vendor on this catalog and its inheritance $parents = Solar_Class::parents(get_class($this), true); array_shift($parents); // Solar_Base $old_vendor = false; foreach ($parents as $class) { $new_vendor = Solar_Class::vendor($class); if ($new_vendor != $old_vendor) { $classes[] = "{$new_vendor}_Model"; } $old_vendor = $new_vendor; } } // build the class stack $this->_stack = Solar::factory('Solar_Class_Stack'); $this->_stack->add($classes); }
/** * * Sets the class this model will extend from. * * @param string $class The model class name. * * @return void * */ protected function _setExtends($class) { // explicit as cli option? $extends = $this->_options['extends']; if ($extends) { $this->_extends = $extends; return; } // explicit as config value? $extends = $this->_config['extends']; if ($extends) { $this->_extends = $this->_config['extends']; return; } // look at the class name and find a Vendor_Sql_Model class $vendor = Solar_Class::vendor($class); $file = $this->_target . "{$vendor}/Sql/Model.php"; if (file_exists($file)) { $this->_extends = "{$vendor}_Sql_Model"; return; } // final fallback: Solar_Sql_Model $this->_extends = 'Solar_Sql_Model'; return; }
/** * * 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; }
/** * * Find the vendors of a given class or object and its parents. * * @param mixed $spec An object, or a class name. * * @return array The vendor names of the class or object hierarchy. * */ public static function vendors($spec) { // vendor listing $stack = array(); // get the list of parents $parents = Solar_Class::parents($spec, true); // look through vendor names $old = null; foreach ($parents as $class) { $new = Solar_Class::vendor($class); if ($new != $old) { // not the same, add the current vendor name and suffix $stack[] = $new; } // retain old vendor for next loop $old = $new; } return $stack; }
/** * * Sets the class this app will extend from. * * @param string $class The app class name. * * @return void * */ protected function _setExtends($class) { // explicit as cli option? $extends = $this->_options['extends']; if ($extends) { $this->_extends = $extends; return; } // explicit as a config value? $extends = $this->_config['extends']; if ($extends) { $this->_extends = $extends; return; } // look at the vendor name and find a controller class $vendor = Solar_Class::vendor($class); $name = "{$vendor}_Controller_Command"; $file = $this->_target . "{$vendor}/Controller/Command.php"; if (file_exists($file)) { $this->_extends = $name; return; } // final fallback: Solar_Controller_Command $this->_extends = 'Solar_Controller_Command'; }