private function checkMemoryLimit() { $memLimit = ini_get('memory_limit'); $minimumRequired = 128; $bytes = Utils::convertSize(ini_get('memory_limit')); if ($bytes < $minimumRequired * pow(1024, 2)) { $this->output->writeln("<comment>Memory limit {$memLimit} might be too low. Trying to set {$minimumRequired}M.</comment>"); ini_set('memory_limit', $minimumRequired . 'M'); } }
/** * Process single file. * * @param $file Source file * @param $parseExamples Whether or not include also examples * @return ParserResult Parse result including all warnings and skipped files */ public function processFile($file, $parseExamples, $result = null) { if (!$result) { $result = new ParserResult(); } $dom = new \DOMDocument(); @$dom->loadHTML(file_get_contents($file)); // Most important object used to traverse HTML DOM structure. $xpath = new \DOMXPath($dom); $function = []; // Parse function name. $h1 = $xpath->query('//h1[@class="refname"]'); // Check if it managed to find function name. if ($h1->length == 0) { $result->addSkipped(basename($file)); return $result; } // Function short description. $description = $xpath->query('//span[@class="dc-title"]'); if ($description->length > 0) { // some functions don't have any description $function['desc'] = trim(Utils::simplifyString($description->item(0)->textContent), '.') . '.'; } else { $function['desc'] = null; } // Function long description. $longDescParagraphs = $xpath->query('//div[contains(@class,"description")]//p[contains(@class,"para") or contains(@class,"simpara")]'); $function['long_desc'] = Utils::extractFormattedText($longDescParagraphs); if ($function['long_desc']) { $function['long_desc'] = trim($function['long_desc'], '.') . '.'; } // PHP version since this function is available. $version = $xpath->query('//p[@class="verinfo"]'); if ($version->length > 0) { // check from which PHP version is it available $function['ver'] = trim($version->item(0)->textContent, '()'); } else { $function['ver'] = null; } // Return value description. $items = $xpath->query('//div[contains(@class,"returnvalues")]/p'); if ($items->length > 0) { $function['ret_desc'] = Utils::simplifyString($items->item(0)->textContent); } // "See also" methods $seeAlso = $xpath->query('//div[contains(@class,"seealso")]'); if ($seeAlso->length > 0) { $function['seealso'] = []; //$seeAlsoArray = array(); $lis = $xpath->query('.//li', $seeAlso->item(0)); foreach ($lis as $li) { // store just the name and description without parenthesis $text = explode('-', $li->textContent); $name = rtrim(trim($text[0]), '()'); if (strpos($name, ' ') === false) { $function['seealso'][] = $name; } } } // filename (url) $function['filename'] = substr(basename($file), 0, -5); // All function parameters in an array of arrays because some function have mutliple definitions. // Note: One function can have multiple parameter count (see http://www.php.net/manual/en/function.strtr.php) $function['params'] = []; $funcDescription = $xpath->query('//div[@class="refsect1 description"]/div[@class="methodsynopsis dc-description"]'); foreach ($funcDescription as $index => $description) { // Function name for this parameter list. $altName = str_replace('->', '::', $xpath->query('./span[@class="methodname"]', $description)->item(0)->textContent); $parsedParams = ['list' => [], 'name' => $altName]; // Return value type (boolean, integer, mixed, ... ). $span = $xpath->query('./span[@class="type"]', $description); if ($span->length > 0 && !isset($function['return']['type'])) { $parsedParams['ret_type'] = $span->item(0)->textContent; } // Parameter containers. $params = $xpath->query('span[@class="methodparam"]', $description); // skip empty parameter list (function declaration that doesn't take any parameter) if ($params->item(0)->textContent != 'void') { $optional = substr_count($description->textContent, '['); $allParameters = $xpath->query('//div[contains(@class,"parameters")]/dl/dt/code[@class="parameter"]'); for ($i = 0; $i < $params->length; $i++) { $paramNodes = $xpath->query('*', $params->item($i)); $descPattern = './*[self::p or self::ul or self::blockquote or self::table or self::div[@class="methodsynopsis dc-description"]]'; $paramDescriptions = null; $varName = $paramNodes->item(1)->textContent; foreach ($allParameters as $index => $paramDesc) { if (ltrim($varName, '$&') == $paramDesc->textContent) { $paramDescriptions = $xpath->query('//div[contains(@class,"parameters")]/dl/dd[' . ($index + 1) . ']'); break; } } // Single parameter. $param = array('type' => $paramNodes->item(0) ? $paramNodes->item(0)->textContent : 'unknown', 'var' => $varName, 'beh' => $params->length - $optional > $i ? 'required' : 'optional', 'desc' => $paramDescriptions ? Utils::extractFormattedText($xpath->query($descPattern, $paramDescriptions->item(0)), $xpath) : null); // Default value for this parameter if ($paramNodes->length >= 3) { $param['default'] = trim($paramNodes->item(2)->textContent, ' ='); } $parsedParams['list'][] = $param; } } $function['params'][] = $parsedParams; } // If parser didn't find any parameters, no short or long description then it's probably not a function in this file if (!$function['params'] || !isset($function['desc']) && !isset($function['long_desc'])) { $result->addSkipped(basename($file)); return $result; } $funcName = strtolower($function['params'][0]['name']); foreach ($function['params'] as $index => $param) { // Use all alternative names just as a reference to the first (primary) name. $name = strtolower($function['params'][$index]['name']); $result->setResult($name, $index == 0 ? $function : $funcName); } // Parse all examples in this file. if ($parseExamples != self::SKIP_EXAMPLES) { // Find all source code containers. $exampleDiv = $xpath->query('//div[@class="example" or @class="informalexample"]'); for ($i = 0; $i < $exampleDiv->length; $i++) { $output = null; // Get source code as pure text, without any syntax highlighting tags. $sourceCode = $dom->saveXML($xpath->query('.//div[@class="phpcode"]', $exampleDiv->item($i))->item(0)); $outputDiv = $xpath->query('.//div[@class="cdata"]', $exampleDiv->item($i)); if ($outputDiv->length > 0) { $output = $xpath->query('.//div[@class="cdata"]', $exampleDiv->item($i))->item(0)->textContent; } // Example title, strip beginning and ending php tags. $ps = $xpath->query('p', $exampleDiv->item($i)); if ($ps->length > 0) { $title = $ps->item(0)->textContent; // Remove some unnecessary stuff. $title = trim(preg_replace('/^(Example #\\d+|Beispiel #\\d+|Exemplo #\\d+|Exemple #\\d+|Przykład #\\d+)/', '', $title)); $title = trim(preg_replace('/\\s+/', ' ', $title)); } else { $title = null; } $example = ['title' => $title, 'source' => Utils::clearSourceCode($sourceCode), 'output' => trim($output) ?: null]; // Skip examples with malformed UTF-8 characters. // @todo: check where's the problem json_encode($example, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE); if (json_last_error()) { $result->addWarning($funcName, 'Example "' . $title . '": ' . json_last_error_msg()); } else { $result->addExample($funcName, $example); } } } return $result; }