2021-09-27 10:39:58 +01:00
< ? php
2021-10-10 09:26:18 +01:00
declare ( strict_types = 1 );
2021-09-27 10:39:58 +01:00
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
2022-01-08 15:10:39 +00:00
namespace Component\Collection\Util ;
2021-09-27 10:39:58 +01:00
use App\Core\Event ;
2021-12-24 09:27:24 +00:00
use App\Entity\Actor ;
2021-09-27 10:39:58 +01:00
use App\Util\Exception\ServerException ;
use Doctrine\Common\Collections\Criteria ;
abstract class Parser
{
2021-10-10 09:23:51 +01:00
/**
* Merge $parts into $criteria_arr
2022-10-19 22:39:17 +01:00
*
* @ param mixed [] $parts
* @ param Criteria [] $criteria_arr
2021-10-10 09:23:51 +01:00
*/
private static function connectParts ( array & $parts , array & $criteria_arr , string $last_op , mixed $eb , bool $force = false ) : void
{
foreach ([ ' ' => 'orX' , '|' => 'orX' , '&' => 'andX' ] as $op => $func ) {
if ( $last_op === $op || $force ) {
$criteria_arr [] = $eb -> { $func }( ... $parts );
break ;
}
}
}
2021-09-27 10:39:58 +01:00
/**
* Parse $input string into a Doctrine query Criteria
*
* Currently doesn ' t support nesting with parenthesis and
* recognises either spaces ( currently `or` , should be fuzzy match ), `OR` or `|` ( `or` ) and `AND` or `&` ( `and` )
*
2021-12-23 13:27:31 +00:00
* TODO : Better fuzzy match , implement exact match with quotes and nesting with parens
2022-02-26 14:09:41 +00:00
* TODO : Proper parser , tokenize better . Mostly a rewrite
2021-10-10 05:44:10 +01:00
*
2022-02-26 14:09:41 +00:00
* @ return array { ? Criteria , ? Criteria } [ ? $note_criteria , ? $actor_criteria ]
2021-09-27 10:39:58 +01:00
*/
2022-01-11 20:28:15 +00:00
public static function parse ( string $input , ? string $locale = null , ? Actor $actor = null , int $level = 0 ) : array
2021-09-27 10:39:58 +01:00
{
if ( $level === 0 ) {
$input = trim ( preg_replace ([ '/\s+/' , '/\s+AND\s+/' , '/\s+OR\s+/' ], [ ' ' , '&' , '|' ], $input ), ' |&' );
}
2021-12-23 13:27:31 +00:00
$left = 0 ;
$right = 0 ;
2021-10-10 05:44:10 +01:00
$lenght = mb_strlen ( $input );
$eb = Criteria :: expr ();
$note_criteria_arr = [];
$actor_criteria_arr = [];
$note_parts = [];
$actor_parts = [];
$last_op = null ;
2021-09-27 10:39:58 +01:00
for ( $index = 0 ; $index < $lenght ; ++ $index ) {
$end = false ;
$match = false ;
foreach ([ '&' , '|' , ' ' ] as $delimiter ) {
if ( $input [ $index ] === $delimiter || $end = ( $index === $lenght - 1 )) {
2021-12-23 13:27:31 +00:00
$term = mb_substr ( $input , $left , $end ? null : $right - $left );
$note_res = null ;
$actor_res = null ;
2022-01-11 20:28:15 +00:00
Event :: handle ( 'CollectionQueryCreateExpression' , [ $eb , $term , $locale , $actor , & $note_res , & $actor_res ]);
2021-12-26 22:17:26 +00:00
if ( \is_null ( $note_res ) && \is_null ( $actor_res )) { // @phpstan-ignore-line
2022-02-21 04:48:18 +00:00
//throw new ServerException("No one claimed responsibility for a match term: {$term}");
// It's okay if the term doesn't exist, just perform a regular search
2021-12-08 21:25:48 +00:00
}
2022-02-26 14:09:41 +00:00
if ( ! empty ( $note_res )) { // @phpstan-ignore-line currently an open bug. See https://web.archive.org/web/20220226131651/https://github.com/phpstan/phpstan/issues/6234
2021-12-11 19:33:30 +00:00
if ( \is_array ( $note_res )) {
$note_res = $eb -> orX ( ... $note_res );
}
2021-10-10 09:26:18 +01:00
$note_parts [] = $note_res ;
2021-12-08 21:25:48 +00:00
}
2022-02-26 14:09:41 +00:00
if ( ! empty ( $actor_res )) { // @phpstan-ignore-line currently an open bug. See https://web.archive.org/web/20220226131651/https://github.com/phpstan/phpstan/issues/6234
2021-12-11 19:33:30 +00:00
if ( \is_array ( $actor_res )) {
2021-12-23 13:27:31 +00:00
$actor_res = $eb -> orX ( ... $actor_res );
2021-12-11 19:33:30 +00:00
}
2021-10-10 09:26:18 +01:00
$actor_parts [] = $actor_res ;
2021-09-27 10:39:58 +01:00
}
$right = $left = $index + 1 ;
2021-10-10 09:23:51 +01:00
if ( ! \is_null ( $last_op ) && $last_op !== $delimiter ) {
self :: connectParts ( $note_parts , $note_criteria_arr , $last_op , $eb , force : false );
2021-09-27 10:39:58 +01:00
} else {
$last_op = $delimiter ;
}
$match = true ;
2021-12-26 22:17:26 +00:00
break ;
2021-09-27 10:39:58 +01:00
}
}
2021-12-26 22:17:26 +00:00
// TODO
2022-02-26 14:09:41 +00:00
if ( ! $match ) {
2021-09-27 10:39:58 +01:00
++ $right ;
}
}
2021-12-23 13:27:31 +00:00
$note_criteria = null ;
$actor_criteria = null ;
2022-02-26 14:09:41 +00:00
if ( ! empty ( $note_parts )) {
2021-10-10 09:23:51 +01:00
self :: connectParts ( $note_parts , $note_criteria_arr , $last_op , $eb , force : true );
2021-10-10 05:44:10 +01:00
$note_criteria = new Criteria ( $eb -> orX ( ... $note_criteria_arr ));
2021-12-08 21:25:48 +00:00
}
2022-02-26 14:09:41 +00:00
if ( ! empty ( $actor_parts )) { // @phpstan-ignore-line weird, but this whole thing needs a rewrite
2021-10-10 09:23:51 +01:00
self :: connectParts ( $actor_parts , $actor_criteria_arr , $last_op , $eb , force : true );
$actor_criteria = new Criteria ( $eb -> orX ( ... $actor_criteria_arr ));
2021-09-27 10:39:58 +01:00
}
2021-10-10 05:44:10 +01:00
return [ $note_criteria , $actor_criteria ];
2021-09-27 10:39:58 +01:00
}
}