/**
  * @return the primary key of the source directory
  */
 public function getOrSave($sourceDir)
 {
     /// make sure that sourceDir ends with the separator to make sure
     // only the correct entries are selected / saved
     $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
     $select = $this->select()->where('directory = ?', $sourceDir);
     $stmt = $select->query(Zend_Db::FETCH_ASSOC);
     if ($row = $stmt->fetch()) {
         return $row['source_id'];
     }
     return $this->insert(array('directory' => $sourceDir));
 }
/**
 * This function will use the resource cache to lookup all controllers and their methods.  Then it
 * will create a Triumph_TemplateFileTag instance for each method.
 *
 * @param  string $sourceDir            the root directory of the project in question
 * @param  string $detectorDbFileName   the location of the resource cache SQLite file; as created by Triumph
 * @param  boolean $doSkip              out parameter; if TRUE then this detector does not know how
 *                                      to detect templates for the given source directory; this situation
 *                                      is different than zero templates being detected.
 * @return Triumph_TemplateFileTag[]  array of Triumph_TemplateFileTag instances the detected template files and their variables
 */
function detectTemplates($sourceDir, $detectorDbFileName, &$doSkip)
{
    $allTemplates = array();
    if (!is_file($detectorDbFileName)) {
        return $allTemplates;
    }
    // need to check that this detector is able to recognize the directory structure of sourceDir
    // if not, then we need to skip detection by returning immediately and setting $doSkip to TRUE.
    // by skipping detection, we prevent any detected templates from the previous detection script
    // from being deleted.
    $doSkip = TRUE;
    // for laravel, we look for the artisan script. if we don't have the artisan script assume
    // that this source is not a laravel project.
    $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
    if (!is_file($sourceDir . 'artisan')) {
        return $allTemplates;
    }
    $doSkip = FALSE;
    // add your logic here; usually it will consist of querying the SQLite database in $detectorDbFileName
    // recursing though the call stack and picking the method calls for templates.
    $pdo = Zend_Db::factory('Pdo_Sqlite', array("dbname" => $detectorDbFileName));
    $callStackTable = new Triumph_CallStackTable($pdo);
    $callStacks = $callStackTable->load();
    // figure out the variables
    $scopes = $callStackTable->splitScopes($callStacks);
    // now go through each scope, looking for calls to $this->load->view
    foreach ($scopes as $scope) {
        // list of all method calls used to find calls to view method
        $methodCalls = $callStackTable->getMethodCalls($scope);
        // list of all property accesses, make sure that calls to view method
        // are used on the loader member variables
        $propertyCalls = $callStackTable->getPropertyCalls($scope);
        $variableCalls = $callStackTable->getVariables($scope);
        foreach ($methodCalls as $destinationVariable => $call) {
            if (\opstring\compare_case($call->methodName, 'make') == 0 && \opstring\compare_case($call->objectName, 'View') == 0) {
                // get the function arguments to View::make
                // the first argument to make is the template file
                // the second argument is an array of data (the template vars)
                $template = $variableCalls[$call->functionArguments[0]];
                $data = $variableCalls[$call->functionArguments[1]];
                if ($template) {
                    $currentTemplate = new Triumph_TemplateFileTag();
                    $currentTemplate->variables = array();
                    $currentTemplate->fullPath = '';
                    $currentTemplate->fullPath = $template->scalarValue;
                    // view file may have an extension; if it has an extension use that; otherwise use the default (.php)
                    if (stripos($currentTemplate->fullPath, '.') === FALSE) {
                        $currentTemplate->fullPath .= '.php';
                    }
                    // not using realpath() so that Triumph can know that a template file has not yet been created
                    // or the programmer has a bug in the project.
                    $currentTemplate->fullPath = \opstring\replace($currentTemplate->fullPath, '/', DIRECTORY_SEPARATOR);
                    $currentTemplate->fullPath = \opstring\replace($currentTemplate->fullPath, '\\', DIRECTORY_SEPARATOR);
                    $currentTemplate->fullPath = $sourceDir . 'app' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $currentTemplate->fullPath;
                    $allTemplates[] = $currentTemplate;
                }
                // argument 2 is an array of template variables
                if (isset($variableCalls[$call->functionArguments[1]])) {
                    $variableCall = $variableCalls[$call->functionArguments[1]];
                    $arrayKeys = $callStackTable->getArrayKeys($scope, $variableCall->destinationVariable);
                    foreach ($arrayKeys as $key) {
                        // add the siguil here; editor expects us to return variables
                        $currentTemplate->variables[] = '$' . $key;
                    }
                    $allTemplates[count($allTemplates) - 1] = $currentTemplate;
                }
            }
        }
    }
    return $allTemplates;
}
function coreResources($dir, $codeIgniterSystemDir, &$allTags)
{
    $comment = '';
    // the core libraries are automatically added to the CI super object
    $codeIgniterSystemDir = \opstring\ensure_ends_with($codeIgniterSystemDir, DIRECTORY_SEPARATOR);
    foreach (glob($codeIgniterSystemDir . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . '*.php') as $libFile) {
        $baseName = basename($libFile, '.php');
        if ('CodeIgniter' != $baseName && 'Common' != $baseName && 'Controller' != $baseName && 'Exceptions' != $baseName && 'Exceptions' != $baseName && 'Model' != $baseName) {
            $propertyType = 'CI_' . $baseName;
            $propertyName = strtolower($baseName);
            $allTags[] = Triumph_DetectedTag::CreateMember('CI_Controller', $propertyName, $propertyType, $comment);
            $allTags[] = Triumph_DetectedTag::CreateMember('CI_Model', $propertyName, $propertyType, $comment);
        }
    }
    // the DB library, this requires checking to see if active record is enabled
    if (is_file($dir . '/application/config/database.php')) {
        $db = array();
        include $dir . '/application/config/database.php';
        $propertyType = isset($active_record) && $active_record ? 'CI_DB_active_record' : 'CI_DB_driver';
        $allTags[] = Triumph_DetectedTag::CreateMember('CI_Controller', 'db', $propertyType, $comment);
        $allTags[] = Triumph_DetectedTag::CreateMember('CI_Model', 'db', $propertyType, $comment);
    }
    // alias the Loader library; seems that there is two properties
    $allTags[] = Triumph_DetectedTag::CreateMember('CI_Controller', 'load', 'CI_Loader', $comment);
    $allTags[] = Triumph_DetectedTag::CreateMember('CI_Model', 'load', 'CI_Loader', $comment);
}
Esempio n. 4
0
 function testEnsureEnds()
 {
     $this->assertEquals('/home/user/', \opstring\ensure_ends_with('/home/user', '/'));
     $this->assertEquals('/home/user/', \opstring\ensure_ends_with('/home/user/', '/'));
     $this->assertEquals('/home/user//', \opstring\ensure_ends_with('/home/user//', '/'));
 }
/**
 *
 * @param  string $sourceDir            the root directory of the project in question
 * @param  string $resourceDbFileName   the location of the resource cache SQLite file; as created by Triumph
 * @param  boolean $doSkip              out parameter; if TRUE then this detector does not know how
 *                                      to detect URLs for the given source directory; this situation
 *                                      is different than zero URLs being detected.
 * @return Triumph_Resource[]         array of Triumph_Resource instances the detected tags
 */
function detectTags($sourceDir, $resourceDbFileName, &$doSkip)
{
    $allTags = array();
    if (!is_file($resourceDbFileName)) {
        return $allTags;
    }
    // need to check that this detector is able to recognize the directory structure of sourceDir
    // if not, then we need to skip detection by returning immediately and setting $doSkip to TRUE.
    // by skipping detection, we prevent any detected tags from the previous detection script
    // from being deleted.
    $doSkip = TRUE;
    // for laravel, we look for the artisan script. if we don't have the artisan script assume
    // that this source is not a laravel project.
    $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
    if (!is_file($sourceDir . 'artisan')) {
        return $allTags;
    }
    $doSkip = FALSE;
    // get all of the configured aliases, laravel automatically imports
    // certain classes into the root namespace.
    // load the laravel bootstrap
    $bootstrapFile = $sourceDir . 'bootstrap' . DIRECTORY_SEPARATOR . 'autoload.php';
    $startFile = $sourceDir . 'bootstrap' . DIRECTORY_SEPARATOR . 'start.php';
    if (is_file($bootstrapFile) && is_file($startFile)) {
        require $bootstrapFile;
        $app = (require_once $startFile);
        // laravel defines a set of aliases that get added in to the
        // root namespace at run-time. we loop through the aliases
        // and add a method with the class name being the alias name
        // and the method name being the method from the class  being
        // aliased
        $pdo = Zend_Db::factory('Pdo_Sqlite', array("dbname" => $resourceDbFileName));
        $resourceTable = new Triumph_ResourceTable($pdo);
        // I want to read the aliases from the config, but unfortunately
        // we need more info.  laravel facades work in this manner:
        // facade => ioc key => class name
        // the config gives us the facade names, but the ioc keys are hidden in the
        // facade.
        // for now, we extract the default ioc keys from the Application
        // the default aliases, IOC keys, and classes are in
        // laravel/framework/src/Illuminate/Foundation/Application.php
        // ideally, we should be able to find "bind" calls in the service
        // providers
        $arrDefaultAliases = defaultAliases();
        $arrIocKeyToFacade = defaultBindings();
        foreach ($arrDefaultAliases as $strIocKey => $strFullClassName) {
            // the full names in the laravel config file do not start
            // with the root namespace, but we want Triumph to know
            // that these are fully qualified class names
            $strFullClassName = \opstring\ensure_begins_with($strFullClassName, '\\');
            $strParent = get_parent_class($strFullClassName);
            $strAlias = $arrIocKeyToFacade[$strIocKey];
            // if the alias is a facade, get the methods of the facade class that is
            // being adapted
            // the facade is created by asking the app for a
            $arrResources = $resourceTable->FindPublicMethodsMembersForClass($strFullClassName, $sourceDir);
            foreach ($arrResources as $resource) {
                if (Triumph_Resource::TYPE_METHOD == $resource->type) {
                    $detectedTag = Triumph_DetectedTag::CreateMethod($strAlias, $resource->identifier, $resource->returnType, '\\', $resource->comment);
                    // set the member as being static
                    // laravel implements facades using the __callStatic
                    // magic method
                    // since triumph is smart enough to only show static methods
                    // when a static call is being made, we must set the static flag
                    // to true, else code completion will not work
                    $detectedTag->isStatic = TRUE;
                    $detectedTag->signature = $resource->signature;
                    $allTags[] = $detectedTag;
                } else {
                    if (Triumph_Resource::TYPE_MEMBER == $resource->type) {
                        $detectedTag = Triumph_DetectedTag::CreateMember($strAlias, $resource->identifier, $resource->returnType, '\\', $resource->comment);
                        $detectedTag->signature = $resource->signature;
                        $detectedTag->isStatic = TRUE;
                        $allTags[] = $detectedTag;
                    }
                }
            }
        }
    }
    return $allTags;
}
/**
 * This function will lookup all database connections for the given source.  Then it
 * will create a Triumph_DatabaseTag connection.
 *
 * @param  string $sourceDir            the root directory of the project in question
 * @param  boolean $doSkip              out parameter; if TRUE then this detector does not know how
 *                                      to detect databases for the given source directory; this situation
 *                                      is different than zero databases being detected.
 * @return Triumph_DatabaseTag[]      array of Triumph_DatabaseTag instances the detected databases
 */
function detectDatabases($sourceDir, &$doSkip)
{
    $allDatabases = array();
    // need to check that this detector is able to recognize the directory structure of sourceDir
    // if not, then we need to skip detection by returning immediately and setting $doSkip to TRUE.
    // by skipping detection, we prevent any detected databases from the previous detection script
    // from being deleted.
    $doSkip = TRUE;
    // for laravel, we look for the artisan script. if we don't have the artisan script assume
    // that this source is not a laravel project.
    $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
    if (!is_file($sourceDir . 'artisan')) {
        return $allDatabases;
    }
    $doSkip = FALSE;
    // load the laravel bootstrap
    $bootstrapFile = $sourceDir . 'bootstrap' . DIRECTORY_SEPARATOR . 'autoload.php';
    $startFile = $sourceDir . 'bootstrap' . DIRECTORY_SEPARATOR . 'start.php';
    if (is_file($bootstrapFile) && is_file($startFile)) {
        require $bootstrapFile;
        $app = (require_once $startFile);
        $arrDbs = Config::get('database.connections');
        foreach ($arrDbs as $strConnectionLabel => $arrDb) {
            if (strcasecmp('mysql', $arrDb['driver']) == 0) {
                // laravel supports read/write connections, look
                // for them
                if (isset($arrDb['read'])) {
                    $tag = tagFromDbArray($arrDb['read']['host'], $strConnectionLabel . ' read', $arrDb);
                    $allDatabases[] = $tag;
                }
                if (isset($arrDb['write'])) {
                    // only add the write db if the host is different
                    $tag = tagFromDbArray($arrDb['write']['host'], $strConnectionLabel . ' write', $arrDb);
                    if (strcasecmp($arrDb['read'], $arrDb['write']) != 0) {
                        $allDatabases[] = $tag;
                    }
                }
                if (isset($arrDb['host'])) {
                    $tag = tagFromDbArray($arrDb['host'], $strConnectionLabel, $arrDb);
                    $allDatabases[] = $tag;
                }
            }
        }
    }
    return $allDatabases;
}
/**
 * This function will look for config files in the given source directory. Then it
 * will create a Triumph_ConfigTag instance for each config file that it finds.
 *
 * @param  string $sourceDir            the root directory of the project in question
 * @param  boolean $doSkip              out parameter; if TRUE then this detector does not know how
 *                                      to detect config for the given source directory; this situation
 *                                      is different than zero condifs being detected.
 * @return Triumph_ConfigTag[]        array of Triumph_ConfigTag instances the detected configs
 */
function detectConfigs($sourceDir, &$doSkip)
{
    $allConfigs = array();
    $doSkip = TRUE;
    // need to check that this detector is able to recognize the directory structure of sourceDir
    // if not, then we need to skip detection by returning immediately and setting $doSkip to TRUE.
    // by skipping detection, we prevent any detected URLs from the previous detection script
    // from being deleted.
    $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
    if (!is_file($sourceDir . 'application/config/database.php')) {
        return $allConfigs;
    }
    $doSkip = FALSE;
    $allConfigs = array(new Triumph_ConfigTag('AutoLoad', realpath($sourceDir . 'application/config/autoload.php')), new Triumph_ConfigTag('Config', realpath($sourceDir . 'application/config/config.php')), new Triumph_ConfigTag('Constants', realpath($sourceDir . 'application/config/constants.php')), new Triumph_ConfigTag('Database', realpath($sourceDir . 'application/config/database.php')), new Triumph_ConfigTag('DocTypes', realpath($sourceDir . 'application/config/doctypes.php')), new Triumph_ConfigTag('Foreign Characters', realpath($sourceDir . 'application/config/foreign_chars.php')), new Triumph_ConfigTag('Hooks', realpath($sourceDir . 'application/config/hooks.php')), new Triumph_ConfigTag('Mime Types', realpath($sourceDir . 'application/config/mimes.php')), new Triumph_ConfigTag('Profiler', realpath($sourceDir . 'application/config/profiler.php')), new Triumph_ConfigTag('Routes', realpath($sourceDir . 'application/config/routes.php')), new Triumph_ConfigTag('Smileys', realpath($sourceDir . 'application/config/smileys.php')), new Triumph_ConfigTag('User Agents', realpath($sourceDir . 'application/config/user_agents.php')));
    return $allConfigs;
}
/**
 * This function will use the resource cache to lookup all controllers and their methods.  Then it
 * will create a Triumph_TemplateFile instance for each method.
 *
 * @param  string $sourceDir            the root directory of the project in question
 * @param  string $detectorDbFileName   the location of the resource cache SQLite file; as created by Triumph
 * @param  boolean $doSkip              out parameter; if TRUE then this detector does not know how
 *                                      to detect templates for the given source directory; this situation
 *                                      is different than zero templates being detected.
 * @return Triumph_TemplateFile[]     array of Triumph_TemplateFile instances the detected template files and their variables
 */
function detectTemplates($sourceDir, $detectorDbFileName, &$doSkip)
{
    $doSkip = TRUE;
    $allTemplates = array();
    if (!is_file($detectorDbFileName)) {
        return $allTemplates;
    }
    // need to check that this detector is able to recognize the directory structure of sourceDir
    // if not, then we need to skip detection by returning immediately and setting $doSkip to TRUE.
    // by skipping detection, we prevent any detected templates from the previous detection script
    // from being deleted.
    $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
    if (!is_file($sourceDir . 'application/config/routes.php') || !is_file($sourceDir . 'application/config/config.php')) {
        // this source directory does not contain a code igniter directory.
        return $allTemplates;
    }
    $doSkip = FALSE;
    // add your logic here; usually it will consist of querying the SQLite database in $detectorDbFileName
    // recursing though the call stack and picking the method calls for templates.
    $pdo = Zend_Db::factory('Pdo_Sqlite', array("dbname" => $detectorDbFileName));
    $callStackTable = new Triumph_CallStackTable($pdo);
    $callStacks = $callStackTable->load();
    // figure out the variables
    $scopes = $callStackTable->splitScopes($callStacks);
    // now go through each scope, looking for calls to $this->load->view
    foreach ($scopes as $scope) {
        // list of all method calls used to find calls to view method
        $methodCalls = $callStackTable->getMethodCalls($scope);
        // list of all property accesses, make sure that calls to view method
        // are used on the loader member variables
        $propertyCalls = $callStackTable->getPropertyCalls($scope);
        $variableCalls = $callStackTable->getVariables($scope);
        foreach ($methodCalls as $destinationVariable => $call) {
            if (\opstring\compare_case($call->methodName, 'view') == 0 && isset($propertyCalls[$call->objectName])) {
                // is this view call of a loader object ?
                $propertyCall = $propertyCalls[$call->objectName];
                if (\opstring\compare('$this', $propertyCall->objectName) == 0 && (\opstring\compare('load', $propertyCall->propertyName) == 0 || \opstring\compare('loader', $propertyCall->propertyName) == 0)) {
                    $currentTemplate = new Triumph_TemplateFileTag();
                    $currentTemplate->variables = array();
                    $currentTemplate->fullPath = '';
                    if (count($call->functionArguments) >= 1) {
                        // argument 1 of the view method call is the template file
                        // most of the time views are given as relative relatives; starting from the application/views/ directory
                        // for now ignore variable arguments
                        if (isset($variableCalls[$call->functionArguments[0]])) {
                            $variableCall = $variableCalls[$call->functionArguments[0]];
                            if ($variableCall->type == Triumph_CallStack::SCALAR) {
                                $currentTemplate->fullPath = $variableCall->scalarValue;
                                // view file may have an extension; if it has an extension use that; otherwise use the default (.php)
                                if (stripos($currentTemplate->fullPath, '.') === FALSE) {
                                    $currentTemplate->fullPath .= '.php';
                                }
                                // not using realpath() so that Triumph can know that a template file has not yet been created
                                // or the programmer has a bug in the project.
                                $currentTemplate->fullPath = \opstring\replace($currentTemplate->fullPath, '/', DIRECTORY_SEPARATOR);
                                $currentTemplate->fullPath = \opstring\replace($currentTemplate->fullPath, '\\', DIRECTORY_SEPARATOR);
                                $currentTemplate->fullPath = $sourceDir . 'application' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $currentTemplate->fullPath;
                                // push it now just in case the template does not get any variables
                                $allTemplates[] = $currentTemplate;
                            }
                        }
                    }
                    if (count($call->functionArguments) >= 2 && !empty($currentTemplate->fullPath)) {
                        // argument 2 is an array of template variables
                        if (isset($variableCalls[$call->functionArguments[1]])) {
                            $variableCall = $variableCalls[$call->functionArguments[1]];
                            $arrayKeys = $callStackTable->getArrayKeys($scope, $variableCall->destinationVariable);
                            foreach ($arrayKeys as $key) {
                                // add the siguil here; editor expects us to return variables
                                $currentTemplate->variables[] = '$' . $key;
                            }
                            $allTemplates[count($allTemplates) - 1] = $currentTemplate;
                        }
                    }
                }
            }
        }
    }
    return $allTemplates;
}
/**
 * This function will lookup all database connections for the given source.  Then it
 * will create a Triumph_DatabaseTag connection.
 *
 * @param  string $sourceDir            the root directory of the project in question
 * @param  boolean $doSkip              out parameter; if TRUE then this detector does not know how
 *                                      to detect databases for the given source directory; this situation
 *                                      is different than zero databases being detected.
 * @return Triumph_DatabaseTag[]      array of Triumph_DatabaseTag instances the detected databases
 */
function detectDatabases($sourceDir, &$doSkip)
{
    $doSkip = TRUE;
    $allDatabases = array();
    $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
    if (!is_file($sourceDir . 'application/config/development/database.php') && !is_file($sourceDir . 'application/config/database.php')) {
        return $allDatabases;
    }
    // need to check that this detector is able to recognize the directory structure of sourceDir
    // if not, then we need to skip detection by returning immediately and setting $doSkip to TRUE.
    // by skipping detection, we prevent any detected databases from the previous detection script
    // from being deleted.
    $doSkip = FALSE;
    // need this define so that we can include code igniter files directly
    define('BASEPATH', '');
    // database config file can be in the environment directory
    // for now just get the development environment info
    if (is_file($sourceDir . 'application/config/development/database.php')) {
        include $sourceDir . 'application/config/development/database.php';
        if ($db) {
            foreach ($db as $groupName => $groupConnection) {
                if (strcasecmp('mysql', $groupConnection['dbdriver']) == 0) {
                    $tag = tagFromDbArray($groupName, $groupConnection);
                    $allDatabases[] = $tag;
                }
            }
        }
    } else {
        if (is_file($sourceDir . 'application/config/database.php')) {
            $db = array();
            include $sourceDir . 'application/config/database.php';
            if ($db) {
                foreach ($db as $groupName => $groupConnection) {
                    if (\opstring\compare_case('mysql', $groupConnection['dbdriver']) == 0 || \opstring\compare_case('mysqli', $groupConnection['dbdriver']) == 0) {
                        $tag = tagFromDbArray($groupName, $groupConnection);
                        $allDatabases[] = $tag;
                    }
                }
            }
        }
    }
    return $allDatabases;
}
/**
 * Resolves the given class name - method name pair into the URL.
 *
 * @param  array  $route the code igniter $routes array
 * @param  array  $config the code igniter $config array
 * @param  string $subDirectory  if non-empty means that the controller is in a sub-directory of application/controllers
 * @param  string $fileName the name of the controller file
 * @param  string $className the name of the controller class
 * @param  string $methodName the name of the method
 * @param  string $extra any extra segments to append to the URL
 * @return Triumph_Url the full URL after routing rules have been applied.
 */
function makeUrl($route, $config, $subDirectory, $fileName, $className, $methodName, $extra)
{
    // this function was taken from http://codeigniter.com/forums/viewthread/102438
    // with the following changes:
    // 1. including routes directly instead of using CI object
    //    as I was having problems including the bootstap file here
    $uri = '';
    if ($subDirectory) {
        $uri .= $subDirectory . '/';
    }
    $uri .= $className . '/' . $methodName;
    if ($extra) {
        $uri .= '/' . $extra;
    }
    // in case subdir has leading slash; routes usually don't
    $uri = ltrim($uri, '/');
    $uri = strtolower($uri);
    if (in_array($uri, $route)) {
        $uri = array_search($uri, $route);
    } else {
        foreach ($route as $singleRoute => $replace) {
            $singleRoute = preg_split('/(\\(.+?\\))/', $singleRoute, -1, PREG_SPLIT_DELIM_CAPTURE);
            $replace = preg_split('/(\\$\\d+)/', $replace, -1, PREG_SPLIT_DELIM_CAPTURE);
            if (count($singleRoute) != count($replace)) {
                continue;
            }
            $newroute = $newreplace = '';
            for ($i = 0; $i < count($singleRoute); $i++) {
                if ($i % 2) {
                    $newroute .= $replace[$i];
                    $newreplace .= $singleRoute[$i];
                } else {
                    $newroute .= $singleRoute[$i];
                    $newreplace .= $replace[$i];
                }
                $newreplace = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $newreplace));
                if (preg_match("#^{$newreplace}\$#", $uri)) {
                    $uri = preg_replace("#^{$newreplace}\$#", $newroute, $uri);
                    break;
                }
            }
        }
    }
    if (0 == \opstring\compare('default_controller', $uri)) {
        $uri = '';
    }
    // respect the url suffix and index page from the config. for urls, make sure there is only one ending '/'
    // url already has leading slash
    // make sure url never has leading slash
    $indexPage = $config['index_page'];
    $indexPage = \opstring\ensure_ends_with($indexPage, '/');
    if (!empty($uri)) {
        $url = $indexPage . trim($uri . '/');
        $url = ltrim($url, '/');
    } else {
        $url = '';
    }
    if (isset($config['url_suffix']) && $config['url_suffix']) {
        $url .= $config['url_suffix'];
    }
    $triumphUrl = new Triumph_UrlTag($url, $fileName, $className, $methodName);
    return $triumphUrl;
}
/**
 * This function will use the resource cache to lookup all controllers and their methods.  Then it
 * will create a Triumph_Url instance for each method; note that the routes file is 
 * also consulted and we will generate URLs for the default controller.
 *
 * @param  string $sourceDir            the root directory of the project in question
 * @param  string $resourceDbFileName   the location of the resource cache SQLite file; as created by Triumph
 * @param  string $host                 the hostname of the application; this will be used a the prefix on all URLs
 * @param  boolean $doSkip               out parameter; if TRUE then this detector does not know how
 *                                       to detect URLs for the given source directory; this situation
 *                                       is different than zero URLs being detected.
 * @return Triumph_UrlTag[]           array of Triumph_UrlTag instances the detected URLs
 */
function detectUrls($sourceDir, $resourceDbFileName, $host, &$doSkip)
{
    $allUrls = array();
    if (!is_file($resourceDbFileName)) {
        return $allUrls;
    }
    // need to check that this detector is able to recognize the directory structure of sourceDir
    // if not, then we need to skip detection by returning immediately and setting $doSkip to TRUE.
    // by skipping detection, we prevent any detected URLs from the previous detection script
    // from being deleted.
    $doSkip = TRUE;
    // for laravel, we look for the artisan script. if we don't have the artisan script assume
    // that this source is not a laravel project.
    $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
    if (!is_file($sourceDir . 'artisan')) {
        return $allUrls;
    }
    $doSkip = FALSE;
    // load the laravel bootstrap
    $bootstrapFile = $sourceDir . 'bootstrap' . DIRECTORY_SEPARATOR . 'autoload.php';
    $startFile = $sourceDir . 'bootstrap' . DIRECTORY_SEPARATOR . 'start.php';
    if (is_file($bootstrapFile) && is_file($startFile)) {
        require $bootstrapFile;
        $app = (require_once $startFile);
        $app->setRequestForConsoleEnvironment();
        $artisan = Illuminate\Console\Application::start($app);
        $router = $app['router'];
        $routes = $router->getRoutes();
        foreach ($routes as $route) {
            $arr = array('host' => $route->domain(), 'uri' => $route->uri(), 'name' => $route->getName(), 'action' => $route->getActionName());
            if (!empty($route->domain())) {
                $url = $route->domain();
            } else {
                $url = $host;
            }
            $url .= $route->uri();
            // special treatment, we dont want urls to end in double slashes
            // if the route is the home route'/'
            if (\opstring\ends_with($url, '//')) {
                $url = substr($url, 0, -1);
            }
            $pos = strpos($route->getActionName(), '@');
            $className = '';
            $methodName = '';
            $fileName = '';
            if ($pos !== FALSE) {
                $className = \opstring\before($route->getActionName(), '@');
                $methodName = \opstring\after($route->getActionName(), '@');
                // find the file that the class/method are in
                $pdo = Zend_Db::factory('Pdo_Sqlite', array("dbname" => $resourceDbFileName));
                $resourceTable = new Triumph_ResourceTable($pdo);
                $allMethods = $resourceTable->FindPublicMethod($className, $methodName, $sourceDir);
                if (count($allMethods)) {
                    $fileName = $allMethods[0]->fullPath;
                }
            }
            $triumphUrl = new Triumph_UrlTag($url, $fileName, $className, $methodName);
            $allUrls[] = $triumphUrl;
        }
    }
    return $allUrls;
}
/**
 * This function will look for config files in the given source directory. Then it
 * will create a Triumph_ConfigTag instance for each config file that it finds.
 *
 * @param  string $sourceDir            the root directory of the project in question
 * @param  boolean $doSkip              out parameter; if TRUE then this detector does not know how
 *                                      to detect config for the given source directory; this situation
 *                                      is different than zero condifs being detected.
 * @return Triumph_ConfigTag[]        array of Triumph_ConfigTag instances the detected configs
 */
function detectConfigs($sourceDir, &$doSkip)
{
    $allConfigs = array();
    // need to check that this detector is able to recognize the directory structure of sourceDir
    // if not, then we need to skip detection by returning immediately and setting $doSkip to TRUE.
    // by skipping detection, we prevent any detected URLs from the previous detection script
    // from being deleted.
    $doSkip = TRUE;
    // need to check that this detector is able to recognize the directory structure of sourceDir
    // if not, then we need to skip detection by returning immediately and setting $doSkip to TRUE.
    // by skipping detection, we prevent any detected URLs from the previous detection script
    // from being deleted.
    // for laravel, we look for the artisan script. if we don't have the artisan script assume
    // that this source is not a laravel project.
    $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
    if (!is_file($sourceDir . 'artisan')) {
        return $allConfigs;
    }
    $doSkip = FALSE;
    $allConfigs = array(new Triumph_ConfigTag('App', realpath($sourceDir . 'app/config/app.php')), new Triumph_ConfigTag('Auth', realpath($sourceDir . 'app/config/auth.php')), new Triumph_ConfigTag('Cache', realpath($sourceDir . 'app/config/cache.php')), new Triumph_ConfigTag('Compile', realpath($sourceDir . 'app/config/compile.php')), new Triumph_ConfigTag('Database', realpath($sourceDir . 'app/config/database.php')), new Triumph_ConfigTag('Mail', realpath($sourceDir . 'app/config/mail.php')), new Triumph_ConfigTag('Queue', realpath($sourceDir . 'app/config/queue.php')), new Triumph_ConfigTag('Remote', realpath($sourceDir . 'app/config/remote.php')), new Triumph_ConfigTag('Session', realpath($sourceDir . 'app/config/session.php')), new Triumph_ConfigTag('View', realpath($sourceDir . 'app/config/view.php')), new Triumph_ConfigTag('Workbench', realpath($sourceDir . 'app/config/workbench.php')));
    return $allConfigs;
}