diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index b406321c4..b98111f71 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,19 +1,34 @@ CHANGELOG +3.7.3 +* NEW: added auto_increment detection, [bcosca/fatfree#1192](https://github.com/bcosca/fatfree/issues/1192), [bcosca/fatfree#1093](https://github.com/bcosca/fatfree/issues/1093), [bcosca/fatfree#1175](https://github.com/bcosca/fatfree/issues/1175), [#290](https://github.com/bcosca/fatfree-core/issues/290) +* added SMTP dialog error handling, [#317](https://github.com/bcosca/fatfree-core/issues/317) +* Fix: Check active transaction before rollback/commit (PHP8 issue) +* refactored increment/decrement operator to preceed variables +* added error output in CLI mode, [bcosca/fatfree#1185](https://github.com/bcosca/fatfree/issues/1185) +* Set PORT to 80 when SERVER_PORT is an empty string +* Fix: unescape dbname when extracting from dsn, [#316](https://github.com/bcosca/fatfree-core/issues/316) +* Fix: handling of PDO prepare() errors +* Fix: edge case in DB\SQL->schema(): PK not detected in PgSQL when the column is also a FK [bcosca/fatfree#1207](https://github.com/bcosca/fatfree/issues/1207) +* Fix: Escape literal hyphens in regex character classes, [bcosca/fatfree#1206](https://github.com/bcosca/fatfree/issues/1206) +* Fix: error highlighting +* Fix: pagination with order by on virtual fields +* Fixed a couple PHPDOC issues + 3.7.2 (28 May 2020) -* CHANGED, View->sandbox: disable escaping when rendering as text/plain, bcosca/fatfree#654 -* update HTTP protocol checks, #bcosca/fatfree#1190 -* Base->clear: close vulnerability on variable compilation, bcosca/fatfree#1191 -* DB\SQL\Mapper: fix empty ID after insert, bcosca/fatfree#1175 +* CHANGED, View->sandbox: disable escaping when rendering as text/plain, [bcosca/fatfree#654](https://github.com/bcosca/fatfree/issues/654) +* update HTTP protocol checks, [bcosca/fatfree#1190](https://github.com/bcosca/fatfree/issues/1190) +* Base->clear: close vulnerability on variable compilation, [bcosca/fatfree#1191](https://github.com/bcosca/fatfree/issues/1191) +* DB\SQL\Mapper: fix empty ID after insert, [bcosca/fatfree#1175](https://github.com/bcosca/fatfree/issues/1175) * DB\SQL\Mapper: fix using correct key variable for grouped sql pagination sets -* Fix return type of 'count' in Cursor->paginate() (bcosca/fatfree#1187) -* Bug fix, Web->minify: fix minification of ES6 template literals, bcosca/fatfree#1178 -* Bug fix, config: refactoring custom section parser regex, bcosca/fatfree#1149 -* Bug fix: token resolve on non-alias reroute paths, ref. 221f0c930f8664565c9825faeb9ed9af0f7a01c8 +* Fix return type of 'count' in Cursor->paginate(), [bcosca/fatfree#1187](https://github.com/bcosca/fatfree/issues/1187) +* Bug fix, Web->minify: fix minification of ES6 template literals, [bcosca/fatfree#1178](https://github.com/bcosca/fatfree/issues/1178) +* Bug fix, config: refactoring custom section parser regex, [bcosca/fatfree#1149](https://github.com/bcosca/fatfree/issues/1149) +* Bug fix: token resolve on non-alias reroute paths, [ref. 221f0c9](https://github.com/bcosca/fatfree-core/commit/221f0c930f8664565c9825faeb9ed9af0f7a01c8) * Websocket: Improved event handler usage * optimized internal get calls * only use cached lexicon when a $ttl was given -* only use money_format up until php7.4, fixes bcosca/fatfree#1174 +* only use money_format up until php7.4, [bcosca/fatfree#1174](https://github.com/bcosca/fatfree/issues/1174) 3.7.1 (30. December 2019) * Base->build: Add support for brace-enclosed route tokens diff --git a/lib/audit.php b/lib/audit.php index 8fcc95ae5..a0d4338e6 100644 --- a/lib/audit.php +++ b/lib/audit.php @@ -145,7 +145,7 @@ function mod10($id) { return FALSE; $id=strrev($id); $sum=0; - for ($i=0,$l=strlen($id);$i<$l;$i++) + for ($i=0,$l=strlen($id);$i<$l;++$i) $sum+=$id[$i]+$i%2*(($id[$i]>4)*-4+$id[$i]%5); return !($sum%10); } diff --git a/lib/base.php b/lib/base.php index ea72e7c31..dfb59c193 100644 --- a/lib/base.php +++ b/lib/base.php @@ -45,7 +45,7 @@ final class Base extends Prefab implements ArrayAccess { //@{ Framework details const PACKAGE='Fat-Free Framework', - VERSION='3.7.2-Release'; + VERSION='3.7.3-Release'; //@} //@{ HTTP status codes (RFC 2616) @@ -186,7 +186,7 @@ function($match) use(&$i,$args) { array_key_exists($match[3],$args)) { if (!is_array($args[$match[3]])) return $args[$match[3]]; - $i++; + ++$i; return $args[$match[3]][$i-1]; } return $match[0]; @@ -1293,10 +1293,10 @@ function trace(array $trace=NULL,$format=TRUE) { function($frame) use($debug) { return isset($frame['file']) && ($debug>1 || - ($frame['file']!=__FILE__ || $debug) && + (($frame['file']!=__FILE__ || $debug) && (empty($frame['function']) || !preg_match('/^(?:(?:trigger|user)_error|'. - '__call|call_user_func)/',$frame['function']))); + '__call|call_user_func)/',$frame['function'])))); } ); if (!$format) @@ -1349,8 +1349,8 @@ function error($code,$text='',array $trace=NULL,$level=0) { error_log($nexus); break; } - if ($highlight=!$this->hive['CLI'] && !$this->hive['AJAX'] && - $this->hive['HIGHLIGHT'] && is_file($css=__DIR__.'/'.self::CSS)) + if ($highlight=(!$this->hive['CLI'] && !$this->hive['AJAX'] && + $this->hive['HIGHLIGHT'] && is_file($css=__DIR__.'/'.self::CSS))) $trace=$this->highlight($trace); $this->hive['ERROR']=[ 'status'=>$header, @@ -1366,29 +1366,34 @@ function error($code,$text='',array $trace=NULL,$level=0) { if ((!$handler || $this->call($handler,[$this,$this->hive['PARAMS']], 'beforeroute,afterroute')===FALSE) && - !$prior && !$this->hive['CLI'] && !$this->hive['QUIET']) - echo $this->hive['AJAX']? - json_encode( - array_diff_key( - $this->hive['ERROR'], - $this->hive['DEBUG']? - []: - ['trace'=>1] - ) - ): - (''.$eol. - ''.$eol. - '
'. - ''.$this->encode($text?:$req).'
'.$eol. - ($this->hive['DEBUG']?(''.$trace.''.$eol):''). - ''.$eol. - ''); + !$prior && !$this->hive['QUIET']) { + $error=array_diff_key( + $this->hive['ERROR'], + $this->hive['DEBUG']? + []: + ['trace'=>1] + ); + if ($this->hive['CLI']) + echo PHP_EOL.'==================================='.PHP_EOL. + 'ERROR '.$error['code'].' - '.$error['status'].PHP_EOL. + $error['text'].PHP_EOL.PHP_EOL.$error['trace']; + else + echo $this->hive['AJAX']? + json_encode($error): + (''.$eol. + ''.$eol. + ''. + '
'.$this->encode($text?:$req).'
'.$eol. + ($this->hive['DEBUG']?(''.$trace.''.$eol):''). + ''.$eol. + ''); + } if ($this->hive['HALT']) die(1); } @@ -1615,7 +1620,7 @@ function mask($pattern,$url=NULL) { $i=0; while (is_int($pos=strpos($wild,'\*'))) { $wild=substr_replace($wild,'(?P<_'.$i.'>[^\?]*)',$pos,2); - $i++; + ++$i; } preg_match('/^'. preg_replace( @@ -1767,7 +1772,7 @@ function($id) use($args) { $ctr=0; foreach (str_split($body,1024) as $part) { // Throttle output - $ctr++; + ++$ctr; if ($ctr/$kbps>($elapsed=microtime(TRUE)-$now) && !connection_aborted()) usleep(1e6*($ctr/$kbps-$elapsed)); @@ -2197,7 +2202,7 @@ protected function autoload($class) { isset($path[1]) && is_callable($path[1])) list($path,$func)=$path; foreach ($this->split($this->hive['PLUGINS'].';'.$path) as $auto) - if ($func && is_file($file=$func($auto.$class).'.php') || + if (($func && is_file($file=$func($auto.$class).'.php')) || is_file($file=$auto.$class.'.php') || is_file($file=$auto.strtolower($class).'.php') || is_file($file=strtolower($auto.$class).'.php')) @@ -2345,11 +2350,11 @@ function($level,$text,$file,$line) { if (!isset($_SERVER['SERVER_NAME']) || $_SERVER['SERVER_NAME']==='') $_SERVER['SERVER_NAME']=gethostname(); $headers=[]; - if ($cli=PHP_SAPI=='cli') { + if ($cli=(PHP_SAPI=='cli')) { // Emulate HTTP request $_SERVER['REQUEST_METHOD']='GET'; if (!isset($_SERVER['argv'][1])) { - $_SERVER['argc']++; + ++$_SERVER['argc']; $_SERVER['argv'][1]='/'; } $req=$query=''; @@ -2433,9 +2438,9 @@ function($level,$text,$file,$line) { 'samesite'=>'Lax', ]; $port=80; - if (isset($headers['X-Forwarded-Port'])) + if (!empty($headers['X-Forwarded-Port'])) $port=$headers['X-Forwarded-Port']; - elseif (isset($_SERVER['SERVER_PORT'])) + elseif (!empty($_SERVER['SERVER_PORT'])) $port=$_SERVER['SERVER_PORT']; // Default configuration $this->hive+=[ @@ -2496,7 +2501,7 @@ function($level,$text,$file,$line) { 'QUIET'=>FALSE, 'RAW'=>FALSE, 'REALM'=>$scheme.'://'.$_SERVER['SERVER_NAME']. - ($port && !in_array($port,[80,443])?(':'.$port):''). + (!in_array($port,[80,443])?(':'.$port):''). $_SERVER['REQUEST_URI'], 'RESPONSE'=>'', 'ROOT'=>$_SERVER['DOCUMENT_ROOT'], @@ -2740,7 +2745,7 @@ function reset($suffix=NULL) { case 'xcache': if ($suffix && !ini_get('xcache.admin.enable_auth')) { $cnt=xcache_count(XC_TYPE_VAR); - for ($i=0;$i<$cnt;$i++) { + for ($i=0;$i<$cnt;++$i) { $list=xcache_list(XC_TYPE_VAR,$i); foreach ($list['cache_list'] as $item) if (preg_match($regex,$item['name'])) @@ -2898,10 +2903,10 @@ protected function sandbox(array $hive=NULL,$mime=NULL) { unset($fw,$hive,$implicit,$mime); extract($this->temp); $this->temp=NULL; - $this->level++; + ++$this->level; ob_start(); require($this->file); - $this->level--; + --$this->level; return ob_get_clean(); } diff --git a/lib/bcrypt.php b/lib/bcrypt.php index f044ff1dd..414daa739 100644 --- a/lib/bcrypt.php +++ b/lib/bcrypt.php @@ -56,7 +56,7 @@ function hash($pw,$salt=NULL,$cost=self::COST) { if (!$iv && extension_loaded('openssl')) $iv=openssl_random_pseudo_bytes($raw); if (!$iv) - for ($i=0;$i<$raw;$i++) + for ($i=0;$i<$raw;++$i) $iv.=chr(mt_rand(0,255)); $salt=str_replace('+','.',base64_encode($iv)); } @@ -88,7 +88,7 @@ function verify($pw,$hash) { if ($len!=strlen($hash) || $len<14) return FALSE; $out=0; - for ($i=0;$i<$len;$i++) + for ($i=0;$i<$len;++$i) $out|=(ord($val[$i])^ord($hash[$i])); return $out===0; } diff --git a/lib/cli/ws.php b/lib/cli/ws.php index f1573c010..4545e9bcc 100644 --- a/lib/cli/ws.php +++ b/lib/cli/ws.php @@ -424,16 +424,16 @@ function fetch() { } else if ($len==0x7f) { - for ($i=0,$len=0;$i<8;$i++) + for ($i=0,$len=0;$i<8;++$i) $len=$len*256+ord($buf[$i+2]); $pos+=8; } - for ($i=0,$mask=[];$i<4;$i++) + for ($i=0,$mask=[];$i<4;++$i) $mask[$i]=ord($buf[$pos+$i]); $pos+=4; if (strlen($buf)<$len+$pos) return FALSE; - for ($i=0,$data='';$i<$len;$i++) + for ($i=0,$data='';$i<$len;++$i) $data.=chr(ord($buf[$pos+$i])^$mask[$i%4]); // Dispatch switch ($op & WS::OpCode) { diff --git a/lib/db/jig/mapper.php b/lib/db/jig/mapper.php index 784b2a8f0..5d26427f0 100644 --- a/lib/db/jig/mapper.php +++ b/lib/db/jig/mapper.php @@ -206,7 +206,7 @@ function($_row) use($fw,$args,$tokens) { if (is_string($token)) if ($token=='?') { // Positional - $ctr++; + ++$ctr; $key=$ctr; } else { diff --git a/lib/db/sql.php b/lib/db/sql.php index 4e464d7e8..566b94a73 100644 --- a/lib/db/sql.php +++ b/lib/db/sql.php @@ -66,7 +66,9 @@ function begin() { * @return bool **/ function rollback() { - $out=$this->pdo->rollback(); + $out=FALSE; + if ($this->pdo->inTransaction()) + $out=$this->pdo->rollback(); $this->trans=FALSE; return $out; } @@ -76,7 +78,9 @@ function rollback() { * @return bool **/ function commit() { - $out=$this->pdo->commit(); + $out=FALSE; + if ($this->pdo->inTransaction()) + $out=$this->pdo->commit(); $this->trans=FALSE; return $out; } @@ -173,7 +177,7 @@ function exec($cmds,$args=NULL,$ttl=0,$log=TRUE,$stamp=FALSE) { $fw=\Base::instance(); $cache=\Cache::instance(); $result=FALSE; - for ($i=0;$i<$count;$i++) { + for ($i=0;$i<$count;++$i) { $cmd=$cmds[$i]; $arg=$args[$i]; // ensure 1-based arguments @@ -257,7 +261,7 @@ function exec($cmds,$args=NULL,$ttl=0,$log=TRUE,$stamp=FALSE) { $query->closecursor(); unset($query); } - elseif (($error=$this->errorinfo()) && $error[0]!=\PDO::ERR_NONE) { + elseif (($error=$this->pdo->errorInfo()) && $error[0]!=\PDO::ERR_NONE) { // PDO-level error occurred if ($this->trans) $this->rollback(); @@ -321,19 +325,37 @@ function schema($table,$fields=NULL,$ttl=0) { if (strpos($table,'.')) list($schema,$table)=explode('.',$table); // Supported engines + // format: engine_name => array of: + // 0: query + // 1: field name of column name + // 2: field name of column type + // 3: field name of default value + // 4: field name of nullable value + // 5: expected field value to be nullable + // 6: field name of primary key flag + // 7: expected field value to be a primary key + // 8: field name of auto increment check (optional) + // 9: expected field value to be an auto-incremented identifier $cmd=[ 'sqlite2?'=>[ - 'PRAGMA table_info(`'.$table.'`)', - 'name','type','dflt_value','notnull',0,'pk',TRUE], + 'SELECT * FROM pragma_table_info('.$this->quote($table).') JOIN ('. + 'SELECT sql FROM sqlite_master WHERE type=\'table\' AND '. + 'name='.$this->quote($table).')', + 'name','type','dflt_value','notnull',0,'pk',TRUE,'sql', + '/\W(%s)\W+[^,]+?AUTOINCREMENT\W/i'], 'mysql'=>[ 'SHOW columns FROM `'.$this->dbname.'`.`'.$table.'`', - 'Field','Type','Default','Null','YES','Key','PRI'], + 'Field','Type','Default','Null','YES','Key','PRI','Extra','auto_increment'], 'mssql|sqlsrv|sybase|dblib|pgsql|odbc'=>[ 'SELECT '. 'C.COLUMN_NAME AS field,'. 'C.DATA_TYPE AS type,'. 'C.COLUMN_DEFAULT AS defval,'. 'C.IS_NULLABLE AS nullable,'. + ($this->engine=='pgsql' + ?'COALESCE(POSITION(\'nextval\' IN C.COLUMN_DEFAULT),0) AS autoinc,' + :'columnproperty(object_id(C.TABLE_NAME),C.COLUMN_NAME,\'IsIdentity\')' + .' AS autoinc,'). 'T.CONSTRAINT_TYPE AS pkey '. 'FROM INFORMATION_SCHEMA.COLUMNS AS C '. 'LEFT OUTER JOIN '. @@ -356,7 +378,7 @@ function schema($table,$fields=NULL,$ttl=0) { ($this->dbname? (' AND C.TABLE_CATALOG='. $this->quote($this->dbname)):''), - 'field','type','defval','nullable','YES','pkey','PRIMARY KEY'], + 'field','type','defval','nullable','YES','pkey','PRIMARY KEY','autoinc',1], 'oci'=>[ 'SELECT c.column_name AS field, '. 'c.data_type AS type, '. @@ -390,15 +412,22 @@ function schema($table,$fields=NULL,$ttl=0) { foreach ($conv as $regex=>$type) if (preg_match('/'.$regex.'/i',$row[$val[2]])) break; - $rows[$row[$val[1]]]=[ - 'type'=>$row[$val[2]], - 'pdo_type'=>$type, - 'default'=>is_string($row[$val[3]])? - preg_replace('/^\s*([\'"])(.*)\1\s*/','\2', - $row[$val[3]]):$row[$val[3]], - 'nullable'=>$row[$val[4]]==$val[5], - 'pkey'=>$row[$val[6]]==$val[7] - ]; + if (!isset($rows[$row[$val[1]]])) // handle duplicate rows in PgSQL + $rows[$row[$val[1]]]=[ + 'type'=>$row[$val[2]], + 'pdo_type'=>$type, + 'default'=>is_string($row[$val[3]])? + preg_replace('/^\s*([\'"])(.*)\1\s*/','\2', + $row[$val[3]]):$row[$val[3]], + 'nullable'=>$row[$val[4]]==$val[5], + 'pkey'=>$row[$val[6]]==$val[7], + 'auto_inc'=>isset($val[8]) && isset($row[$val[8]]) + ? ($this->engine=='sqlite'? + (bool) preg_match(sprintf($val[9],$row[$val[1]]), + $row[$val[8]]): + ($row[$val[8]]==$val[9]) + ) : NULL, + ]; } if ($fw->CACHE && $ttl) // Save to cache backend @@ -510,7 +539,7 @@ function __construct($dsn,$user=NULL,$pw=NULL,array $options=NULL) { $fw=\Base::instance(); $this->uuid=$fw->hash($this->dsn=$dsn); if (preg_match('/^.+?(?:dbname|database)=(.+?)(?=;|$)/is',$dsn,$parts)) - $this->dbname=$parts[1]; + $this->dbname=str_replace('\\ ',' ',$parts[1]); if (!$options) $options=[]; if (isset($parts[0]) && strstr($parts[0],':',TRUE)=='mysql') diff --git a/lib/db/sql/mapper.php b/lib/db/sql/mapper.php index 1eb45c6d0..574cc9fbd 100644 --- a/lib/db/sql/mapper.php +++ b/lib/db/sql/mapper.php @@ -378,6 +378,9 @@ function count($filter=NULL,array $options=NULL,$ttl=0) { // for simple count just add a new adhoc counter $fields='COUNT(*) AS '.$this->db->quotekey('_rows'); } + // no need to order for a count query as that could include virtual + // field references that are not present here + unset($options['order']); list($sql,$args)=$this->stringify($fields,$filter,$options); if ($subquery_mode) $sql='SELECT COUNT(*) AS '.$this->db->quotekey('_rows').' '. @@ -438,27 +441,30 @@ function insert() { // duplicate record foreach ($this->fields as $key=>&$field) { $field['changed']=true; - if ($field['pkey'] && !$inc && $field['pdo_type']==\PDO::PARAM_INT - && !$field['nullable']) + if ($field['pkey'] && !$inc && ($field['auto_inc'] === TRUE || + ($field['auto_inc'] === NULL && !$field['nullable'] + && $field['pdo_type']==\PDO::PARAM_INT) + )) $inc=$key; unset($field); } foreach ($this->fields as $key=>&$field) { if ($field['pkey']) { $field['previous']=$field['value']; - if (!$inc && $field['pdo_type']==\PDO::PARAM_INT && - empty($field['value']) && !$field['nullable'] && - is_null($field['default'])) + if (!$inc && empty($field['value']) && + ($field['auto_inc'] === TRUE || ($field['auto_inc'] === NULL + && $field['pdo_type']==\PDO::PARAM_INT && !$field['nullable'])) + ) $inc=$key; $filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?'; $nkeys[$nctr+1]=[$field['value'],$field['pdo_type']]; - $nctr++; + ++$nctr; } if ($field['changed'] && $key!=$inc) { $fields.=($actr?',':'').$this->db->quotekey($key); $values.=($actr?',':'').'?'; $args[$actr+1]=[$field['value'],$field['pdo_type']]; - $actr++; + ++$actr; $ckeys[]=$key; } unset($field); @@ -617,7 +623,7 @@ function erase($filter=NULL,$quick=TRUE) { $filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?'; $args[$ctr+1]=[$field['previous'],$field['pdo_type']]; $pkeys[$key]=$field['previous']; - $ctr++; + ++$ctr; } $field['value']=NULL; $field['changed']=(bool)$field['default']; diff --git a/lib/image.php b/lib/image.php index 59c165eb7..b7f149ced 100644 --- a/lib/image.php +++ b/lib/image.php @@ -367,15 +367,15 @@ function identicon($str,$size=64,$blocks=4) { imagefill($this->data,0,0,IMG_COLOR_TRANSPARENT); $ctr=count($sprites); $dim=$blocks*floor($size/$blocks)*2/$blocks; - for ($j=0,$y=ceil($blocks/2);$j<$y;$j++) - for ($i=$j,$x=$blocks-1-$j;$i<$x;$i++) { + for ($j=0,$y=ceil($blocks/2);$j<$y;++$j) + for ($i=$j,$x=$blocks-1-$j;$i<$x;++$i) { $sprite=imagecreatetruecolor($dim,$dim); imagefill($sprite,0,0,IMG_COLOR_TRANSPARENT); $block=$sprites[hexdec($hash[($j*$blocks+$i)*2])%$ctr]; - for ($k=0,$pts=count($block);$k<$pts;$k++) + for ($k=0,$pts=count($block);$k<$pts;++$k) $block[$k]*=$dim; imagefilledpolygon($sprite,$block,$pts/2,$fg); - for ($k=0;$k<4;$k++) { + for ($k=0;$k<4;++$k) { imagecopyresampled($this->data,$sprite, $i*$dim/2,$j*$dim/2,0,0,$dim/2,$dim/2,$dim,$dim); $this->data=imagerotate($this->data,90, @@ -416,7 +416,7 @@ function captcha($font,$size=24,$len=5, -$len)); $block=$size*3; $tmp=[]; - for ($i=0,$width=0,$height=0;$i<$len;$i++) { + for ($i=0,$width=0,$height=0;$i<$len;++$i) { // Process at 2x magnification $box=imagettfbbox($size*2,0,$path,$seed[$i]); $w=$box[2]-$box[0]; @@ -440,7 +440,7 @@ function captcha($font,$size=24,$len=5, } $this->data=imagecreatetruecolor($width,$height); imagefill($this->data,0,0,IMG_COLOR_TRANSPARENT); - for ($i=0;$i<$len;$i++) { + for ($i=0;$i<$len;++$i) { imagecopy($this->data,$tmp[$i], $i*$block/2,($height-imagesy($tmp[$i]))/2,0,0, imagesx($tmp[$i]),imagesy($tmp[$i])); @@ -520,7 +520,7 @@ function save() { if ($this->flag) { if (!is_dir($dir=$fw->TEMP)) mkdir($dir,Base::MODE,TRUE); - $this->count++; + ++$this->count; $fw->write($dir.'/'.$fw->SEED.'.'. $fw->hash($this->file).'-'.$this->count.'.png', $this->dump()); diff --git a/lib/markdown.php b/lib/markdown.php index 885f21582..4be4c5611 100644 --- a/lib/markdown.php +++ b/lib/markdown.php @@ -224,7 +224,7 @@ protected function _li($str) { $type='ul'; // Main loop while ($ptr<$len) { - if (preg_match('/^\h*[*-](?:\h?[*-]){2,}(?:\n+|$)/', + if (preg_match('/^\h*[*\-](?:\h?[*\-]){2,}(?:\n+|$)/', substr($str,$ptr),$match)) { $ptr+=strlen($match[0]); // Embedded horizontal rule @@ -232,7 +232,7 @@ protected function _li($str) { ('<'.$type.'>'."\n".$dst.''.$type.'>'."\n\n"):''). '