665 lines
24 KiB
PHP
665 lines
24 KiB
PHP
<?php
|
|
/**
|
|
* This file contains the external class BIBTEXPARSE of WIKINDX
|
|
* @see https://wikindx.sourceforge.io/ The WIKINDX SourceForge project
|
|
*
|
|
* @author The WIKINDX Team
|
|
* @license https://www.isc.org/licenses/ ISC License
|
|
*/
|
|
|
|
/*
|
|
|
|
(Amendments to file reading Daniel Pozzi for v1.1)
|
|
|
|
21/08/2004 v1.4 Guillaume Gardey
|
|
- Added PHP string parsing and expand macro features.
|
|
- Fix bug with comments, strings macro.
|
|
- expandMacro = FALSE/TRUE to expand string macros.
|
|
- loadStringMacro($bibtex_string) to load a string. (array of lines)
|
|
|
|
22/08/2004 v1.4 Mark Grimshaw
|
|
- a few adjustments to Guillaume's code.
|
|
|
|
28/04/2005 v1.5 Mark Grimshaw
|
|
- a little debugging for @preamble
|
|
|
|
02/05/2005 G. Gardey
|
|
- Add support for @string macro defined by curly brackets: @string{M12 = {December}}
|
|
- Don't expand macro for bibtexCitation and bibtexEntryType
|
|
- Better support for fields like journal = {Journal of } # JRNL23
|
|
|
|
03/05/2005 G. Gardey
|
|
- Fix wrong field value parsing when an entry ends by someField = {value}}
|
|
|
|
11/06/2005 v1.53 Mark Grimshaw-Aagaard:
|
|
- Stopped expansion of @string when entry is enclosed in {...} or "..."
|
|
|
|
v2 ****************************************** v2
|
|
|
|
30/01/2006 v2.0 Esteban Zimanyi
|
|
- Add support for @string defined by other strings as in @string( AA = BB # " xx " # C }
|
|
- Add support for comments as defined in Bibtex, i.e., ignores everything that is outside
|
|
entries delimited by '@' and the closing delimiter. In particular, comments in Bibtex do not
|
|
necessarily have a % at the begining of the line !
|
|
This required a complete rewrite of many functions as well as writing new ones !
|
|
|
|
31/01/2006 Mark Grimshaw-Aagaard
|
|
- Ensured that @comment{...} is ignored in parseEntry().
|
|
- Modified extractEntries() to ensure that entries where the start brace/parenthesis is on a
|
|
new line are properly parsed.
|
|
|
|
10/02/2006 Mark Grimshaw-Aagaard
|
|
- A 4th array, $this->undefinedStrings, is now returned that holds field values that are judged to be undefined strings.
|
|
i.e. they are a non-numeric value that is not defined in a @string{...} entry and not enclosed by braces or double-quotes.
|
|
This array will be empty unless the following condition is met:
|
|
($this->removeDelimit || $this->expandMacro && $this->fieldExtract)
|
|
|
|
24/04/2006 Esteban Zimanyi
|
|
- When an undefined string is found in function removeDelimiters return the empty string
|
|
- Return $this->undefinedStrings in the last position to allow compatibility with previous versions
|
|
- Fix management of preamble in function returnArrays
|
|
|
|
28/12/2015 Michael Winkler
|
|
- Outdated PHP methods replaced
|
|
- Fix for saving line breaks in abstracts and other fields
|
|
|
|
07/04/2019 Michael Winkler
|
|
- Fix array offset errors in get_line()
|
|
|
|
15/02/2021 Michael Winkler
|
|
- Include updates from WIKINDX 6.4.1
|
|
|
|
*/
|
|
|
|
/**
|
|
* Main parsing class of BibTeXParse
|
|
*
|
|
* Inspired by an awk BibTeX parser written by Nelson H. F. Beebe over
|
|
* 20 years ago although little of that remains.
|
|
*
|
|
* @version 2.1.1
|
|
*/
|
|
class BIBTEXPARSE {
|
|
|
|
public array $preamble;
|
|
public array $strings;
|
|
public array $undefinedStrings;
|
|
public array $entries;
|
|
public int $count;
|
|
public bool $fieldExtract;
|
|
public bool $removeDelimit;
|
|
public bool $expandMacro;
|
|
public bool $parseFile;
|
|
public bool $outsideEntry;
|
|
public array $userStrings= [];
|
|
public array $bibtexString = [];
|
|
public int $currentLine;
|
|
|
|
public function __construct() {
|
|
$this->preamble = [];
|
|
$this->strings = [];
|
|
$this->undefinedStrings = [];
|
|
$this->entries = [];
|
|
|
|
$this->count = 0;
|
|
$this->fieldExtract = TRUE;
|
|
$this->removeDelimit = TRUE;
|
|
$this->expandMacro = FALSE;
|
|
$this->parseFile = TRUE;
|
|
$this->outsideEntry = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Open bib file
|
|
*
|
|
* @param string $file
|
|
*/
|
|
public function openBib($file) {
|
|
if(!is_file($file)) {
|
|
debug_print_backtrace();
|
|
die;
|
|
}
|
|
$this->fid = fopen($file, 'r');
|
|
$this->parseFile = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Load a bibtex string to parse it
|
|
*
|
|
* @param string $bibtex_string
|
|
*/
|
|
public function loadBibtexString($bibtex_string) {
|
|
if (is_string($bibtex_string)) {
|
|
$line_array = explode("\n",$bibtex_string);
|
|
$this->bibtexString = self::_setLineBreak($line_array);
|
|
}
|
|
else {
|
|
$this->bibtexString = $bibtex_string;
|
|
}
|
|
$this->parseFile = FALSE;
|
|
$this->currentLine = 0;
|
|
}
|
|
|
|
/**
|
|
* Set strings macro
|
|
*
|
|
* @param array $macro_array
|
|
*/
|
|
public function loadStringMacro($macro_array) {
|
|
$this->userStrings = $macro_array;
|
|
}
|
|
|
|
/**
|
|
* Close bib file
|
|
*/
|
|
public function closeBib() {
|
|
fclose($this->fid);
|
|
}
|
|
|
|
/**
|
|
* Get a non-empty line from the bib file or from the bibtexString
|
|
*
|
|
* @return false|string
|
|
*/
|
|
public function getLine() {
|
|
if ($this->parseFile) {
|
|
if (!feof($this->fid)) {
|
|
do {
|
|
$line = trim(fgets($this->fid));
|
|
}
|
|
while (!feof($this->fid) && !$line);
|
|
return $line;
|
|
}
|
|
return FALSE;
|
|
}
|
|
else {
|
|
while($this->currentLine < count($this->bibtexString) && !isset($line)) {
|
|
$line = trim($this->bibtexString[$this->currentLine]);
|
|
$this->currentLine++;
|
|
// echo $this->currentLine . ' ' . $line . '<br/>';
|
|
return $line;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract value part of @string field enclosed by double-quotes or braces.
|
|
*
|
|
* The string may be expanded with previously-defined strings
|
|
*
|
|
* @param string $string
|
|
* @return string
|
|
*/
|
|
public function extractStringValue($string) {
|
|
// $string contains a end delimiter, remove it
|
|
$string = trim( mb_substr( $string, 0, mb_strlen($string) - 1 ) );
|
|
// remove delimiters and expand
|
|
$string = $this->removeDelimitersAndExpand($string);
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Extract a field
|
|
*
|
|
* @param string $seg
|
|
* @return array
|
|
*/
|
|
public function fieldSplit($seg) {
|
|
// handle fields like another-field = {}
|
|
$array = preg_split("/,\\s*([-_.:,a-zA-Z0-9]+)\\s*={1}\\s*/Uu", $seg, PREG_SPLIT_DELIM_CAPTURE);
|
|
if (!array_key_exists(1, $array)) {
|
|
return array($array[0], FALSE);
|
|
}
|
|
return array($array[0], $array[1]);
|
|
}
|
|
|
|
/**
|
|
* Extract and format fields
|
|
*
|
|
* @param string $oldString
|
|
*/
|
|
public function reduceFields($oldString) {
|
|
// 03/05/2005 G. Gardey. Do not remove all occurrences, just one
|
|
// * correctly parse an entry ended by: somefield = {aValue}}
|
|
$lg = mb_strlen($oldString);
|
|
$lastChar = mb_substr($oldString, $lg - 1, 1);
|
|
|
|
if ($lastChar == '}' || $lastChar == ')' || $lastChar == ',') {
|
|
$oldString = mb_substr($oldString, 0, $lg - 1);
|
|
}
|
|
|
|
$split = preg_split('/=/u', $oldString, 2);
|
|
|
|
$string = $split[1];
|
|
while ($string) {
|
|
list($entry, $string) = $this->fieldSplit($string);
|
|
$values[] = $entry;
|
|
}
|
|
|
|
foreach ($values as $value) {
|
|
$pos = mb_strpos($oldString, $value);
|
|
$oldString = self::_mb_substr_replace($oldString, '', $pos, mb_strlen($value));
|
|
}
|
|
$rev = self::_mb_strrev(trim($oldString));
|
|
if ( mb_substr($rev, 0, 1) != ',' ) {
|
|
$oldString .= ',';
|
|
}
|
|
|
|
$keys = preg_split('/=,/u', $oldString);
|
|
// 22/08/2004 - Mark Grimshaw-Aagaard
|
|
// I have absolutely no idea why this array_pop is required but it is. Seems to always be
|
|
// an empty key at the end after the split which causes problems if not removed.
|
|
array_pop($keys);
|
|
foreach ($keys as $key) {
|
|
$value = trim(array_shift($values));
|
|
$rev = self::_mb_strrev($value);
|
|
// remove any dangling ',' left on final field of entry
|
|
if ( mb_substr($rev, 0, 1) == ',' ) {
|
|
$value = rtrim($value, ',');
|
|
}
|
|
if (!$value) {
|
|
continue;
|
|
}
|
|
|
|
// 28/12/2015 Michael Winkler
|
|
// Replace the control character for line breaks
|
|
$value = self::_replaceLineBreak($value);
|
|
|
|
// 21/08/2004 G.Gardey -> expand macro
|
|
// Don't remove delimiters now needs to know if the value is a string macro
|
|
// $this->entries[$this->count][strtolower(trim($key))] = trim($this->removeDelimiters(trim($value)));
|
|
$key = strtolower(trim($key));
|
|
$this->entries[$this->count][$key] = trim($value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start splitting a bibtex entry into component fields.
|
|
* Store the entry type and citation.
|
|
*
|
|
* @param string $entry
|
|
*/
|
|
public function fullSplit($entry) {
|
|
$matches = preg_split("/@(.*)[{(](.*),/Uu", $entry, 2, PREG_SPLIT_DELIM_CAPTURE);
|
|
$this->entries[$this->count]['bibtexEntryType'] = mb_strtolower(trim($matches[1]));
|
|
// sometimes a bibtex entry will have no citation key
|
|
if (preg_match("/=/u", $matches[2])) { // this is a field
|
|
$matches = preg_split("/@(.*)\\s*[{(](.*)/Uu", $entry, 2, PREG_SPLIT_DELIM_CAPTURE);
|
|
}
|
|
$this->entries[$this->count]['bibtexCitation'] = $matches[2];
|
|
$this->reduceFields($matches[3]);
|
|
}
|
|
|
|
/**
|
|
* Grab a complete bibtex entry
|
|
*
|
|
* @param string $entry
|
|
*/
|
|
public function parseEntry($entry) {
|
|
$lastLine = FALSE;
|
|
if (preg_match("/@(.*)([{(])/Uu", preg_quote($entry), $matches)) {
|
|
if (!array_key_exists(1, $matches)) {
|
|
return $lastLine;
|
|
}
|
|
if (preg_match("/string/ui", trim($matches[1]))) {
|
|
$this->strings[] = $entry;
|
|
}
|
|
elseif (preg_match("/preamble/ui", trim($matches[1]))) {
|
|
$this->preamble[] = $entry;
|
|
}
|
|
elseif (preg_match("/comment/ui", $matches[1])) {
|
|
// MG (31/Jan/2006) -- ignore @comment
|
|
}
|
|
else {
|
|
if ($this->fieldExtract) {
|
|
$this->fullSplit($entry);
|
|
}
|
|
else {
|
|
$this->entries[$this->count] = $entry;
|
|
}
|
|
$this->count++;
|
|
}
|
|
return $lastLine;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove delimiters from a string
|
|
*
|
|
* @param string $string
|
|
* @return string
|
|
*/
|
|
public function removeDelimiters($string) {
|
|
$StrLen = mb_strlen($string);
|
|
|
|
if ($StrLen > 0) {
|
|
$firstChar = mb_substr($string, 0, 1);
|
|
$lastChar = mb_substr($string, $StrLen - 1, 1);
|
|
|
|
if ($firstChar == '"' || ($firstChar == '{' && $lastChar == '}')) {
|
|
$string = mb_substr($string, 1);
|
|
$string = mb_substr($string, 0, -1);
|
|
}
|
|
/* Commented out to handle undelimited strings such as months. The bibtex entry is expected to be properly formatted.
|
|
else if (!is_numeric($string) && !array_key_exists($string, $this->strings))
|
|
{ // Undefined string that is not a year
|
|
echo "NO DELIMITERS: $string<P>";
|
|
if (array_search($string, $this->undefinedStrings) === FALSE)
|
|
// if not already in the undefinedStrings array
|
|
$this->undefinedStrings[] = $string;
|
|
return '';
|
|
}
|
|
*/
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* This function works like explode('#',$val) but has to take into account whether
|
|
* the character # is part of a string (i.e., is enclosed into "..." or {...} )
|
|
* or defines a string concatenation as in @string{ "x # x" # ss # {xx{x}x} }
|
|
*
|
|
* @param string $val
|
|
* @return string[]
|
|
*/
|
|
public function explodeString($val) {
|
|
$valLen = mb_strlen($val);
|
|
$openquote = $bracelevel = $i = $j = 0;
|
|
while ($i < $valLen) {
|
|
$s = mb_substr($val, $i, 1);
|
|
|
|
if ($s == '"') {
|
|
$openquote = !$openquote;
|
|
}
|
|
elseif ($s == '{') {
|
|
$bracelevel++;
|
|
}
|
|
elseif ($s == '}') {
|
|
$bracelevel--;
|
|
}
|
|
elseif ( $s == '#' && !$openquote && !$bracelevel ) {
|
|
$strings[] = mb_substr($val, $j, $i - $j);
|
|
$j = $i + 1;
|
|
}
|
|
$i++;
|
|
}
|
|
$strings[] = mb_substr($val, $j);
|
|
return $strings;
|
|
}
|
|
|
|
/**
|
|
* This function receives a string and a closing delimiter '}' or ')'
|
|
* and looks for the position of the closing delimiter taking into
|
|
* account the following Bibtex rules:
|
|
* Inside the braces, there can arbitrarily nested pairs of braces,
|
|
* but braces must also be balanced inside quotes!
|
|
* Inside quotes, to place the " character it is not sufficient
|
|
* to simply escape with \": Quotes must be placed inside braces.
|
|
*
|
|
* @param string $val
|
|
* @param string $delimitBegin
|
|
* @param string $delimitEnd
|
|
*
|
|
* @return int (0)
|
|
*/
|
|
public function closingDelimiter($val, $delimitBegin, $delimitEnd) {
|
|
$valLen = mb_strlen($val);
|
|
$DelimLen = mb_strlen($delimitEnd);
|
|
|
|
$openquote = 0;
|
|
$bracelevel = 0;
|
|
$i = 0;
|
|
while ($i < $valLen && ($valLen - $i >= $DelimLen)) {
|
|
$s = mb_substr($val, $i, $DelimLen);
|
|
// a '"' found at brace level 0 defines a value such as "ss{\"o}ss"
|
|
if ($s == '"' && !$bracelevel) {
|
|
$openquote = !$openquote;
|
|
}
|
|
elseif ($s == $delimitBegin) {
|
|
$bracelevel++;
|
|
}
|
|
elseif ($s == $delimitEnd) {
|
|
$bracelevel--;
|
|
}
|
|
if ($s == $delimitEnd && !$openquote && !$bracelevel) {
|
|
return $i;
|
|
}
|
|
|
|
$i += $DelimLen;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Remove enclosures around entry field values. Additionally, expand macros if flag set.
|
|
*
|
|
* @param string $string
|
|
* @param bool $inpreamble Default is FALSE
|
|
* @return string
|
|
*/
|
|
public function removeDelimitersAndExpand($string, $inpreamble = FALSE) {
|
|
// only expand the macro if flag set, if strings defined and not in preamble
|
|
if (!$this->expandMacro || empty($this->strings) || $inpreamble) {
|
|
$string = $this->removeDelimiters($string);
|
|
}
|
|
else {
|
|
$stringlist = $this->explodeString($string);
|
|
$string = "";
|
|
foreach ($stringlist as $str) {
|
|
// trim the string since usually # is enclosed by spaces
|
|
$str = trim($str);
|
|
// replace the string if macro is already defined
|
|
// strtolower is used since macros are case insensitive
|
|
if ( isset( $this->strings[mb_strtolower($str)] ) ) {
|
|
$string .= $this->strings[mb_strtolower($str)];
|
|
}
|
|
else {
|
|
$string .= $this->removeDelimiters( trim($str) );
|
|
}
|
|
}
|
|
}
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* This function extract entries taking into account how comments are defined in BibTeX.
|
|
* BibTeX splits the file in two areas: inside an entry and outside an entry, the delimitation
|
|
* being indicated by the presence of a @ sign. When this character is met, BibTex expects to
|
|
* find an entry. Before that sign, and after an entry, everything is considered a comment!
|
|
*/
|
|
public function extractEntries() {
|
|
$EntryDelim = '@';
|
|
$ListDelim = array('(' => ')', '{' => '}');
|
|
$inside = $possibleEntryStart = FALSE;
|
|
$entry = '';
|
|
while ($line = $this->getLine()) {
|
|
if ($possibleEntryStart) {
|
|
$line = $possibleEntryStart . $line;
|
|
}
|
|
if (!$inside && mb_strstr($line, $EntryDelim)) {
|
|
// throw all characters before the '@'
|
|
$line = mb_strstr($line, $EntryDelim);
|
|
|
|
$IsOpenDelimNotFinded = TRUE;
|
|
foreach ($ListDelim as $do => $dc) {
|
|
$IsOpenDelimNotFinded &= !mb_strstr($line, $do);
|
|
}
|
|
if ($IsOpenDelimNotFinded) {
|
|
$possibleEntryStart = $line;
|
|
}
|
|
elseif (preg_match("/$EntryDelim.*([" . preg_quote(implode('', array_keys($ListDelim))) . "])/Uu", preg_quote($line), $matches)) {
|
|
$inside = TRUE;
|
|
$delimitBegin = $matches[1];
|
|
$delimitEnd = $ListDelim[$matches[1]];
|
|
$possibleEntryStart = FALSE;
|
|
}
|
|
}
|
|
|
|
if ($inside) {
|
|
$entry .= ' ' . $line;
|
|
if ( $j = $this->closingDelimiter($entry, $delimitBegin, $delimitEnd) ) {
|
|
// all characters after the delimiter are thrown but the remaining
|
|
// characters must be kept since they may start the next entry !!!
|
|
$lastLine = mb_substr($entry, $j + 1);
|
|
$entry = mb_substr($entry, 0, $j + 1);
|
|
// Strip excess whitespaces from the entry
|
|
$entry = preg_replace('/\s\s+/u', ' ', $entry);
|
|
$this->parseEntry($entry);
|
|
$entry = mb_strstr($lastLine, $EntryDelim);
|
|
if ($entry) {
|
|
$inside = TRUE;
|
|
}
|
|
else {
|
|
$inside = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return arrays of entries etc. to the calling process.
|
|
*
|
|
* @return array (preamble, strings, entry, undefinedStrings)
|
|
*/
|
|
public function returnArrays() {
|
|
foreach ($this->preamble as $value) {
|
|
preg_match("/.*?[{(](.*)/u", $value, $matches);
|
|
$preamble = mb_substr($matches[1], 0, -1);
|
|
$preambles['bibtexPreamble'] = trim($this->removeDelimitersAndExpand(trim($preamble), TRUE));
|
|
}
|
|
if (isset($preambles)) {
|
|
$this->preamble = $preambles;
|
|
}
|
|
if ($this->fieldExtract) {
|
|
// Next lines must take into account strings defined by previously-defined strings
|
|
$strings = $this->strings;
|
|
// $this->strings is initialized with strings provided by user if they exists
|
|
// it is supposed that there are no substitutions to be made in the user strings, i.e., no #
|
|
$this->strings = isset($this->userStrings) ? $this->userStrings : array();
|
|
foreach ($strings as $value) {
|
|
// changed 21/08/2004 G. Gardey
|
|
// 23/08/2004 Mark G. account for comments on same line as @string - count delimiters in string value
|
|
$value = trim($value);
|
|
$matches = preg_split("/@\\s*string\\s*([{(])/ui", $value, 2, PREG_SPLIT_DELIM_CAPTURE);
|
|
$matches = preg_split("/=/u", $matches[2], 2, PREG_SPLIT_DELIM_CAPTURE);
|
|
// macros are case insensitive
|
|
$this->strings[mb_strtolower(trim($matches[0]))] = $this->extractStringValue($matches[1]);
|
|
}
|
|
}
|
|
// changed 21/08/2004 G. Gardey
|
|
// 22/08/2004 Mark Grimshaw-Aagaard - stopped useless looping.
|
|
// removeDelimit and expandMacro have NO effect if !$this->fieldExtract
|
|
if ($this->removeDelimit || $this->expandMacro && $this->fieldExtract) {
|
|
for ($i = 0; $i < count($this->entries); $i++) {
|
|
foreach ($this->entries[$i] as $key => $value) {
|
|
// 02/05/2005 G. Gardey don't expand macro for bibtexCitation
|
|
// and bibtexEntryType
|
|
if ($key != 'bibtexCitation' && $key != 'bibtexEntryType') {
|
|
$this->entries[$i][$key] = trim($this->removeDelimitersAndExpand($this->entries[$i][$key]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return array($this->preamble, $this->strings, $this->entries, $this->undefinedStrings);
|
|
}
|
|
|
|
/**
|
|
* Simulate substr_replace() for multibytes strings
|
|
*
|
|
* @param string $string
|
|
* @param string $replacement
|
|
* @param int $start
|
|
* @param int $length Default is NULL.
|
|
* @param string $encoding Default is NULL.
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function _mb_substr_replace($string, $replacement, $start, $length = NULL, $encoding = NULL) {
|
|
if (extension_loaded('mbstring') === FALSE) {
|
|
return (is_null($length) === TRUE) ? substr_replace($string, $replacement, $start) : substr_replace($string, $replacement, $start, $length);
|
|
}
|
|
|
|
$string_length = (is_null($encoding) === TRUE) ? mb_strlen($string) : mb_strlen($string, $encoding);
|
|
|
|
if ($start < 0) {
|
|
$start = max(0, $string_length + $start);
|
|
}
|
|
elseif ($start > $string_length) {
|
|
$start = $string_length;
|
|
}
|
|
|
|
if ($length < 0) {
|
|
$length = max(0, $string_length - $start + $length);
|
|
}
|
|
elseif ((is_null($length) === TRUE) || ($length > $string_length)) {
|
|
$length = $string_length;
|
|
}
|
|
|
|
if (($start + $length) > $string_length) {
|
|
$length = $string_length - $start;
|
|
}
|
|
|
|
if (is_null($encoding) === TRUE) {
|
|
return mb_substr($string, 0, $start) . $replacement . mb_substr($string, $start + $length, $string_length - $start - $length);
|
|
}
|
|
|
|
return mb_substr($string, 0, $start, $encoding) . $replacement . mb_substr($string, $start + $length, $string_length - $start - $length, $encoding);
|
|
}
|
|
|
|
/**
|
|
* Simulate strrev() for multibytes strings
|
|
*
|
|
* @param string $str
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function _mb_strrev($str) {
|
|
preg_match_all('/./us', $str, $ar);
|
|
|
|
return implode('', array_reverse($ar[0]));
|
|
}
|
|
|
|
/**
|
|
* Sets a control character for saving line breaks
|
|
* @param array $line_array
|
|
* @return array
|
|
* @since 2.3
|
|
* @author Michael Winkler <michael.mtrv@gmail.com>
|
|
*/
|
|
private static function _setLineBreak($line_array) {
|
|
$max = count($line_array);
|
|
|
|
// Ignore the first line
|
|
for ( $i = 1; $i < $max; $i++ ) {
|
|
$line_before = mb_substr(trim( $line_array[$i-1] ), -2, 2);
|
|
|
|
// Fix blank lines at the end of the entry
|
|
$line_end = ( isset($line_array[$i][0]) ) ? $line_array[$i][0] : '';
|
|
|
|
if ( strpos($line_array[$i], '@') === false && // No '@' in the line
|
|
strpos($line_array[$i], '=') === false && // No '=' in the line
|
|
$line_before !== '},' && // No '},' at the end of the line before
|
|
$line_end !== '}' // No '}' at the beginning of the line
|
|
) {
|
|
$line_array[$i] = '<LineBreak>' . $line_array[$i];
|
|
}
|
|
}
|
|
return $line_array;
|
|
}
|
|
|
|
/**
|
|
* Replaces the control character with the original line break
|
|
* @param string $string
|
|
* @return string
|
|
* @since 2.3
|
|
* @author Michael Winkler <michael.mtrv@gmail.com>
|
|
*/
|
|
private static function _replaceLineBreak($string) {
|
|
$return = str_replace('<LineBreak>', chr(13) . chr(10), $string);
|
|
return $return;
|
|
}
|
|
|
|
} |