/**
 *
 * @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_DetectedTag[]         array of Triumph_DetectedTag instances the detected tags
 */
function detectTags($sourceDir, $resourceDbFileName, &$doSkip)
{
    $allTags = 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;
    $sourceDir = \opstring\ensure_ends_with($sourceDir, DIRECTORY_SEPARATOR);
    $codeIgniterSystemDir = locateSystemDir($sourceDir);
    if (\opstring\length($codeIgniterSystemDir) <= 0) {
        return $allTags;
    }
    $doSkip = FALSE;
    // need this define so that we can include code igniter files directly
    define('BASEPATH', '');
    coreResources($sourceDir, $codeIgniterSystemDir, $allTags);
    libraryResources($sourceDir, $codeIgniterSystemDir, $allTags);
    // TODO: handle more than one application
    $appDir = $sourceDir . DIRECTORY_SEPARATOR . 'application';
    $modelDir = $appDir . DIRECTORY_SEPARATOR . 'models';
    modelResources($sourceDir, $modelDir, $allTags);
    // the "super" object
    $allTags[] = Triumph_DetectedTag::CreateMethod('CI_Controller', 'get_instance', '\\CI_Controller');
    return $allTags;
}
/**
 *
 * @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;
}