示例#1
0
    public static function get_response($in_input)
    {
        if (JxBotConverse::user_input_looks_strange($in_input)) {
            return 'Your last comment looks a bit strange.';
        }
        // ** configurable?
        /* cap general server requests (safety); should be configurable
        		as people have different host specs; 300 recommended for small shared host */
        $cap_bot_ipm = JxBotConfig::option('sys_cap_bot_ipm', 300);
        if ($cap_bot_ipm > 0) {
            $stmt = JxBotDB::$db->prepare('SELECT COUNT(*) FROM log 
				WHERE stamp >= DATE_SUB(NOW(), INTERVAL 1 MINUTE)');
            $stmt->execute();
            $last_min_total = intval($stmt->fetchAll(PDO::FETCH_NUM)[0][0]);
            if ($last_min_total >= $cap_bot_ipm) {
                return 'Sorry, I\'m too busy to chat right now.  Please come back later.';
            }
        }
        /* count interaction */
        JxBotDB::$db->exec('UPDATE stats SET interactions = interactions + 1');
        /* start timer */
        $start_time = microtime(true);
        /* initalize tracking variables */
        JxBotConverse::$match_time = 0.0;
        JxBotConverse::$service_time = 0.0;
        JxBotConverse::$iq_score = 0.0;
        JxBotConverse::$category_stack = array();
        $fault = false;
        /* run the bot */
        try {
            $output = JxBotConverse::srai($in_input);
        } catch (Exception $err) {
            $output = $err->getMessage();
            $fault = true;
        }
        /* end timer */
        $end_time = microtime(true);
        //print 'IQ '.JxBotConverse::$iq_score. '<br>';
        /* log this interaction */
        if (trim($output) == '') {
            $fault = true;
        }
        if ($fault) {
            JxBotConverse::$iq_score = -1;
        }
        JxBotConverse::log($in_input, $output, $end_time - $start_time, JxBotConverse::$match_time, JxBotConverse::$service_time, JxBotConverse::$iq_score);
        /* return the bot response */
        if ($fault) {
            return 'I do apologise.  I seem to be experiencing a positronic malfunction.';
        }
        return $output;
    }
示例#2
0

<?php 
        JxWidget::textfield(array('name' => 'input', 'label' => 'Administrator', 'max' => 150, 'autofocus' => true));
        ?>


<p><?php 
        JxWidget::button('Talk');
        ?>
</p>


<div class="left">
<img src="<?php 
        print JxBotConfig::bot_url();
        ?>
jxbot/core/gfx/robot-small.png" id="chat-robot">
</div>


<div class="bubble" style="max-width: 80%;">
<div class="bubble-top"><div class="bubble-corner-tl"></div><div class="bubble-corner-tr"></div></div>
<div class="bubble-left"></div>
<div class="bubble-content">

<?php 
        print $response;
        ?>

</div>
示例#3
0
} else {
    if (isset($_POST['bot_name'])) {
        //JxBotConfig::set_option('bot_name', $_POST['bot_name']);
        foreach ($_POST as $key => $value) {
            if (substr($key, 0, 4) == 'bot_') {
                JxBotConfig::set_option($key, $value);
            }
        }
        JxBotConfig::save_configuration();
    }
    ?>

<p><div class="field"><label for="bot_name">Bot Name: </label>
<input type="text" name="bot_name" id="bot_name" size="40" value="<?php 
    print JxBotConfig::option('bot_name');
    ?>
"></div></p>

<div class="field"><label for="bot_birthday">Birthday: </label>
<input type="text" name="bot_birthday" id="bot_birthday" size="12" value="<?php 
    print JxBotConfig::option('bot_birthday');
    ?>
"> (YYYY/MM/DD)</div>



<p><input type="submit" value="Save"></p>


<?php 
}
示例#4
0
?>
</div>


<h2>Language Processing</h2>

<div class="field"><label for="pre_strip_accents">Strip Accents:</label>
<?php 
JxWidget::toggle_switch('pre_strip_accents', JxBotConfig::option('pre_strip_accents'));
?>
<br><small>(strip accents during normalisation; good for English)</small></div>


<h2>Security</h2>

<div class="field"><label for="sys_cap_bot_ipm">Bot Load Maximum: </label>
<input type="text" name="sys_cap_bot_ipm" id="sys_cap_bot_ipm" size="6" value="<?php 
print JxBotConfig::option('sys_cap_bot_ipm');
?>
"><br><small>(interactions per minute; 0 = unlimited)</small></div>

<div class="field"><label for="admin_timeout">Administration Timeout: </label>
<input type="text" name="admin_timeout" id="admin_timeout" size="6" value="<?php 
print JxBotConfig::option('admin_timeout');
?>
"><br><small>(minutes; 0 = no timeout)</small></div>


<p class="left" id="buttons"><button type="submit" name="action" value="save">Save</button></p>

示例#5
0
 public function generate($in_context)
 {
     switch ($this->name) {
         case 'system':
             /* security implications; will not implement until reasonable
                controls are developed and an on/off switch */
         /* security implications; will not implement until reasonable
            controls are developed and an on/off switch */
         case 'javascript':
             /* server-side javascript is not implemented */
             break;
         case 'think':
             $this->text_value($in_context);
             break;
         case 'template':
         case 'gossip':
             /* doesn't do anything in this AIML interpreter
                and is removed in AIML 2.0 */
         /* doesn't do anything in this AIML interpreter
            and is removed in AIML 2.0 */
         case 'x-learn-1':
             /* AIML 1 learn tag to be filtered on import and contents
                passed through without further action */
             return $this->text_value($in_context);
         case 'random':
             $count = $this->child_element_count();
             $index = mt_rand(1, $count) - 1;
             return $this->child_element($index)->text_value($in_context);
         case 'condition':
             $loop = false;
             // preparation for AIML 2 loop; will need to keep a stack & check the stack so it can be toggled
             // by a <loop/> element anywhere in the depth of the <li>
             do {
                 $count = $this->child_element_count('li');
                 if ($count == 0) {
                     $predicate = $this->child_or_attr_named($in_context, 'name');
                     $value = JxBotConverse::predicate($predicate);
                     $pattern = $this->child_or_attr_named($in_context, 'value');
                     if (JxBotElement::matches_simple_pattern($value, $pattern)) {
                         return $this->text_value($in_context, array('name', 'value'));
                     }
                 } else {
                     $count = $this->child_element_count();
                     $predicate = $this->child_or_attr_named($in_context, 'name', null);
                     if ($predicate !== null) {
                         $value = JxBotConverse::predicate($predicate);
                         for ($i = 0; $i < $count; $i++) {
                             $item = $this->child_element($i);
                             $pattern = $item->child_or_attr_named($in_context, 'value', null);
                             if ($pattern === null) {
                                 return $item->text_value($in_context, array('value'));
                             }
                             if (JxBotElement::matches_simple_pattern($value, $pattern)) {
                                 return $item->text_value($in_context, array('value'));
                             }
                         }
                     } else {
                         for ($i = 0; $i < $count; $i++) {
                             $item = $this->child_element($i);
                             $predicate = $item->child_or_attr_named($in_context, 'name');
                             $value = JxBotConverse::predicate($predicate);
                             $pattern = $item->child_or_attr_named($in_context, 'value', null);
                             if ($pattern === null) {
                                 return $item->text_value($in_context, array('value', 'name'));
                             }
                             if (JxBotElement::matches_simple_pattern($value, $pattern)) {
                                 return $item->text_value($in_context, array('value', 'name'));
                             }
                         }
                     }
                 }
             } while ($loop);
             break;
         case 'star':
         case 'thatstar':
         case 'topicstar':
             $index = intval($this->child_or_attr_named($in_context, 'index', 1));
             return $this->get_capture($in_context, $this->name, $index);
         case 'srai':
             return JxBotConverse::srai($this->text_value($in_context));
         case 'sr':
             /* srai star /srai */
             return JxBotConverse::srai($this->get_capture($in_context, 'star', 1));
         case 'bot':
         case 'get':
             $name = trim($this->child_or_attr_named($in_context, 'name'));
             if ($name !== '') {
                 if ($this->name == 'get') {
                     return JxBotConverse::predicate($name);
                 } else {
                     if ($name == 'size') {
                         return JxBotNLData::pattern_count();
                     } else {
                         if ($name == 'age') {
                             return JxBotElement::compute_age();
                         } else {
                             if ($name == 'birthday') {
                                 return JxBotElement::birthday();
                             } else {
                                 return JxBotConfig::predicate($name);
                             }
                         }
                     }
                 }
             }
             break;
         case 'tag':
             /* non-standard Tag feature */
             $name = trim($this->child_or_attr_named($in_context, 'name'));
             return $in_context->tag_value($name);
         case 'id':
             return JxBotConverse::predicate('id');
         case 'size':
             /* we return the number of patterns, which is equivalent to AIML standard
             			category count; since in JxBot one category != one pattern. */
             return JxBotNLData::pattern_count();
         case 'vocabulary':
             return JxBotNLData::word_count();
         case 'version':
             return JxBot::VERSION;
         case 'program':
             return JxBot::PROGRAM . ' ' . JxBot::VERSION;
         case 'set':
             $name = trim($this->child_or_attr_named($in_context, 'name'));
             if ($name != '') {
                 $value = $this->text_value($in_context, array('name'));
                 JxBotConverse::set_predicate($name, $value);
                 return $value;
             }
             break;
         case 'date':
             $php_format = 'r';
             /* default - AIML 1.0 - we specify date & time format */
             return date($php_format);
         case 'uppercase':
             return JxBotNL::upper($this->text_value($in_context));
         case 'lowercase':
             return JxBotNL::lower($this->text_value($in_context));
         case 'formal':
             return JxBotNL::formal($this->text_value($in_context));
         case 'sentence':
             return JxBotNL::sentence($this->text_value($in_context));
         case 'explode':
             return JxBotNL::explode($this->text_value($in_context));
         case 'normalize':
             return JxBotNL::template_normalize($this->text_value($in_context));
         case 'denormalize':
             return JxBotNL::template_denormalize($this->text_value($in_context));
         case 'gender':
             if (count($this->children) == 0) {
                 return JxBotNL::remap('gender', $this->get_capture($in_context, 'star', 1));
             }
             return JxBotNL::remap('gender', $this->text_value($in_context));
         case 'person':
             if (count($this->children) == 0) {
                 return JxBotNL::remap('person', $this->get_capture($in_context, 'star', 1));
             }
             return JxBotNL::remap('person', $this->text_value($in_context));
         case 'person2':
             if (count($this->children) == 0) {
                 return JxBotNL::remap('person2', $this->get_capture($in_context, 'star', 1));
             }
             return JxBotNL::remap('person2', $this->text_value($in_context));
         case 'map':
             $map_name = $this->child_or_attr_named($in_context, 'name');
             return JxBotNL::remap($map_name, $this->text_value($in_context, array('name')));
         case 'that':
             $indicies = JxBotElement::indicies($this->child_or_attr_named($in_context, 'index'));
             $in_response = count($indicies) >= 1 ? $indicies[0] : 1;
             $in_sentence = count($indicies) >= 2 ? $indicies[1] : 1;
             $response = JxBotConverse::history_response($in_response - 1);
             $sentences = JxBotNL::split_sentences($response);
             if ($in_sentence < 1 || $in_sentence > count($sentences)) {
                 return '';
             }
             return $sentences[$in_sentence - 1];
         case 'input':
             $indicies = JxBotElement::indicies($this->child_or_attr_named($in_context, 'index'));
             $in_request = count($indicies) >= 1 ? $indicies[0] : 1;
             $in_sentence = count($indicies) >= 2 ? $indicies[1] : 1;
             $request = JxBotConverse::history_request($in_request - 1);
             $sentences = JxBotNL::split_sentences($request);
             if ($in_sentence < 1 || $in_sentence > count($sentences)) {
                 return '';
             }
             return $sentences[$in_sentence - 1];
         case 'request':
             $index = intval($this->child_or_attr_named($in_context, 'index', 1));
             return JxBotConverse::history_request($index - 1);
         case 'response':
             $index = intval($this->child_or_attr_named($in_context, 'index', 1));
             return JxBotConverse::history_response($index - 1);
         default:
             /* push unknown content & tags through to output */
             // ! REVIEW:  A better policy might be to tightly control which tags are
             //            allowed through, or provide an appropriate system option.
             return $this->flatten();
     }
 }
示例#6
0
                }
                JxBotConfig::save_configuration();
            }
            ?>


<div class="field"><label for="bot_active">Online:</label>
<?php 
            JxWidget::toggle_switch('bot_active', JxBotConfig::option('bot_active'));
            ?>
</div>


<div class="field"><label for="admin_user">Administration Username: </label>
<input type="text" name="admin_user" id="admin_user" size="20" value="<?php 
            print JxBotConfig::option('admin_user');
            ?>
"></div>

<div class="field"><label for="bot_password">Change Password: </label>
<input type="text" name="bot_password" id="bot_password" size="20"></div>



<p class="left" id="buttons"><button type="submit" name="action" value="save">Save</button></p>



<?php 
        }
    }
示例#7
0
    private static function page_complete()
    {
        ?>
<h1>Installation Complete!</h1>

<p>Installation was successful.</p>

<p>You can now <a href="<?php 
        print JxBotConfig::bot_url();
        ?>
jxbot/">login to your bot's administration panel</a> to upload AIML and further configure your bot.</p>

<?php 
    }
示例#8
0
文件: nl.php 项目: jhawcroft/jxbot
 public static function normalise($in_input)
 {
     //if ($in_keep_wildcards) return JxBotNL::normalise_pattern($in_input);
     /* preparation */
     $output = ' ' . $in_input . ' ';
     // leading & trailing space to help with substitution matching
     // consider replacing vertical & unusual horizontal whitespace (tabs) with all spaces here
     $output = JxBotNL::upper($output);
     /* do `tagging` in a different routine; really a pre-normalisation function */
     /* `substitution` normalisations; substitutions, abbreviations, spelling */
     $output = JxBotNL::apply_substitutions($output);
     /* `pattern fitting` normalisations */
     if (JxBotConfig::option('pre_strip_accents', 0) == 1) {
         $output = JxBotNL::strip_accents($output);
     }
     $output = JxBotNL::strip_punctuation($output);
     $output = JxBotNL::split_words($output);
     return $output;
 }
示例#9
0
    JxBotConfig::save_configuration();
}
if (isset($_REQUEST['del-name']) && trim($_REQUEST['del-name']) !== '') {
    JxBotConfig::bot_delete_prop($_REQUEST['del-name']);
    JxBotConfig::save_configuration();
}
if (isset($_POST['action']) && $_POST['action'] == 'Save') {
    //JxBotConfig::set_option('bot_name', $_POST['bot_name']);
    foreach ($_POST as $key => $value) {
        if (substr($key, 0, 4) == 'bot_') {
            JxBotConfig::set_option($key, $value);
        }
    }
    JxBotConfig::save_configuration();
}
$bot_properties = JxBotConfig::bot_properties();
$rows_per_col = ceil(count($bot_properties) / 2.0);
function editable_section(&$properties, $row_count)
{
    if (count($properties) == 0) {
        return;
    }
    print '<table>';
    for ($i = 0; $i < $row_count; $i++) {
        $prop = array_shift($properties);
        if ($prop === null) {
            break;
        }
        print '<tr>';
        print '<td style="width: 10em;">' . $prop[1] . '</td>';
        print '<td><input type="text" name="' . $prop[0] . '" size="20" value="' . $prop[2] . '" style="width:95%"></td>';
示例#10
0
    public static function widget_timezone()
    {
        ?>
<select name="bot_tz" id="bot_tz">
<option value=""></option>
<?php 
        $timezone_identifiers = DateTimeZone::listIdentifiers();
        foreach ($timezone_identifiers as $tz) {
            print '<option value="' . $tz . '" ' . (JxBotConfig::option('bot_tz') == $tz ? ' selected="true"' : '') . '>' . $tz . '</option>';
        }
        ?>
</select>
<?php 
    }
示例#11
0
function show_process_status()
{
    $stmt = JxBotDB::$db->prepare('SELECT COUNT(*) FROM file WHERE last_update > DATE_SUB(NOW(), INTERVAL 20 SECOND)');
    $stmt->execute();
    $recent_status_changes = $stmt->fetchAll(PDO::FETCH_NUM)[0][0] != 0;
    ?>

<h2>Status</h2>

<?php 
    if ($recent_status_changes == false) {
        ?>
Idle.
<?php 
    } else {
        ?>

<p style="margin-top: -20px; "><img src="<?php 
        print JxBotConfig::bot_url();
        ?>
jxbot/core/gfx/43.GIF" style="vertical-align: middle; margin-right: 1.5em"> <?php 
        $stmt = JxBotDB::$db->prepare('SELECT status,name FROM file WHERE status LIKE \'Loading%\'');
        $stmt->execute();
        $status = $stmt->fetchAll(PDO::FETCH_NUM);
        if (count($status) == 0) {
            $status = 'Checking...';
        } else {
            $status = $status[0][0] . ', ' . $status[0][1];
        }
        print $status;
        ?>
</p>

<script type="text/javascript">

// ! TODO:  Can be improved with an AJAX call to avoid reloading the page

window.setTimeout(function() {
	window.location = '?page=import';
}, 10000);

</script>
<?php 
    }
}
示例#12
0
文件: admin.php 项目: jhawcroft/jxbot
    public static function check_and_login()
    {
        $inputs = JxBotUtil::inputs('username,password');
        /* check the user hasn't logged in too often recently */
        $stmt = JxBotDB::$db->prepare('SELECT COUNT(*) FROM login
			WHERE stamp > DATE_SUB(NOW(), INTERVAL 1 MINUTE)
				AND username=?');
        $stmt->execute(array($inputs['username']));
        $recent_logins = intval($stmt->fetchAll(PDO::FETCH_NUM)[0][0]);
        if ($recent_logins > 5) {
            return false;
        }
        /* are credentials wrong? */
        if (JxBotConfig::option('admin_user') != $inputs['username'] || JxBotConfig::option('admin_hash') != hash('sha256', $inputs['password'])) {
            $stmt = JxBotDB::$db->prepare('INSERT INTO login
				(username, note) VALUES (?, ?)');
            $stmt->execute(array($inputs['username'], 'failure'));
            return false;
        }
        /* do the login */
        $_SESSION['jxbot-admin'] = 1;
        $stmt = JxBotDB::$db->prepare('INSERT INTO login
			(username, note) VALUES (?, ?)');
        $stmt->execute(array($inputs['username'], 'success'));
        $_SESSION['jxbot-last'] = time();
        /* generate the admin page */
        JxBotAdmin::admin_generate();
        return true;
    }
示例#13
0
文件: jxbot.php 项目: jhawcroft/jxbot
 public static function init_client()
 {
     JxBotConfig::setup_environment();
 }
示例#14
0
 public static function small_delete_icon()
 {
     print '<img src="' . JxBotConfig::bot_url() . 'jxbot/core/gfx/delete16.png" alt="Delete">';
 }
示例#15
0
    JxBotConfig::def_delete_pred($_REQUEST['del-name']);
}
if (isset($_POST['save'])) {
    foreach ($_POST as $key => $value) {
        if (substr($key, 0, 4) == 'def_') {
            JxBotConfig::set_option($key, $value);
        }
    }
    JxBotConfig::save_configuration();
}
JxWidget::hidden('save', 1);
?>


<?php 
$bot_properties = JxBotConfig::client_defaults();
$rows_per_col = ceil(count($bot_properties) / 2.0);
function editable_section(&$properties, $row_count)
{
    if (count($properties) == 0) {
        return;
    }
    print '<table>';
    for ($i = 0; $i < $row_count; $i++) {
        $prop = array_shift($properties);
        if ($prop === null) {
            break;
        }
        print '<tr>';
        print '<td style="width: 10em;">' . $prop[1] . '</td>';
        print '<td><input type="text" name="' . $prop[0] . '" size="20" value="' . $prop[2] . '" style="width:95%"></td>';
示例#16
0
 public static function process_scheduled()
 {
     /* detatch from invoking HTTP process so we can't be interrupted */
     JxBotAsyncLoader::detatch_http_request();
     /* ensure we are the only instance running this process */
     try {
         if (!JxBotExclusion::get_exclusive()) {
             return;
         }
     } catch (Exception $err) {
         JxBotAsyncLoader::log(JxBotAsyncLoader::LOG_LEVEL_ERROR, $err->getMessage(), '');
         return;
     }
     /* iterate through all scheduled files */
     while (true) {
         $stmt = JxBotDB::$db->prepare('SELECT name FROM file WHERE status = \'Scheduled\' ORDER BY name LIMIT 1');
         $stmt->execute();
         $next = $stmt->fetchAll(PDO::FETCH_NUM);
         if (count($next) == 0) {
             return;
         }
         /* we're done */
         $file = $next[0][0];
         /* flag the file to indicate we're processing it, and prevent a loop if something is amiss */
         JxBotAsyncLoader::set_file_status($file, 'Loading');
         /* does the file actually exist? */
         $path = JxBotConfig::aiml_dir() . $file;
         if (!file_exists($path)) {
             JxBotAsyncLoader::set_file_status($file, 'Not Available');
             continue;
         }
         /* run the AIML importer */
         $importer = new JxBotAimlImport();
         $result = $importer->import($path);
         // ! TODO:  The notices, warnings and errors of this mechanism should be
         //          sent to us via our log() method, not passed back in an array. **
         /* check for errors and notices */
         if (is_array($result)) {
             JxBotAsyncLoader::set_file_status($file, 'Loaded');
             JxBotAsyncLoader::log(JxBotAsyncLoader::LOG_LEVEL_NOTICE, 'Loaded.', $file);
             /* log the results */
             foreach ($result as $notice) {
                 JxBotAsyncLoader::log(JxBotAsyncLoader::LOG_LEVEL_WARNING, $notice, $file);
             }
         } else {
             JxBotAsyncLoader::set_file_status($file, 'Load Error');
             JxBotAsyncLoader::log(JxBotAsyncLoader::LOG_LEVEL_ERROR, $result, $file);
         }
     }
 }
示例#17
0
 protected function walk($in_parent_id, $in_term_index)
 {
     /* check search depth to prevent infinite recursion */
     $this->search_depth++;
     if ($this->search_depth > JxBotEngine::MAX_SEARCH_DEPTH) {
         throw new Exception('Too much recursion (in pattern search)');
     }
     /* look in this branch for all possible matching sub-branches;
     		ie. an exact match with the input term, or, a wildcard or complex pattern term
     		such as a bot property or AIML 2 'set' */
     $stmt = JxBotDB::$db->prepare("SELECT id,expression,is_terminal FROM pattern_node \n\t\t\tWHERE parent=? AND ( (expression = ? AND sort_key IN (0,5)) OR (sort_key NOT IN (0,5)) ) \n\t\t\tORDER BY sort_key");
     $current_term = $this->get_term($in_term_index);
     $stmt->execute(array($in_parent_id, $current_term));
     $possible_branches = $stmt->fetchAll(PDO::FETCH_NUM);
     //print "Walk  parent=$in_parent_id, term_index=$in_term_index, term=$current_term<br>";
     //print '<pre>';
     //var_dump($possible_branches);
     //print '</pre>';
     foreach ($possible_branches as $possibility) {
         /* decode the possibility and prepare to match */
         list($br_parent, $br_expr, $br_terminal) = $possibility;
         // in future, for speed, this information could be assessed at pattern registration
         // and stored & accessed, possibly using the sort key integer ?
         $is_wildcard = JxBotEngine::is_wildcard($br_expr);
         $set_ref = JxBotEngine::is_set_ref($br_expr);
         $bot_ref = JxBotEngine::is_bot_ref($br_expr);
         //print $br_expr;
         //print "Considering possible branch=$br_parent, expr=$br_expr, term=$br_terminal, wild=$is_wildcard  :<br>";
         // pattern side sets & bot tags will have to be handled similarly, since they may have multi-word values
         // basically, like wildcards, except all words must match
         /* branch to appropriate match handler depending on type of branch */
         if ($bot_ref !== false || $set_ref !== false) {
             /* match:  bot predicate or set reference: */
             if ($bot_ref !== false) {
                 $values = array(JxBotNL::normalise(JxBotConfig::bot($bot_ref)));
                 $match = $this->try_match_values($br_parent, $values, false, $in_term_index, $br_terminal);
             } else {
                 $values = JxBotNLData::set_values($set_ref);
                 $match = $this->try_match_values($br_parent, $values, true, $in_term_index, $br_terminal);
             }
         } else {
             if (!$is_wildcard) {
                 /* match:  normal word or pattern clause separator: */
                 $match = $this->try_match_word($br_parent, $br_expr, $in_term_index, $br_terminal);
             } else {
                 /* match:  wildcard */
                 $match = $this->try_match_wildcard($br_parent, $br_expr, $in_term_index, $br_terminal);
             }
         }
         /* if matching was successful, return the matching pattern,
         			otherwise, continue looking at sub-branches at this level */
         if ($match !== false) {
             return $match;
         }
     }
     /* no possible subbranches; no match this branch */
     $this->search_depth--;
     return false;
 }