Your project uses non-strict array lookups 5
- Read doc
- Reliability
- Major
More information: https://insight.symfony.com/what-we-analyse/php.strict_array_lookup
- // Use higher priority
- $priorities = ['low' => 1, 'medium' => 2, 'high' => 3, 'critical' => 4];
- $priority1 = $priorities[$obj1->getPriority()];
- $priority2 = $priorities[$obj2->getPriority()];
- $combinedPriority = array_search(max($priority1, $priority2), $priorities);
- // Estimate combined time (not just sum, as there may be synergies)
- $estimatedTime = (int)(($obj1->getEstimatedCompletionTime() + $obj2->getEstimatedCompletionTime()) * 0.8);
- return [
- unset($this->storage[$scopedKey]);
- unset($this->metadata[$scopedKey]);
- // Remove from the scope's key list
- $scopeKeys = $this->scopes[$this->currentScope] ?? [];
- $index = array_search($scopedKey, $scopeKeys);
- if ($index !== false) {
- array_splice($this->scopes[$this->currentScope], $index, 1);
- }
- <?php
- foreach ($modules as $module) {
- $secCheck = $GLOBALS[$module['class']];
- if (!\in_array($secCheck->type, $types)) {
- $secCheck->type = 'info';
- }
- $output = '';
- }
- }
- if (!empty($this->_upload)) {
- if (!empty($this->_extensions)) {
- if (!in_array(mb_strtolower(substr($this->_upload['name'], strrpos($this->_upload['name'], '.') + 1)), $this->_extensions)) {
- $CLICSHOPPING_MessageStack->add(CLICSHOPPING::getDef('error_filetype_not_allowed') . implode(', ', $this->_extensions), 'warning');
- return false;
- }
- }
- while ($file = $dir->read()) {
- if (!is_dir($backup_directory . $file)) {
- $ext = substr($file, strrpos($file, '.') + 1);
- if (in_array($ext, array('zip', 'sql', 'gz')) && !isset($contents[$ext])) {
- $contents[$ext] = $file;
- if ($ext != 'sql') { // zip and gz (binaries) are prioritized over sql (plain text)
- break;
- }
Your project should not use insecure random number functions 2
- Read doc
- Security
- Major
More information: https://insight.symfony.com/what-we-analyse/php.use_insecure_random_function
- // Boucle infinie pour l'envoi des événements
- while (true) {
- try {
- // Simulate random failure before computing data
- if ($sim_fail_rate > 0 && mt_rand(0, mt_getrandmax()) / mt_getrandmax() < $sim_fail_rate) {
- throw new \Exception('Simulated failure');
- }
- // Récupérer les données de performance
- $data = $monitor->getPerformanceData($range);
- $data['metrics']['error_frequency'] = max(0.0, (float)$sim_error_pct);
- }
- }
- // Optionally force a random connection drop to test auto-reconnect
- if ($sim_drop && (mt_rand(1, 100) <= 5)) { // ~5% chance per tick
- // Flush a final event then terminate the connection
- echo "event: error\n";
- echo "data: {\"error\":\"Simulated connection drop\"}\n\n";
- ob_flush();
- flush();
Your project should not contain PHP files defining multiple classes
- Read doc
- Productivity
- Info
More information: https://insight.symfony.com/what-we-analyse/php.file_contains_more_than_one_class
Your project should use dedicated PHP string functions 50
- Read doc
- Productivity
- Info
More information: https://insight.symfony.com/what-we-analyse/php.use_string_function
- private function determineSuggestedActionForSqlError(string $errorMessage, string $temporalPeriod): string
- {
- $errorLower = strtolower($errorMessage);
- // Check for common error patterns
- if (strpos($errorLower, 'column') !== false || strpos($errorLower, 'field') !== false) {
- return "The database may not have the required columns for {$temporalPeriod} aggregation. Try a different time period or check your data schema.";
- }
- if (strpos($errorLower, 'table') !== false) {
- return "The required table for {$temporalPeriod} aggregation may not exist. Verify your database structure.";
- if (strpos($errorLower, 'syntax') !== false) {
- return "There was a SQL syntax error. Try rephrasing your query or using a simpler time period.";
- }
- if (strpos($errorLower, 'permission') !== false || strpos($errorLower, 'access') !== false) {
- return "You may not have permission to access the required data. Contact your administrator.";
- }
- if (strpos($errorLower, 'timeout') !== false) {
- return "The query took too long. Try a shorter time range or simpler aggregation.";
- if (strpos($errorLower, 'permission') !== false || strpos($errorLower, 'access') !== false) {
- return "You may not have permission to access the required data. Contact your administrator.";
- }
- if (strpos($errorLower, 'timeout') !== false) {
- return "The query took too long. Try a shorter time range or simpler aggregation.";
- }
- // Default suggestion
- return "Try rephrasing your query or using a different temporal period. If the problem persists, contact support.";
- if (strpos($errorLower, 'table') !== false) {
- return "The required table for {$temporalPeriod} aggregation may not exist. Verify your database structure.";
- }
- if (strpos($errorLower, 'syntax') !== false) {
- return "There was a SQL syntax error. Try rephrasing your query or using a simpler time period.";
- }
- if (strpos($errorLower, 'permission') !== false || strpos($errorLower, 'access') !== false) {
- return "You may not have permission to access the required data. Contact your administrator.";
- // Check for common error patterns
- if (strpos($errorLower, 'column') !== false || strpos($errorLower, 'field') !== false) {
- return "The database may not have the required columns for {$temporalPeriod} aggregation. Try a different time period or check your data schema.";
- }
- if (strpos($errorLower, 'table') !== false) {
- return "The required table for {$temporalPeriod} aggregation may not exist. Verify your database structure.";
- }
- if (strpos($errorLower, 'syntax') !== false) {
- return "There was a SQL syntax error. Try rephrasing your query or using a simpler time period.";
- // Propose permission level always requires approval for write actions
- if ($permissionLevel === self::PERMISSION_PROPOSE) {
- $writeActions = ['create', 'update', 'delete', 'modify', 'propose'];
- foreach ($writeActions as $writeAction) {
- if (strpos($action, $writeAction) !== false) {
- return true;
- }
- }
- }
- 'database', 'table', 'index', 'query', 'cache', 'memory',
- 'file', 'log', 'configuration', 'setting', 'registry'
- ];
- foreach ($resourceKeywords as $keyword) {
- if (strpos($text, $keyword) !== false) {
- $resources[] = $keyword;
- }
- }
- return array_unique($resources);
- 'optimize' => ['optimize', 'improve', 'enhance', 'tune']
- ];
- foreach ($operationPatterns as $operation => $keywords) {
- foreach ($keywords as $keyword) {
- if (strpos($text, $keyword) !== false) {
- $operations[] = $operation;
- break;
- }
- }
- }
- // Check if goals contain contradictory terms
- foreach ($contradictions as $pair) {
- $has1in1 = strpos($goal1, $pair[0]) !== false;
- $has2in1 = strpos($goal1, $pair[1]) !== false;
- $has1in2 = strpos($goal2, $pair[0]) !== false;
- $has2in2 = strpos($goal2, $pair[1]) !== false;
- // If obj1 has first term and obj2 has second term (or vice versa)
- if (($has1in1 && $has2in2) || ($has2in1 && $has1in2)) {
- // Check if they're operating on similar subjects
- $similarity = $this->calculateSimilarity($obj1, $obj2);
- ];
- // Check if goals contain contradictory terms
- foreach ($contradictions as $pair) {
- $has1in1 = strpos($goal1, $pair[0]) !== false;
- $has2in1 = strpos($goal1, $pair[1]) !== false;
- $has1in2 = strpos($goal2, $pair[0]) !== false;
- $has2in2 = strpos($goal2, $pair[1]) !== false;
- // If obj1 has first term and obj2 has second term (or vice versa)
- if (($has1in1 && $has2in2) || ($has2in1 && $has1in2)) {
- ['grow', 'shrink']
- ];
- // Check if goals contain contradictory terms
- foreach ($contradictions as $pair) {
- $has1in1 = strpos($goal1, $pair[0]) !== false;
- $has2in1 = strpos($goal1, $pair[1]) !== false;
- $has1in2 = strpos($goal2, $pair[0]) !== false;
- $has2in2 = strpos($goal2, $pair[1]) !== false;
- // If obj1 has first term and obj2 has second term (or vice versa)
- // Check if goals contain contradictory terms
- foreach ($contradictions as $pair) {
- $has1in1 = strpos($goal1, $pair[0]) !== false;
- $has2in1 = strpos($goal1, $pair[1]) !== false;
- $has1in2 = strpos($goal2, $pair[0]) !== false;
- $has2in2 = strpos($goal2, $pair[1]) !== false;
- // If obj1 has first term and obj2 has second term (or vice versa)
- if (($has1in1 && $has2in2) || ($has2in1 && $has1in2)) {
- // Check if they're operating on similar subjects
- private function generateUserFriendlyErrorMessage(array $errors): string
- {
- // Check for common error patterns and generate appropriate messages
- foreach ($errors as $error) {
- // Pattern: "Semantic result missing sources and data"
- if (strpos($error, 'Semantic result missing sources and data') !== false) {
- return "I couldn't find any information about that in the database. The requested content (like terms and conditions) may not be available yet. Please try asking about something else or contact support to add this content.";
- }
- // Pattern: "Analytics result missing data"
- if (strpos($error, 'Analytics result missing data') !== false) {
- if (strpos($error, 'Semantic result missing sources and data') !== false) {
- return "I couldn't find any information about that in the database. The requested content (like terms and conditions) may not be available yet. Please try asking about something else or contact support to add this content.";
- }
- // Pattern: "Analytics result missing data"
- if (strpos($error, 'Analytics result missing data') !== false) {
- return "I couldn't retrieve the requested data. This might be because there are no records matching your query, or the data hasn't been entered yet. Please try a different query or check if the data exists.";
- }
- // Pattern: "Hybrid result missing"
- if (strpos($error, 'Hybrid result missing') !== false) {
- // Semantic results MUST have sources OR data, unless it's a valid LLM/memory fallback
- if (!$hasSources) {
- $sourceType = strtolower($finalResult['source_attribution']['source_type'] ?? '');
- $isLLMFallback = $hasTextResponse && (
- strpos($sourceType, 'llm') !== false ||
- strpos($sourceType, 'general knowledge') !== false ||
- strpos($sourceType, 'conversation') !== false ||
- strpos($sourceType, 'memory') !== false
- );
- if (!$isLLMFallback) {
- // Semantic results MUST have sources OR data, unless it's a valid LLM/memory fallback
- if (!$hasSources) {
- $sourceType = strtolower($finalResult['source_attribution']['source_type'] ?? '');
- $isLLMFallback = $hasTextResponse && (
- strpos($sourceType, 'llm') !== false ||
- strpos($sourceType, 'general knowledge') !== false ||
- strpos($sourceType, 'conversation') !== false ||
- strpos($sourceType, 'memory') !== false
- );
- $sourceType = strtolower($finalResult['source_attribution']['source_type'] ?? '');
- $isLLMFallback = $hasTextResponse && (
- strpos($sourceType, 'llm') !== false ||
- strpos($sourceType, 'general knowledge') !== false ||
- strpos($sourceType, 'conversation') !== false ||
- strpos($sourceType, 'memory') !== false
- );
- if (!$isLLMFallback) {
- $sourcesStatus = isset($finalResult['sources']) ? 'empty' : 'not set';
- $dataStatus = isset($finalResult['data']) ? 'empty' : 'not set';
- if (strpos($error, 'Hybrid result missing') !== false) {
- return "I couldn't complete your request because some of the required information is missing. Please try breaking your question into smaller parts or asking about something else.";
- }
- // Pattern: "Empty response"
- if (strpos($error, 'empty') !== false || strpos($error, 'Empty') !== false) {
- return "I couldn't find any results for your query. The information you're looking for might not be available in the system yet. Please try rephrasing your question or asking about something else.";
- }
- }
- // Default fallback message
- if (strpos($error, 'Analytics result missing data') !== false) {
- return "I couldn't retrieve the requested data. This might be because there are no records matching your query, or the data hasn't been entered yet. Please try a different query or check if the data exists.";
- }
- // Pattern: "Hybrid result missing"
- if (strpos($error, 'Hybrid result missing') !== false) {
- return "I couldn't complete your request because some of the required information is missing. Please try breaking your question into smaller parts or asking about something else.";
- }
- // Pattern: "Empty response"
- if (strpos($error, 'empty') !== false || strpos($error, 'Empty') !== false) {
- if (!$hasSources) {
- $sourceType = strtolower($finalResult['source_attribution']['source_type'] ?? '');
- $isLLMFallback = $hasTextResponse && (
- strpos($sourceType, 'llm') !== false ||
- strpos($sourceType, 'general knowledge') !== false ||
- strpos($sourceType, 'conversation') !== false ||
- strpos($sourceType, 'memory') !== false
- );
- if (!$isLLMFallback) {
- $sourcesStatus = isset($finalResult['sources']) ? 'empty' : 'not set';
- * @return bool
- */
- private function hasKeyword(string $text, array $keywords): bool
- {
- foreach ($keywords as $keyword) {
- if (strpos($text, $keyword) !== false) {
- return true;
- }
- }
- return false;
- }
- }
- $keywords[] = $entityType;
- $keywords[] = str_replace('_', ' ', $entityType);
- if (substr($entityType, -3) === 'ies') {
- $keywords[] = substr($entityType, 0, -3) . 'y';
- } elseif (substr($entityType, -1) === 's') {
- $keywords[] = substr($entityType, 0, -1);
- } else {
- $keywords[] = $entityType . 's';
- $keywords[] = $entityType;
- $keywords[] = str_replace('_', ' ', $entityType);
- if (substr($entityType, -3) === 'ies') {
- $keywords[] = substr($entityType, 0, -3) . 'y';
- } elseif (substr($entityType, -1) === 's') {
- $keywords[] = substr($entityType, 0, -1);
- } else {
- $keywords[] = $entityType . 's';
- }
- }
- return false;
- }
- private function hasComments(string $sql): bool
- {
- return strpos($sql, '--') !== false || strpos($sql, '/*') !== false;
- }
- private function hasConsistentData(array $results): bool
- {
- if (empty($results)) return true;
- private function evaluateSqlEfficiency(string $sql, array $tablesUsed, string $queryType): float
- {
- $score = 0.7; // Base score
- // Check for SELECT * (inefficient)
- if (strpos($sql, 'SELECT *') !== false) {
- $score -= 0.2;
- }
- // Check for proper indexing hints
- if ($this->hasProperIndexing($sql, $tablesUsed)) {
- $queryWords = explode(' ', strtolower($userQuery));
- $sqlLower = strtolower($sql);
- $matches = 0;
- foreach ($queryWords as $word) {
- if (strlen($word) > 3 && strpos($sqlLower, $word) !== false) {
- $matches++;
- }
- }
- return $matches > 0;
- // Fallback to simple keyword detection if detector fails
- $ambiguousTerms = ['best', 'good', 'recent', 'popular', 'top', 'better', 'worse'];
- $queryLower = strtolower($query);
- foreach ($ambiguousTerms as $term) {
- if (strpos($queryLower, $term) !== false) {
- return true;
- }
- }
- return false;
- }
- private function hasProperIndexing(string $sql, array $tables): bool
- {
- // Check for WHERE clauses that might benefit from indexes
- return strpos($sql, 'WHERE') !== false;
- }
- private function hasUnnecessaryJoins(string $sql): bool
- {
- // Count JOINs - more than 5 might be excessive
- 'HAVING',
- 'DISTINCT'
- ];
- foreach ($slowPatterns as $pattern) {
- if (strpos($sqlUpper, $pattern) !== false) {
- return true;
- }
- }
- // Check for multiple JOINs
- }
- private function isWellFormatted(string $sql): bool
- {
- // Check for basic formatting (line breaks, indentation)
- return strpos($sql, "\n") !== false || strpos($sql, "\r") !== false;
- }
- private function hasMeaningfulAliases(string $sql): bool
- {
- // Check for table aliases that are more than single letters
- return 'memory';
- }
- if (strpos($message, 'validation') !== false || strpos($message, 'invalid') !== false) {
- return 'validation';
- }
- if (strpos($message, 'classification') !== false || strpos($message, 'intent') !== false) {
- return 'classification';
- }
- if (strpos($message, 'api') !== false || strpos($message, 'gpt') !== false) {
- return 'api';
- }
- $message = strtolower($errorMessage);
- if (strpos($message, 'database') !== false || strpos($message, 'sql') !== false) {
- return 'database';
- }
- if (strpos($message, 'timeout') !== false || strpos($message, 'time out') !== false) {
- return 'timeout';
- }
- if (strpos($message, 'memory') !== false) {
- return 'memory';
- }
- return 'database';
- }
- if (strpos($message, 'timeout') !== false || strpos($message, 'time out') !== false) {
- return 'timeout';
- }
- if (strpos($message, 'memory') !== false) {
- return 'memory';
- }
- if (strpos($message, 'validation') !== false || strpos($message, 'invalid') !== false) {
- return 'validation';
- }
- return 'validation';
- }
- if (strpos($message, 'classification') !== false || strpos($message, 'intent') !== false) {
- return 'classification';
- }
- if (strpos($message, 'api') !== false || strpos($message, 'gpt') !== false) {
- return 'api';
- }
- return 'unknown';
- }
- return 'timeout';
- }
- if (strpos($message, 'memory') !== false) {
- return 'memory';
- }
- if (strpos($message, 'validation') !== false || strpos($message, 'invalid') !== false) {
- return 'validation';
- }
- if (strpos($message, 'classification') !== false || strpos($message, 'intent') !== false) {
- return 'classification';
- }
- */
- private function categorizeError(string $errorMessage): string
- {
- $message = strtolower($errorMessage);
- if (strpos($message, 'database') !== false || strpos($message, 'sql') !== false) {
- return 'database';
- }
- if (strpos($message, 'timeout') !== false || strpos($message, 'time out') !== false) {
- return 'timeout';
- }
- $text = strtolower("$reason $comment");
- foreach ($keywords as $type => $words) {
- foreach ($words as $word) {
- if (strpos($text, $word) !== false) {
- return $type;
- }
- }
- }
- }
- }
- // Priority 4: Check if rawResult has 'text_response' field (but not if it's the JSON fallback)
- if (isset($rawResult['text_response']) && !empty($rawResult['text_response'])) {
- if (strpos($rawResult['text_response'], 'Résultat:') === false) {
- return $rawResult['text_response'];
- }
- }
- // Priority 5: Use finalResponse if available (but not if it's the JSON fallback)
- // If it's an array, search for the response
- if (is_array($executionResult)) {
- if (isset($executionResult['text_response']) && !empty($executionResult['text_response'])) {
- // Check if text_response is NOT the JSON fallback
- if (strpos($executionResult['text_response'], 'Résultat:') === false) {
- return $executionResult['text_response'];
- }
- }
- if (isset($executionResult['response']) && !empty($executionResult['response'])) {
- return $rawResult['text_response'];
- }
- }
- // Priority 5: Use finalResponse if available (but not if it's the JSON fallback)
- if (!empty($finalResponse) && strpos($finalResponse, 'Résultat:') === false) {
- return $finalResponse;
- }
- return '';
- }
- // Fallback to source_table if no name found
- if ($docName === null && isset($metadata['source_table'])) {
- $tableName = $metadata['source_table'];
- // Remove prefix and _embedding suffix
- $prefix = defined('CLICSHOPPING_DB_TABLE_PREFIX') ? CLICSHOPPING_DB_TABLE_PREFIX : 'clic_';
- if (strpos($tableName, $prefix) === 0) {
- $tableName = substr($tableName, strlen($prefix));
- }
- $tableName = str_replace('_embedding', '', $tableName);
- $tableName = str_replace('_', ' ', $tableName);
- $docName = ucwords($tableName);
- return $entityType;
- }
- // Remove table prefix if present
- $prefix = CLICSHOPPING::getConfig('db_table_prefix');
- if (!empty($prefix) && strpos($tableName, $prefix) === 0) {
- $tableName = substr($tableName, strlen($prefix));
- }
- // Remove '_embedding' suffix if present
- $tableName = str_replace('_embedding', '', $tableName);
- // 🔍 DEBUG: Log prompt loading verification
- if ($this->debug) {
- error_log("[INFO : ANALYSE] [UnifiedQueryAnalyzer] Prompt Loading Verification:");
- error_log(" Prompt length: " . strlen($prompt) . " characters");
- error_log(" Prompt preview (first 200 chars): " . substr($prompt, 0, 200) . "...");
- error_log(" Query in prompt: " . (strpos($prompt, $query) !== false ? 'YES' : 'NO'));
- }
- // Single GPT call for everything
- // Use Gpt::getGptResponse() instead of non-existent complete() method
- if ($this->debug) {
- error_log("UnifiedQueryAnalyzer: Built prompt from language file sections");
- error_log("UnifiedQueryAnalyzer: Query to analyze: {$query}");
- error_log("UnifiedQueryAnalyzer: Total prompt length: " . strlen($prompt) . " characters");
- error_log("UnifiedQueryAnalyzer: Prompt contains query: " . (strpos($prompt, $query) !== false ? 'YES' : 'NO'));
- }
- return $prompt;
- }
- 'as an ai',
- 'i\'m an ai'
- ];
- foreach ($genericPhrases as $phrase) {
- if (strpos($response, $phrase) !== false) {
- $validationErrors[] = "Response contains generic LLM phrase indicating lack of knowledge";
- return false;
- }
- }
- // Check if source type is valid (case-insensitive, partial match)
- $sourceType = strtolower($sourceAttr['source_type']);
- $isValidSource = false;
- foreach ($validSourceTypes as $validType) {
- if (strpos($sourceType, strtolower($validType)) !== false) {
- $isValidSource = true;
- break;
- }
- }
- * @return string Escaped value
- */
- private function escapeCsvValue(string $value): string
- {
- // Escape quotes and wrap in quotes if contains comma, quote, or newline
- if (strpos($value, ',') !== false || strpos($value, '"') !== false || strpos($value, "\n") !== false) {
- return '"' . str_replace('"', '""', $value) . '"';
- }
- return $value;
- }
- // Simple contradiction check
- $s1Lower = strtolower($statement1);
- $s2Lower = strtolower($statement2);
- // Check for negation patterns
- if (strpos($s1Lower, 'not') !== false && strpos($s2Lower, 'not') === false) {
- return true;
- }
- return false;
- }
- // Simple keyword matching (replaces deleted PatternAnalysisPattern)
- $patternKeywords = ['pattern', 'trend', 'style', 'dominant', 'recurring', 'common'];
- $queryLower = strtolower($queryToAnalyze);
- foreach ($patternKeywords as $keyword) {
- if (strpos($queryLower, $keyword) !== false) {
- if ($this->debug) {
- $this->logDebug("Pattern analysis keyword detected: $keyword");
- }
- return true;
- }
- $table = $matches[1];
- }
- // Ajouter le préfixe si nécessaire
- $prefix = CLICSHOPPING::getConfig('db_prefix');
- if (!empty($prefix) && strpos($table, $prefix) !== 0) {
- $table = $prefix . $table;
- }
- return trim($table, '`"\'');
- }
Your project uses legacy callable syntax instead of first-class callable syntax
- Read doc
- Productivity
- Info
More information: https://insight.symfony.com/what-we-analyse/php.use_first_class_callable_syntax
- // Build a mapping of table names (without prefix) to entity types
- $tableToEntityType = [];
- foreach ($allTables as $fullTableName) {
- $entityType = $registry->getEntityTypeForTable($fullTableName);
- // Remove prefix and _embedding suffix to get base table name
- $tableName = str_replace([$prefix, '_embedding'], '', $fullTableName);
- $tableToEntityType[$tableName] = $entityType;
- }
- // Pattern 1: FROM {table_name}
- if (preg_match('/FROM\s+(?:\w+\.)?(\w+)/i', $query, $matches)) {
gyakutsuki
gyakutsuki